자체 방문 통계는 항상 수집됩니다. 사용자가 공개 페이지를 어떻게 이용하는지 파악하는 데 쓰이는 Google Analytics(로그인한 워크스페이스는 제외)는 동의한 경우에만 사용됩니다. 자세한 내용은 개인정보 처리방침을 참고하세요.

Xiaohongshu 댓글 일괄 수집: 노트 목록에서 검증 가능한 결과까지

Xiaohongshu comments_batch 연동을 단계별로 안내합니다. 완전한 노트 URL 준비, URL 수와 댓글 수 구분, partial·missed 결과 식별, 노트 기준 중복 제거, 빈 결과·재시도·대상별 과금 처리까지 다룹니다.

Xiaohongshu 댓글을 조사할 때 첫 단계는 감성 분석보다 노트 URL 정리인 경우가 많습니다. 여러 사람이 제출한 목록에는 중복 노트가 섞여 있고, 시간이 지나면 일부 링크는 만료되며, 배치 요청이 200을 반환해도 결과에는 일부 노트만 나옵니다. 이런 문제를 먼저 해결하지 않으면 이후의 “자주 나오는 문제”나 “부정 비율”은 수집 공백을 다르게 표현한 것에 불과할 수 있습니다.

EveryInfra의 xiaohongshu.comments_batch 엔드포인트는 노트 URL 배열을 받아 여러 대상을 한 번에 제출할 수 있습니다. 이 가이드는 노트 하나에서 시작해, 기록이 남고 검증 가능한 배치 프로세스로 확장하는 방법을 설명합니다. 대상 준비, 파라미터 확인, 요청 전송, 댓글을 소속 노트별로 정리하기, 재시도할 수 있는 대상 판별까지 다룹니다.

이 가이드의 입력 제약과 응답 처리 방식은 2026-09-04 기준 공개 카탈로그와 소스 코드에서 확인했습니다. 비즈니스 요청은 상황에 맞게 조정할 수 있는 템플릿이며, 오프라인 예시는 합성 데이터로 명확히 표시했고 이번 가이드에서는 실제 댓글인 것처럼 제시하지 않습니다. 호출하기 전에 대상, 사용 목적, 보존 범위가 승인되었는지 확인하십시오.

알맞은 기능 고르기: 노트 검색, 댓글 수집, 답글 수집은 서로 다른 작업입니다

search 엔드포인트는 키워드로 노트를 찾고, note는 특정 노트의 상세 정보를 읽으며, comments는 노트 하나를, comments_batch는 여러 노트를 대상으로 합니다. 특정 댓글의 하위 답글이 필요하면 카탈로그의 sub_comments를 사용하며, 이때 comment_id도 입력해야 합니다. 이 인터페이스들은 파라미터 이름만 바꿔서 서로 대체할 수 없습니다.

먼저 조사 대상을 정하십시오. 예를 들어 특정 상품 노트 묶음의 사용자 피드백을 분석하려면 노트 목록이 필요합니다. 반면 특정 브랜드를 언급한 모든 댓글을 얻으려면 노트 탐색 범위, 수집 누락, 댓글 커버리지, 시간 범위가 모두 관련되므로, 일괄 댓글 기능만으로는 완전한 커버리지를 약속할 수 없습니다.

공식 계정 연동과도 구분해야 합니다. Xiaohongshu 공식 API 레퍼런스는 권한 부여, 토큰, 사용자 정보용 엔드포인트를 제공합니다. 사용자 로그인이나 권한을 받은 계정 프로필 조회가 목적이라면 해당 공식 절차에서 시작하십시오. 이 글의 EveryInfra 요청은 그 공식 계정 API가 아니며, 공식 링크가 있다고 해서 댓글 조회가 플랫폼의 승인을 받았다는 증거가 되지도 않습니다.

1단계: 카탈로그에서 입력, 모드, 필드 확인하기

