Наша собственная статистика посещений включена всегда. Google Analytics помогает нам понять, как пользуются нашими публичными страницами (но не вашим рабочим пространством после входа), и включается только с вашего разрешения. См. нашу политику конфиденциальности.

Как получить комментарии Douyin: от URL видео к JSON со структурой ответов

Начните с актуальных параметров douyin.comments, получайте комментарии с помощью минимальных запросов, сохраняйте источник видео, ID комментариев и связи ответов, обрабатывайте лимиты, пустые результаты, разбор временных меток и повторные наблюдения.

После выгрузки комментариев Douyin в JSON легко упустить распространённую проблему: строка в файле может быть самостоятельным комментарием или ответом на другой комментарий. Если считать все строки самостоятельной обратной связью пользователей, теряется контекст, а смешение числа лайков, числа ответов и общего числа комментариев делает метрики вовлечённости в обсуждение неоднозначными.

Поэтому получение комментариев по URL видео не сводится к вызову API, который возвращает массив. Полный процесс интеграции включает проверку того, какое видео выбрано, чтение актуальных параметров, сохранение идентификаторов комментариев и связей ответов с родительскими комментариями, а также явное указание того, что охватывает и не охватывает наблюдение. В этой статье действие douyin.comments в EveryInfra разбирается пошагово. Материал предназначен для разработчиков, которые планируют включить комментарии в исследовательские таблицы, процессы анализа контента или ручной проверки.

Параметры и поля в этом разделе сверены с публичным каталогом и исходным кодом на 2026-09-04. Шаблон запроса в этот раз не выполнял платных операций; синтетический JSON ниже нужен только для демонстрации нормализации и не является образцом реального видео, пользователя или комментария.

Определите, с каким типом интерфейса вы интегрируетесь

Если вы реализуете вход с авторизацией через Douyin, публикацию контента или другие официальные возможности открытой платформы, выберите соответствующий сценарий в списке API Douyin Open Platform и проверьте тип приложения и права доступа. Официальные токены доступа, параметры и коды ошибок относятся к официальному API Douyin и не могут напрямую использоваться в запросах к API данных EveryInfra, описанных в этой статье.

В статье рассматривается только получение комментариев после определения целевого видео. Сведения о видео, комментарии, поиск и транскрибация речи в EveryInfra доступны как отдельные действия. Если вы хотите проанализировать обсуждение в комментариях, не выдавайте заголовки видео или расшифровки речи за текст комментариев. Если контент приватный, удалённый или доступ к нему ограничен, наличие URL видео само по себе не означает, что у вас сохранился доступ к чтению.

Шаг 1. Проверьте актуальные параметры запроса комментариев

Ознакомьтесь с полным описанием возможности получения комментариев Douyin
curl -fsS --max-time 30 \
  'https://api.everyinfra.com/api/v1/social/catalog?platform=douyin' \
  | jq -e '.capabilities[] | select(.action == "comments") | {
      action, required_params, optional_params, param_meanings,
      mode, available, returns_list,
      default_limit, max_limit, response_fields
    }'

В текущем каталоге указано, что для douyin.comments обязателен параметр url, результат возвращается синхронно и представляет собой список, значение count по умолчанию равно 20, а максимальное значение limit — 100. Для возможностей, возвращающих списки, можно использовать общий параметр limit. Эти значения ограничивают масштаб запроса, но не гарантируют ни получение 100 записей, ни то, что у видео не более 100 комментариев.

Текущее описание этого действия не предоставляет вызывающей стороне параметров page, cursor или has_more, а также параметров сортировки или фильтрации. Не добавляйте sort: newest самостоятельно, чтобы получить 100 самых новых записей: поддержка параметра в других действиях ещё не означает, что его поддерживает comments. Если нужную сортировку или пагинацию нельзя подтвердить по документации, прямо укажите, что требование пока не выполнено, и не позволяйте программе незаметно подменять его произвольной выборкой.

Описание response_fields в каталоге относится к записям комментариев, а не к протоколу пагинации. Даже если в будущем в записях появится поле cursor, сначала убедитесь, что оно относится к механизму пагинации, и только затем используйте его для перехода к следующим запросам. Не создавайте циклы, опираясь только на имена полей.

Шаг 2. Перед запросом установите, какое именно видео выбрано

На вход следует передавать корректные ссылки на видео, которые поддерживает эта возможность. На практике коллеги, которые ведут операционную работу, могут передать текст из меню «Поделиться», короткую ссылку или несколько URL одного видео с разными параметрами запроса. Если до вызова точно определить целевое видео, устранять неполадки будет проще, чем при передаче в url произвольной строки.

