当社独自のアクセス統計は常に有効です。Google Analyticsは、公開ページ(ログイン後のワークスペースは対象外)の利用状況を把握するためのもので、許可をいただいた場合のみ有効になります。詳しくは プライバシーポリシーをご覧ください。

Xiaohongshuのコメントを一括取得する:ノート一覧から検証可能な結果まで

Xiaohongshuのcomments_batchを組み込む方法を順を追って解説します。完全なノートURLの準備、URL数とコメント数の区別、部分取得と未取得の識別、ノート単位の重複排除、空の結果、リトライ、対象ごとの課金への対応を取り上げます。

Xiaohongshuのコメント調査では、最初の作業は感情分析ではなく、ノートURLの整理になることが少なくありません。複数の担当者が提出したリストには重複したノートが含まれ、時間が経つと一部のリンクは失効します。一括リクエストが200を返しても、結果には一部のノートしか含まれていないことがあります。こうした問題を先に解決しないと、後で出てくる「頻出する問題」や「ネガティブ比率」は、単に収集漏れを別の形で表しているだけかもしれません。

EveryInfraのxiaohongshu.comments_batchエンドポイントは、ノートURLの配列を受け取り、複数の対象をまとめて送信できます。このガイドでは1件のノートから始め、ログを残して検証できる一括処理へ拡張する方法を詳しく説明します。対象の準備、パラメータの確認、リクエストの送信、コメントをそれぞれが紐づくノートごとに整理する方法、リトライできる対象の判断までを扱います。

このガイドの入力制約とレスポンスの扱いは、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で、対象の配列は対象ごとに課金されます。ここには2つの数量がある点に注意してください。urlsの長さはノート数で、結果のcountは返されたレコード数です。200を「一度に200件のノートを送信できる」と解釈すると、リクエスト設計の最初の段階でつまずきます。

現在のソースコードでは、comments_batchに対して明示的な入力チェックがあります。urlsは空でないリストで最大20件、各項目はノート識別子に正しく解決でき、空でないxsec_tokenパラメータを含んでいる必要があります。対象数の上限を超えたり、必要なリンク情報が欠けていたりすると、パラメータエラーになります。この20という値はこの実装のチェックによるもので、max_limitから算出したものではありません。

リンクにトークンが含まれていても、サーバーがそのトークンがまだ有効だと確認したわけではありません。現在のパラメータチェックでは、識別子とパラメータが存在するかどうかは判断できますが、文字列だけでは、その対象が今この時点で取得可能かどうかは判断できません。トークンを含むリンクをすべて有効とみなすローカルのチェッカーを書き、その通過率をコメント収集の成功率として扱うことは避けてください。

ステップ2:リクエスト用リンクとノート識別情報の個別保存

リクエストには、承認された経路で取得した完全なノートURLを使ってください。URLを見やすくするために送信前にクエリパラメータを削除したり、アクセスパラメータを推測・偽造したり、ある対象のパラメータを別の対象に流用したりしないでください。短縮リンクや共有テキストは、まずこの機能が受け付けるノートリンクに整えてから使います。インターフェースが任意の共有テキストを解析してくれるとは想定しないでください。

また、重複排除をURL文字列全体の比較だけに頼るべきではありません。同じノートへのリンクでも異なるクエリパラメータが付いていることがあり、文字列で重複排除すると同じ対象が複数残ります。識別キーと現在使えるリンクは分けて管理してください。識別キーはプラットフォームとノートIDで構成し、リンクは元の値を取得日時とともに保持します。

  • 対象リスト:ノートの識別キー、完全なリクエスト用リンク、取得経路、取得日時、この調査で必要な理由。
  • 重複排除リスト:どの入力が同じノートを指しているか、最終的にどの確認済みリンクを採用したか。重複を単に削除するのではなく、取得元との対応関係を残します。
  • 要確認リスト:解析できないリンク、パラメータが欠けているリンク、同じ識別キーで取得元が食い違うリンク。これらは有料の一括処理に混ぜず、先に対処してください。

完全なリンクは、管理された入力やタスク用のストレージにのみ保存してください。ログ、チケット、エクスポートしたレポートでは、通常はノートIDと加工済みの出典情報があれば足ります。有効期限のあるアクセスパラメータをあちこちにコピーすべきではありません。読みやすい出典を共有する場合は、実際のリクエスト入力を壊さないよう、公開表示に適したリンクを別に用意してください。

ステップ3:1件のノートによるリクエストと紐付けの事前確認

以下の例ではバッチアクションの形式を使いますが、コメントが実際にその対象のものかを確認しやすいよう、配列には承認済みのノートを1件だけ入れています。サーバーにはEVERYINFRA_API_KEYとEVERYINFRA_XHS_NOTE_URLが設定済みであるものとします。後者には、この記事のために作ったプレースホルダーではなく、実在するノートの完全なリンクを設定してください。

単一対象のバッチリクエストテンプレート(送信は1回のみ)
: "${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に含まれていなくても、最終的に切り詰められたリストにその対象のレコードが残っているとは限りません。

ノートごとに個別の受け入れ確認が必要な場合、初期バージョンでは単一対象のリクエスト単位でタスクを組み、返された結果をそれぞれ検証できます。「バッチ処理」を、すべてのノートを1つのレスポンスにまとめる必要があるものと捉えないでください。複数対象のバッチを使う場合は、結果がどの対象のものかを対象ごとに検証し、「ノート数×limit」を想定受信件数としてハードコードするのではなく、件数に関する仕様を統合時の受け入れ確認に組み込んでください。