comments_batch의 현재 선언만 확인
curl -fsS --max-time 30 \
  'https://api.everyinfra.com/api/v1/social/catalog?platform=xiaohongshu' \
  | jq -e '.capabilities[] | select(.action == "comments_batch") | {
      action, required_params, optional_params, param_meanings,
      mode, available, default_limit, max_limit, response_fields
    }'

이 카탈로그 버전에서 필수 파라미터는 urls, 모드는 sync, 기본 count는 30, max_limit은 200이며, 대상 배열은 대상별로 과금됩니다. 여기에는 두 가지 수량이 있다는 점에 유의하십시오. urls의 길이는 노트 수이고, 결과의 count는 전달된 레코드 수입니다. 200을 “노트 200개를 한 번에 제출할 수 있다”로 받아들이면 요청 설계 첫 단계부터 잘못됩니다.

현재 소스 코드에는 comments_batch에 대한 명시적인 입력 검사가 있습니다. urls는 비어 있지 않은 목록이어야 하고 최대 20개 항목까지 허용되며, 각 항목은 노트 식별자로 해석될 수 있어야 하고 비어 있지 않은 xsec_token 파라미터를 포함해야 합니다. 대상 한도를 넘거나 필요한 링크 정보가 빠지면 파라미터 오류 경로로 처리됩니다. 이 20은 max_limit에서 계산한 값이 아니라 이 구현의 검사에서 나온 값입니다.

링크에 토큰이 들어 있다고 해서 서버가 그 토큰이 아직 유효한지 확인했다는 뜻은 아닙니다. 현재 파라미터 검사로는 식별자와 파라미터가 있는지만 판단할 수 있고, 문자열만으로 해당 대상이 지금 전달 가능한지는 판단할 수 없습니다. 토큰이 들어 있는 링크는 모두 유효하다고 가정하는 로컬 검사기를 만들고 그 통과율을 댓글 수집 성공률로 간주하지 마십시오.

2단계: 요청 링크와 노트 식별 정보를 따로 저장하기

요청에는 승인된 경로로 얻은 완전한 노트 URL을 사용하십시오. URL을 깔끔하게 만들려고 제출 전에 쿼리 파라미터를 지우거나, 접근 파라미터를 추측·위조하거나, 한 대상의 파라미터를 다른 대상에 섞지 마십시오. 단축 링크와 공유 텍스트는 먼저 이 기능이 받는 노트 링크로 정리해야 합니다. 인터페이스가 임의의 공유 텍스트를 대신 파싱해 줄 것이라고 가정하지 마십시오.

또한 중복 제거를 URL 문자열 전체 비교에만 의존해서는 안 됩니다. 같은 노트의 링크라도 쿼리 파라미터가 다를 수 있어, 문자열 기준으로 중복을 제거하면 같은 대상이 여러 개 남습니다. 식별 키와 현재 사용할 수 있는 링크를 분리하십시오. 식별 키는 플랫폼과 노트 ID로 구성하고, 링크는 원래 값을 확보 시각과 함께 보존합니다.

  • 대상 목록: 노트 식별 키, 완전한 요청 링크, 출처 경로, 확보 시각, 이번 조사에 이 대상이 필요한 이유.
  • 중복 제거 목록: 어떤 입력이 같은 노트를 가리키는지, 최종적으로 어떤 확인된 링크를 채택했는지. 중복을 단순히 삭제하지 말고 출처 관계를 보존합니다.
  • 검토 대상 목록: 파싱할 수 없는 링크, 파라미터가 빠진 링크, 같은 식별 정보에 대해 출처가 충돌하는 링크. 이런 항목은 유료 배치에 섞지 말고 먼저 처리합니다.

완전한 링크는 접근이 통제되는 입력 저장소나 작업 저장소에만 보관하십시오. 로그, 티켓, 내보낸 보고서에는 보통 노트 ID와 정리된 출처 참조 정보만 있으면 되며, 유효 기간이 있는 접근 파라미터를 곳곳에 복사해서는 안 됩니다. 사람이 읽을 수 있는 출처 링크를 공유할 때는 실제 요청 입력을 훼손하지 않도록 공개 표시에 적합한 링크를 별도로 관리하십시오.