На уровне бизнес-логики рекомендуется хранить три значения: исходное значение, переданное пользователем, подтверждённый URL запроса и надёжно установленный ID видео. Если надёжно получить ID видео не удаётся, временно используйте подтверждённое исходное значение для контролируемой привязки. Не угадывайте ID по количеству цифр и не принимайте короткие коды из текста «Поделиться» за идентификаторы видео.

Если одно и то же видео встречается в нескольких исследовательских списках, результаты одного наблюдения, выполненного в рамках имеющихся прав, можно использовать повторно, но необходимо сохранять сведения о связанных с ним исследовательских задачах. И наоборот, два видео с одинаковым заголовком или одними и теми же людьми в кадре нельзя объединять автоматически. При дедупликации нужно учитывать, один ли это и тот же контент, а не только сходство текстов.

Нормализация ссылок должна устранять только те различия в представлении, которые, как вы убедились, не имеют значения. Не удаляйте все параметры запроса без разбора; исходные входные данные и фактически отправленные значения храните отдельно. Неподтверждённые перенаправления, недоступный контент или ссылки на другие объекты отправляйте на ручную проверку, а не пытайтесь бесконечно повторять запрос, меняя формат ссылки.

Шаг 3. Выполните минимальный запрос для одного видео

Предположим, что на сервере заданы EVERYINFRA_API_KEY и переменная EVERYINFRA_DOUYIN_VIDEO_URL с URL видео, разрешённого к обработке. Выполните один приведённый ниже запрос, проверьте поля на небольшом числе результатов, не включайте автоматические повторные попытки и не утверждайте, что API обязательно вернёт максимально допустимое число записей.