バッチの識別子もAPIリクエストとは独立させてください。独自のbatch_idは業務上の対象の集合を表し、API呼び出しで返されるIDは1回の呼び出しを表します。リトライ時は元のバッチに紐づく新しい試行レコードを作成し、最初のレスポンスを上書きして問題を追跡する手がかりを失わないようにします。

ステップ5:partial、missed、対象ごとの結果を解釈する

この機能の同期レスポンスでは、コメントのリストが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"]
));

3番目の対象にはレコードも明示的な未返却フラグもないため、この例では「コメント0件」として埋めず、未確認のままにしています。同様に、records_observedはレコードが確認されたことを示すだけで、all_comments_completeを意味するものではありません。ステータス名をこの区別に合わせておくことで、後続の集計で結論を誤って過度に一般化するのを防げます。

ステップ6:コメントの重複排除と内容の更新を2つの層で扱う

現在のカタログには、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が含まれます。すべての課金情報にこの3つのフィールドがあるとは想定せず、countでbilled_targetsを代用しないでください。

特に、2つのカウンターを混同してはいけません。複数のコメントを返したノートも1つの対象であり、期待より少ないコメントしか返らなかったノートについて、件数に比例して返金されるわけではありません。監査はレスポンスと課金記録に基づいて行い、フィールドが欠けている場合はレスポンスから確認できないものとして記録してください。このガイドでは固定の単価は示していません。実際の料金とアカウントの条件は、呼び出し時点の規約に従います。

リトライする前に失敗の種類を判断してください。422は通常、リンクの修正や対象配列の絞り込みなど、入力の変更が必要です。明確に返されなかった対象は、原因を確認済みで、用途が引き続き承認されており、リトライに意味がある場合に限って、回数を制限したリトライに回します。すでにレコードが返された対象を、失敗したバッチに戻して混ぜないでください。

ネットワークのタイムアウトはmissedとは異なります。タイムアウトは完全なレスポンスを受け取れなかったことを意味し、バッチ内のどの対象が実行されたかを確認できません。この場合は、結果不明の試行として記録を残し、バッチ全体を自動的に再送するのではなく、呼び出しログを照会するか確認を依頼してください。クライアント側のbatch_idは、サーバー側での冪等性を保証するものではありません。非冪等なリクエストのリトライの制約については、公式のHTTP仕様を参照してください。

コメント取得後に調査用データセットを構築する

分析用テーブルは、特定のノートやコメントまでたどれることが望ましいものの、利用可能なフィールドをすべて保持する必要はありません。製品の課題を調査するなら、コメント本文、出典の参照情報、タイムスタンプ、トピックタグで十分な場合があります。ニックネーム、作成者の識別子、ip_locationを保存するかどうかは、返されたからという理由ですべてを長期保存するのではなく、実際の目的に応じて決めてください。

モデルによる判定は別の層に置き、モデルのバージョン、分類ルールのバージョン、手動での修正を記録しておきます。皮肉、否定、他者の引用を含むサンプルでは、ラベルが安定しないことがあります。レポートには検証可能な例とサンプリングの範囲を示し、モデルのスコアをユーザーの本当の態度として提示しないでください。

具体的には、正常に返された部分的なコメント結果を、総数が不明な状況での分母として扱わないでください。説明可能なレポートでは、重複排除後に確認した対象の数、レコードがあった対象、未返却または未確認の対象、カバーした観測期間を明示します。このレポートが答えるのはこの特定のサンプルに関する問いであり、裏付けのないプラットフォーム全体の評判ではありません。

規模を拡大する前に対象範囲と保持ルールを定める

公開されているからといって、コンテンツの権利、個人情報の取り扱い、再配布の許可が自動的に解決されるわけではありません。適用されるプラットフォームの規約と具体的なユースケースを確認し、誰が原文にアクセスできるか、どのくらいの期間保持するか、いつ削除するか、結果を他のシステムで処理してよいかを判断する必要があります。本ガイドでは、ログイン、プライバシー設定、アクセス制限を回避する方法は扱いません。

本番のバッチ実行前に、正常に結果を取得できる対象、入力エラー、部分的な結果、不明な状態からの復旧について、それぞれ少なくとも1件確認してください。空の結果が業務上何を意味するかも、別途確認する必要があります。これらの状況をまだ説明できない場合は小規模のまま運用し、プロセス上の不備を覆い隠すためにリクエスト量を増やさないでください。

保持の対象には診断ログも含める必要があります。OWASPは、アクセストークン、セッションID、機微な個人データを削除するか、適切に取り扱うことを推奨しています。トラブルシューティングでは、コメントの一括取得結果全体をデフォルトのログフィールドとして記録するのではなく、リクエストの参照情報、対象件数、ステータスの概要を残すことを優先してください。

信頼できるバッチが完了時に残すべきもの

最終的な出力はコメントファイルだけでは不十分です。重複排除済みの対象リスト、各試行のリクエストID、対象ごとの収集ステータス、コメントのIDと取得元、実際の課金記録、引き続き確認が必要な項目も含めてください。これにより、次回の実行で一から再取得することなく、次に処理すべき対象を正確に把握できます。

まず1件のノートでパイプライン全体を通して確認し、その後に小規模なバッチへ広げてください。バッチ用インターフェースを使うとリクエストを繰り返し組み立てる手間が減ります。ただし、コメント調査の信頼性を本当に支えるのは、すべての記録の出所を説明でき、欠けていたものを正直に示せることです。

参考資料 LF-02