3단계: 먼저 노트 하나로 요청과 결과의 귀속 관계 확인하기

아래 예시는 배치 액션 형식을 사용하되 배열에 승인된 노트 하나만 넣어, 댓글이 실제로 해당 대상에 속하는지 쉽게 확인할 수 있게 했습니다. 서버에 EVERYINFRA_API_KEY와 EVERYINFRA_XHS_NOTE_URL이 설정되어 있다고 가정합니다. EVERYINFRA_XHS_NOTE_URL에는 이 글을 위해 만든 자리표시자가 아니라 실제 완전한 노트 링크가 들어 있어야 합니다.

단일 대상 배치 요청 템플릿(정확히 한 번만 제출)
: "${EVERYINFRA_API_KEY:?set the API Key first}"
: "${EVERYINFRA_XHS_NOTE_URL:?set the authorized full note URL first}"
jq -cn --arg url "${EVERYINFRA_XHS_NOTE_URL}" '{
  platform: "xiaohongshu",
  action: "comments_batch",
  params: {urls: [$url], limit: 5}
}' | curl --silent --show-error --include --max-time 180 \
  'https://api.everyinfra.com/api/v1/social' \
  -H "Authorization: Bearer ${EVERYINFRA_API_KEY}" \
  -H 'Content-Type: application/json' \
  --data-binary @-

이 예시는 JSON을 직접 이어 붙이지 않고 jq로 URL을 직렬화합니다. limit: 5는 소수의 레코드를 확인하기 위한 값이고, 클라이언트 시간 초과 180초도 예시 설정입니다. 레코드 5개가 반환된다는 보장은 아니며, 노트의 댓글이 5개뿐이라는 뜻은 더더욱 아닙니다. 첫 확인에서는 대상을 추가하기 전에 응답 엔벨로프, 댓글 ID, note_id 또는 note_url, null 값, 시간 형식을 확인해야 합니다.

4단계: 배치를 나눌 때 limit을 노트별 전달 건수 보장으로 간주하지 않기

노트 하나로 확인을 마쳤다면 중복을 제거한 링크를 현재 제한을 넘지 않는 작은 배치로 나눌 수 있습니다. 20개 항목이라는 한도를 곧바로 고정 배치 크기로 삼지 말고, 더 작은 배치부터 시작해 각 배치에 실제로 제출한 대상을 기록하십시오. 상한은 허용되는 요청 범위만 나타낼 뿐, 허용 가능한 대기 시간, 데이터량, 조사 목적을 고려할 때 최대치를 한 번에 실행하는 것이 적절한지는 알려 주지 않습니다.

여기서 분명히 해야 할 구현 세부 사항이 있습니다. 현재 코드는 limit을 대상별 수집 설정에 적용한 뒤, 같은 값으로 최종 병합 목록을 다시 잘라 냅니다. 따라서 여러 대상에 limit: 5를 전달해도 “노트마다 5개씩 전달된다”는 뜻이 아닙니다. 어떤 대상이 missed에 없더라도 최종적으로 잘린 목록에 그 대상의 레코드가 남지 않을 수 있습니다.

노트마다 개별 검수가 필요하다면, 초기 버전에서는 대상 하나씩 요청하도록 작업을 구성하고 반환 결과를 각각 확인할 수 있습니다. “배치 처리”가 모든 노트를 응답 하나에 담아야 한다는 뜻은 아닙니다. 다중 대상 배치를 사용할 때는 대상별로 결과의 소속을 확인하고, “노트 수 × limit”을 기대 수령 건수로 하드코딩하지 말고 수량의 의미를 연동 검수 기준에 반영하십시오.

배치 식별 정보도 API 요청과 분리해야 합니다. 직접 만든 batch_id는 비즈니스 대상 묶음을 나타내고, API 호출이 반환한 ID는 한 번의 호출을 나타냅니다. 재시도할 때는 원래 배치에 연결된 새 시도 레코드를 만들어, 첫 응답을 덮어써 문제 추적 단서를 잃는 일이 없게 하십시오.