Шаблон минимального запроса к API комментариев Douyin
: "${EVERYINFRA_API_KEY:?set the API Key first}"
: "${EVERYINFRA_DOUYIN_VIDEO_URL:?set the authorized full video URL first}"
jq -cn --arg url "${EVERYINFRA_DOUYIN_VIDEO_URL}" '{
  platform: "douyin",
  action: "comments",
  params: {url: $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 @-

В обычном синхронном ответе верхний уровень описывает запрос, а записи комментариев находятся в results. Также обратите внимание на id, count, billing и другие данные. Сохраните внешний id как идентификатор запроса API и не перезаписывайте им id комментариев внутри results. 180 секунд — это время ожидания клиента в данном примере, а не гарантия быстродействия API. После тайм-аута статус результата следует считать неизвестным.

При первичной интеграции не спешите оставлять только текст. Прежде чем сохранять комментарии в базе данных, проверьте, что они относятся к нужному видео, что ID стабильны и что в ответе присутствуют флаги ответов и поля связей. Если ответ нельзя разобрать как JSON или объект данных не является ожидаемым списком, сохраните данные об ошибке и прекратите разбор этого ответа, а не возвращайте пустой массив, имитируя успех.

Шаг 4. Разделяйте содержание комментариев, вовлечённость и связи ответов

Сверяйтесь со словарём полей и не интерпретируйте их только по названиям
curl -fsS --max-time 30 \
  'https://api.everyinfra.com/api/v1/social/fields?platform=douyin&action=comments' \
  | jq '{fields, undocumented}'

Текущее описание включает поля id, text, like_count, reply_count, author_name, author_id, ip_location, liked_by_author, is_reply, reply_to_id, posted_at и platform. Их можно разделить по назначению на четыре группы; не нужно пытаться любой ценой заполнить все столбцы:

  • Идентификация и источник: id, platform и источник видео, который приложение связывает с записью на основе контекста запроса. Никнеймы авторов не могут использоваться как идентификаторы комментариев.
  • Содержание и время: text и posted_at, а также добавленное вами поле observed_at с фактическим временем наблюдения.
  • Показатели вовлечённости: like_count, reply_count, liked_by_author. Отсутствующее значение не равно нулю, а отметка «Нравится» от автора видео не свидетельствует о конверсии или коммерческой ценности.
  • Связи ответов: is_reply и reply_to_id. Отсутствующие связи помечайте как требующие проверки и не подставляйте родительские комментарии автоматически по сходству текста или позиции в массиве.

Код распределяет полученные комментарии и ответы по спискам и сохраняет идентификаторы связей, если их подтверждают данные. Поэтому плоский массив не означает, что все записи находятся на одном смысловом уровне. При обработке сначала постройте индекс ID комментариев и только затем обрабатывайте связи ответов. Не считайте, что следующая запись обязательно является ответом на предыдущую.

reply_count показывает число ответов, но не гарантирует, что массив содержит все эти ответы. Если is_reply имеет значение true, но reply_to_id отсутствует, либо reply_to_id указывает на комментарий, которого нет в текущих результатах, такой разрыв связи нужно сохранить. Не добавляйте выдуманные родительские комментарии для построения полного дерева. В отчёте нельзя заявлять о полноте ветки только потому, что дерево удалось построить.

Шаг 5. Спроектируйте JSON для бизнес-логики так, чтобы сохранять идентификаторы

Источник видео следует связывать с записями по подтверждённому контексту запроса, не рассчитывая на то, что данные комментария будут содержать копию URL видео. В базе данных ключом может служить сочетание platform, идентификатора видео и ID комментария. Когда тот же комментарий появляется снова, добавляйте новое наблюдение или обновляйте изменяемые поля, но не учитывайте комментарий повторно как новый.

Идентификаторы лучше сохранять в исходном строковом виде. Если длинные десятичные идентификаторы сначала преобразовать в значения JavaScript Number, а затем обратно в строки, исходные значения могут оказаться невосстановимыми. Спецификация JSON описывает пределы точности, важные для совместимости числовых значений. Хранение в виде строк идентификаторов, которые не участвуют в арифметике, позволяет избежать бессмысленных числовых преобразований.

JavaScript-код ниже представляет собой намеренно ограниченный офлайн-конвертер: он принимает ID комментариев только в подтверждённом строковом формате и не позволяет автоматически записывать в базу данных значения нераспознанных типов. Он сохраняет лишь поля и состояния связей, необходимые для этой статьи, не является полноценным SDK и не выполняет сетевых запросов. Искусственно созданный текст явно помечен как пример и не может использоваться как реальный отзыв пользователя.

Офлайн-демо: разрыв связи сохраняется как отдельное состояние, родительский комментарий не выдумывается
function mapComment(row, source) {
  if (!row || typeof row.id !== "string" || !row.id.trim()) {
    throw new Error("string comment id required");
  }
  if (row.reply_to_id != null && typeof row.reply_to_id !== "string") {
    throw new Error("string parent id required");
  }
  const parentId = row.reply_to_id || null;
  const isReply = typeof row.is_reply === "boolean" ? row.is_reply : null;
  const relation = isReply === true
    ? parentId ? "reply_with_reference" : "reply_parent_unknown"
    : isReply === false && !parentId ? "top_level"
    : "needs_review";
  return {
    platform: "douyin",
    video_ref: source.video_ref,
    comment_id: row.id,
    text: typeof row.text === "string" ? row.text : null,
    posted_at_raw: row.posted_at ?? null,
    observed_at: source.observed_at,
    like_count: typeof row.like_count === "number"
      && Number.isFinite(row.like_count) && row.like_count >= 0
      ? row.like_count : null,
    reply_to_id: parentId,
    relation
  };
}

console.log(mapComment(
  {
    id: "synthetic-reply",
    text: "synthetic example, not a real review",
    is_reply: true,
    like_count: null
  },
  {
    video_ref: "synthetic-video",
    observed_at: "2026-09-04T00:00:00Z"
  }
));

В этом примере posted_at_raw сохраняется без принудительного преобразования в метку времени. При записи в базу данных можно отдельно хранить преобразованную метку времени, использованный часовой пояс и статус разбора. Если единицы измерения или формат не подтверждены, не угадывайте, указаны ли секунды, миллисекунды или локальное время. Отсутствующее значение text также остаётся null, чтобы модели не восполняли исходный текст догадками.

reply_with_reference означает лишь, что ссылка на родительский комментарий указана. Наличие этого комментария всё равно нужно проверить среди полученных записей того же видео. Если его нет, сохраните ссылку и пометьте её как внешнюю либо ведущую к неполученному комментарию. top_level — тоже лишь классификация по текущим полям, а не доказательство того, что структура обсуждения на исходной платформе восстановлена полностью.

Подтвердив единицы и смысл исходного времени, можно записывать разобранное время по RFC 3339, сохраняя Z или явное смещение относительно UTC. Спецификация решает вопрос формата представления: она не определит, секунды или миллисекунды означает последовательность цифр в источнике, и не превратит неизвестный часовой пояс источника в часовой пояс текущей машины.

Шаг 6. Отделяйте регулярные наблюдения от полного постраничного обхода

Поскольку в текущих публичных запросах нет параметра пагинации, точное повторение одного и того же запроса даёт несколько наблюдений, а не вторую или третью страницу. Даже если результаты нескольких запросов случайно различаются, нельзя утверждать, что вы получили все комментарии.

При настройке задач по расписанию можно объединять последовательные наблюдения по идентификатору комментария и хранить время, когда комментарий был замечен впервые и в последний раз. Можно добавлять комментарии, появившиеся позже, и обновлять показатели вовлечённости и связи ответов. Однако отсутствие комментария в текущем запуске не означает, что его удалили. Если нет явных признаков удаления или отдельного подтверждения, фиксируйте лишь то, что в этот раз комментарий не наблюдался.

Если нужно сравнить два периода, сначала приведите к сопоставимому виду наборы видео, параметры вызовов, частоту наблюдений и долю успешно охваченных целей. Если на прошлой неделе были проверены все цели, а на следующей удалось проверить лишь некоторые из них, само по себе число комментариев не доказывает снижение вовлечённости. Указать в отчёте различия в охвате часто важнее, чем добавить ещё одну метку тональности.

Также уточните единицу подсчёта: считаете ли вы записи комментариев, ветки обсуждения или уникальных авторов. Ситуации, когда несколько человек отвечают в одной ветке и когда один человек публикует несколько комментариев подряд, отражают разные структуры обсуждения. Число уникальных пользователей в отчёте нельзя заменять числом записей. Если надёжных идентификаторов авторов нет, не заявляйте о подсчёте уникальных пользователей.

Шаг 7. Проверяйте ошибки, пустые результаты и списания по отдельности

При обработке сбоев различайте как минимум недопустимые входные данные, ошибки аутентификации или прав доступа, исчерпание квоты, временные сбои сервиса, пустой список в ответе и случаи, когда клиент не смог получить ответ целиком. Для каждого случая нужны свои действия. Если записывать их все как «нет комментариев», это введёт в заблуждение и пользователей, и систему мониторинга.

При ошибке 422 исправьте запрос с учётом error.code и сообщений об ошибке, не повторяя его неограниченное число раз. При временных сбоях перед повторной попыткой проверьте, мог ли исходный запрос быть выполнен; тайм-аут POST-запроса не означает, что запрос не поступил на сервер. Если контент явно подтверждён как недоступный, прекратите автоматические попытки и проверьте область и права доступа, не пытаясь обойти ограничения.

Данные о списании также нужно привязывать к тому же запросу. Сохраняйте фактические данные о списании и ID запроса API, а не рассчитывайте сумму к оплате по числу собранных комментариев. Сбой локального разбора данных сам по себе не позволяет определить, завершил ли сервер обработку или отменил списание. Если в ответе отсутствуют поля со сведениями о списании, помечайте его статус как «требует проверки», а не как «бесплатно».

Сохраняйте контекст и возможность ручной проверки, прежде чем передавать комментарии моделям

Для классификации комментариев лучше подавать на вход ветки или фрагменты с чёткими границами. Если ответ звучит просто как «да не особо», по этим трём словам невозможно понять, против чего возражает автор. Если родительский комментарий не получен, сохраняйте статус «недостаточно контекста», а не навешивайте принудительно позитивную или негативную метку.

Сохраняйте производные результаты отдельно: темы, тональность, версии правил оценки, версии моделей и ручные исправления. Не записывайте эти поля обратно в столбцы с исходными фактами о комментариях и не считайте уверенность, которую выдаёт модель, измерением реального отношения пользователей. В отчётах сохраняйте достаточно связей с источниками, чтобы уполномоченные сотрудники могли их проверить; в публичных материалах показывайте только необходимый контент, разрешённый к показу.

Чек-лист перед масштабированием

  • Цель: идентификаторы видео соответствуют URL запросов, права доступа и сценарии использования подтверждены, а приватный контент не обрабатывается в обход установленных процедур.
  • Структура: results содержит ожидаемый список, идентификаторы комментариев сохраняются без потери точности, а записи можно связать с конкретными видео.
  • Связи: поля ответов сохранены, отсутствующие родительские комментарии и неизвестные состояния видны, ветки не додумываются.
  • Наблюдения: пропущенные значения не заполняются нулями, время публикации и время наблюдения хранятся раздельно, а о полном охвате не заявляется без доказательств.
  • Эксплуатация: тайм-ауты не приводят к автоматической повторной отправке, ошибки и списания сохраняются вместе с идентификаторами запросов, а повторные наблюдения не учитываются как новые комментарии.
  • Хранение: сохраняются только необходимые поля; для исходного текста и данных об авторах настроен контроль доступа, установлены сроки хранения и график удаления.

При составлении схем для адаптированного бизнес-JSON учитывайте, что наличие описания поля, обязательность поля и допустимость null — разные условия. Ключ properties в JSON Schema по умолчанию не делает поля обязательными: задавайте обязательность отдельно с помощью required и ограничений type, а не используйте пустую строку для обозначения всех неизвестных значений.

Передавайте набор данных с понятным контекстом, а не просто JSON-файлы

Набор комментариев, пригодный для повторного использования, должен позволять специалистам, которые продолжат с ним работу, понять, какие цели он охватывает, откуда получены данные, когда проводились наблюдения, как связаны ответы и где данные отсутствуют. Тогда JSON можно надёжно включать в процессы анализа, проверки и отчётности. Иначе выгрузка множества записей просто переносит исходную неопределённость в другой формат файла.

Начните с одного видео, на которое у вас есть права, сначала проверьте структуру и связи, а затем постепенно добавляйте цели. Документация и актуальные каталоги ниже помогут подтвердить текущие возможности; не подменяйте проверку при интеграции параметрами из старых статей.

Справочник LF-03