5단계: 대상별로 partial, missed, results 해석하기

이 기능의 동기 응답은 댓글 목록을 results에 담고, count는 반환된 레코드 수를 나타냅니다. 일부 대상이 전달되지 않으면 응답에 partial: true와 missed가 포함됩니다. missed는 전달되지 않은 대상을 표시할 뿐, “이 노트들은 댓글이 0개”임을 확인해 주는 목록이 아닙니다.

결과를 정리할 때는 먼저 note_id나 확인된 note_url을 사용해 각 댓글을 대상 목록에 다시 연결하십시오. 연결할 수 없는 레코드는 검토 영역으로 보내야 하며, 보고서를 채우려고 첫 번째 노트에 배정해서는 안 됩니다. missed에 없다고 해서 그 노트의 댓글을 모두 확보했다는 뜻은 아닙니다. 수량 제한에 따른 잘림, 필드 누락, 수집 범위에 포함되지 않은 과거 댓글 등의 한계는 여전히 남습니다.

아래는 대상 단위의 상태 분류만 보여 주는 오프라인 JavaScript 합성 예시입니다. note-a 같은 값은 가상의 식별 정보이며, 유효한 플랫폼 ID도 API 응답도 아닙니다. 입력은 애플리케이션 측에서 이미 노트 식별 정보로 변환된 상태이며, 실제로 사용하려면 먼저 URL과 note_id 매핑을 완료해야 합니다.

오프라인 데모: 기록됨, 명시적 미전달, 미확인 상태를 구분
function classifyBatch(targetIds, rows, missedIds) {
  const targets = new Set(targetIds);
  if (targets.size !== targetIds.length) throw new Error("duplicate target");
  const missed = new Set(missedIds);
  const counts = new Map(targetIds.map(id => [id, 0]));
  for (const id of missed) {
    if (!targets.has(id)) throw new Error("unknown missed target");
  }
  for (const row of rows) {
    if (!targets.has(row.note_id)) throw new Error("unmatched row");
    counts.set(row.note_id, counts.get(row.note_id) + 1);
  }
  return targetIds.map(note_id => {
    const count = counts.get(note_id);
    if (count > 0 && missed.has(note_id)) {
      throw new Error("conflicting target state");
    }
    return {
      note_id, count,
      state: count > 0 ? "records_observed"
        : missed.has(note_id) ? "not_delivered" : "unconfirmed"
    };
  });
}

console.log(classifyBatch(
  ["note-a", "note-b", "note-c"],
  [{note_id: "note-a", id: "synthetic-comment"}],
  ["note-b"]
));

세 번째 대상은 레코드도 없고 명시적인 미전달 표시도 없으므로, 예시에서는 “댓글 0개”로 채우지 않고 unconfirmed로 둡니다. records_observed도 레코드를 보았다는 뜻일 뿐, all_comments_complete를 뜻하지 않습니다. 상태 이름을 이 구분에 맞춰 두면 이후 통계에서 결론이 의도치 않게 부풀려지지 않습니다.

6단계: 댓글 중복 제거와 콘텐츠 업데이트를 두 계층으로 처리하기

현재 카탈로그에는 id, text, like_count, posted_at, ip_location, author_name, author_id, sub_comment_count, note_id, note_url, platform이 나열되어 있습니다. 이는 필드 선언일 뿐 모든 레코드에 값이 있다는 보장은 아닙니다. 저장하기 전에 실제 반환 형태, 특히 댓글 ID와 상위 노트를 확인하십시오.

필드 정의 및 설명되지 않은 필드 검토
curl -fsS --max-time 30 \
  'https://api.everyinfra.com/api/v1/social/fields?platform=xiaohongshu&action=comments_batch' \
  | jq '{fields, undocumented}'

안정적인 ID가 있다면 “플랫폼 + 노트 ID + 댓글 ID”를 비즈니스 중복 제거 키로 사용하고, 같은 댓글을 다시 가져오면 관측 시각과 변할 수 있는 필드를 업데이트하십시오. 닉네임은 바뀔 수 있고 고유 식별자가 아니므로 키로 쓰지 마십시오. 텍스트가 같다는 이유만으로 레코드를 삭제해서도 안 됩니다. 서로 다른 사용자가 실제로 같은 내용을 쓸 수 있습니다.

안정적인 ID가 없을 때는 “작성자 식별자 + 타임스탬프 + 텍스트 요약” 같은 조합을 남겨 근사 중복 제거에 쓸 수 있지만, 신뢰할 수 있는 식별 정보가 아니라 신뢰도가 낮은 값으로 표시해야 합니다. 참여 지표 변화와 텍스트 수정도 새 댓글과 구분해야 합니다. 같은 댓글을 새로 관측한 것은 새 댓글이 아닙니다.

sub_comment_count는 답글 수 정보를 제공할 뿐이며, 이것만으로 중첩된 답글 본문을 모두 가져왔다고 주장할 수 없습니다. 하위 답글이 필요하면 sub_comments 카탈로그와 실제 반환값으로 확인하십시오. 시간 분석을 할 때는 posted_at과 직접 기록한 observed_at을 분리하고, 파싱에 실패한 값을 요청 날짜로 조용히 대체해서는 안 됩니다.

이 식별 규칙을 PostgreSQL로 구현한다면 복합 고유 제약 조건으로 결합 식별자를 표현할 수 있지만, ID가 없는 경우는 따로 처리해야 합니다. 공식 문서에 따르면 기본 고유 제약 조건에서 NULL 값은 서로 같다고 비교되지 않으므로, unique를 추가해도 식별 정보가 불완전한 중복 레코드가 자동으로 해결되지 않습니다. 이는 로컬 저장소 설계 예시이며 API 내부 데이터베이스와는 관계가 없습니다.

7단계: 대상별 비용을 검증하고 명확히 정의된 대상만 재시도하기

이런 배치는 HTTP 호출 수나 반환된 댓글 수가 아니라 제출한 대상 수를 기준으로 과금됩니다. 제출 전에 중복을 제거하고, 반환 후에는 과금 정보를 보존하십시오. 현재 구현에는 일부 대상이 전달되지 않았을 때를 위한 환불 필드가 있으며, 해당하는 경우 total_targets, billed_targets, refunded_credits가 포함됩니다. 모든 과금 정보에 이 세 필드가 있다고 가정하거나 count로 billed_targets를 대신하지 마십시오.

특히 두 카운터를 혼동해서는 안 됩니다. 노트 하나가 댓글을 여러 개 반환해도 대상은 하나이며, 노트가 기대보다 적은 댓글을 반환했다고 해서 댓글 수에 비례해 환불액을 산정할 수는 없습니다. 응답과 과금 기록을 감사의 근거로 삼고, 필드가 없는 경우는 응답에서 확인 불가로 표시하십시오. 이 가이드는 고정 단가를 제시하지 않으며, 실제 요금과 계정 조건은 호출 시점의 약관을 따릅니다.

재시도 전에 실패 유형부터 판단하십시오. 422는 보통 링크를 고치거나 대상 배열을 줄이는 등 입력을 수정해야 합니다. 명확히 전달되지 않은 대상은 원인을 확인했고, 사용이 여전히 승인된 상태이며, 재시도가 의미 있을 때만 제한된 재시도에 넣으십시오. 이미 레코드를 반환한 대상을 실패한 배치에 다시 섞지 마십시오.

네트워크 시간 초과는 missed와 다릅니다. 시간 초과는 완전한 응답을 받지 못했다는 뜻이므로 배치의 어떤 대상이 실행되었는지 확인할 수 없습니다. 이럴 때는 배치 전체를 자동으로 다시 보내지 말고, 상태 미확인 시도로 보존한 채 호출 로그를 조회하거나 확인을 요청하십시오. 클라이언트의 batch_id는 서버 측 멱등성을 보장하지 않습니다. 비멱등 요청의 재시도 제한은 공식 HTTP 명세를 참고하십시오.

댓글을 확보한 뒤 조사용 데이터셋 만들기

분석 테이블은 특정 노트와 댓글까지 거슬러 추적할 수 있는 것이 좋지만, 사용 가능한 필드를 모두 보존할 필요는 없습니다. 상품 관련 문제를 조사한다면 댓글 텍스트, 출처 정보, 타임스탬프, 주제 태그로 충분할 수 있습니다. 닉네임, 작성자 식별자, ip_location을 저장할지는 반환되었다는 이유로 모두 장기 보관하지 말고 실제 목적에 따라 결정해야 합니다.

모델 판단은 별도 계층에 두고 모델 버전, 분류 규칙 버전, 수동 수정 내역을 보존하십시오. 반어, 부정, 타인 인용이 포함된 샘플은 레이블이 불안정할 수 있습니다. 보고서에는 모델 점수를 사용자의 실제 태도로 포장하지 말고 검증 가능한 예시와 샘플링 범위를 보여 주십시오.

특히 성공적으로 반환된 일부 댓글을 알 수 없는 전체 수의 분모로 쓰지 마십시오. 설명 가능한 보고서는 중복 제거 후 확인한 대상이 몇 개인지, 어떤 대상에 레코드가 있었는지, 어떤 대상이 미전달이거나 미확인인지, 어떤 관측 기간을 다루었는지를 밝힙니다. 이런 보고서는 입증되지 않은 플랫폼 전체의 평판이 아니라 이 샘플 안에서의 질문에 답합니다.

규모를 늘리기 전에 범위와 보존 규칙 정하기

공개되어 있다고 해서 콘텐츠 권리, 개인정보 처리, 재배포 권한 문제가 자동으로 해결되지는 않습니다. 해당 플랫폼 규칙과 구체적인 사용 사례를 검토해 누가 원문에 접근할 수 있는지, 얼마나 보관하는지, 언제 삭제하는지, 결과를 다른 시스템에서 처리해도 되는지를 정해야 합니다. 이 가이드는 로그인, 공개 범위 설정, 접근 제한을 우회하는 방법을 제공하지 않습니다.

본격적인 배치 실행 전에 최소한 전달 가능한 대상 1건, 입력 오류 1건, 부분 결과 1건, 알 수 없는 상태 복구 1건을 확인하십시오. 빈 결과의 비즈니스적 의미는 따로 검증해야 합니다. 아직 이런 상황을 설명할 수 없다면 요청량을 늘려 절차의 공백을 가리지 말고 작은 규모를 유지하십시오.

보존 범위는 진단 로그에도 적용해야 합니다. OWASP는 액세스 토큰, 세션 식별자, 민감한 개인 데이터를 제거하거나 별도로 처리하도록 권고합니다. 문제 해결을 위해서는 댓글 배치 전체를 기본 로그 필드로 남기지 말고 요청 참조, 대상 수, 상태 요약을 우선 남기십시오.

신뢰할 수 있는 배치가 완료 후 남겨야 할 것

최종 산출물에는 댓글 파일만이 아니라 중복 제거된 대상 목록, 시도별 요청 식별자, 대상 단위 수집 상태, 댓글 식별 정보와 출처, 실제 과금 기록, 아직 검토가 필요한 항목도 포함되어야 합니다. 그래야 다음 실행에서 처음부터 다시 스크래핑하지 않고 다음에 처리할 대상을 정확히 알 수 있습니다.

작은 배치로 확장하기 전에 먼저 노트 하나로 파이프라인 전체를 점검하십시오. 배치 인터페이스는 반복적인 요청 조립 작업을 줄여 줍니다. 댓글 조사를 실제로 신뢰할 수 있게 만드는 것은 모든 레코드의 출처를 설명하고 누락된 부분을 솔직하게 밝히는 능력입니다.

참고 LF-02