What does Douyin Benchmark do?
Let the script handle public account data collection, aggregation, and HTML rendering; the agent writes a Markdown report with evidence boundaries based on report_data.json.
Boundaries
- Only collect publicly visible web data; do not crack signatures, bypass CAPTCHAs, or circumvent access controls.
- Only use the
~/.douyin-benchmark/chrome-profilededicated login state. Do not read, copy, or directly point to the user's daily Chrome user directory; do not output cookie values. - Keep missing values as missing; do not rewrite them as
0. Likes can only serve as a proxy for public engagement, not as a measure of play performance, interaction rate, or follower conversion. - Distinguish between a regular login form, real security verification, and page service errors. Stop on encountering a security challenge, page service error, or empty response soft block; do not retry in a loop, auto-switch IPs, create delayed tasks, or promise to complete later.
Initial Setup
Run the following commands in this Skill's directory:
npm ci
node scripts/login.js
login.js opens a dedicated browser, authenticates to a real account entry point, then automatically saves and closes. To check an existing login state, run node scripts/login.js --check.
One-Command Execution
Prefer to ask the user for the Douyin App "Share Profile" short link. A numeric Douyin ID is not the same as sec_uid; directly concatenating /user/<number> may silently redirect to the feed.
OUT="$HOME/douyin-benchmark-output/<account-identifier>"
node scripts/run_benchmark.js "<profile-share-link/sec_uid/douyin-id/nickname>" "$OUT" \
--expected-id "<douyin-id, optional>"
By default, it uses headed Chrome for better observability and a real browser UA. Only explicitly add --headless in headless environments. The script executes in order:
- Resolve the short link or
sec_uid; only use in-app search when given a nickname or Douyin ID. - Collect profile info, posts, and collections, writing them to
account_full_raw.json. - If posts exist, generate
report_data.jsonandreport.html. - Update
run_status.jsonthroughout; do not continue analysis after a failure.
Check Status First, Then Write Conclusions
Read run_status.json and account_full_raw.json.capture:
complete: The API returnedhas_more=0. A full account-level report can be written; still compare the homepage post count with the collected count.partial: Only write a "sampled post report"; indicate the sample size, total homepage post count, and coverage near the title. Do not use terms like "full" or "account's max."blocked_empty_body: The posts endpoint returned HTTP 200 with an empty body. Stop requests, deliver only the profile snapshot and diagnostics; any retry must be user-initiated, and at most once.challenge: Ask the user to complete the verification in the dedicated browser before retrying; do not attempt to bypass.login_required: The page shows a regular login form, not a security challenge. Run the login script, then retry only once after success.page_service_error: The posts area explicitly shows "Service error, please refresh." Stop refreshing; after verifying the login state, retry at most once.no_post_response: May be an incorrect input or a page/API change. First check thesec_uidand the headed page; do not blindly increase scroll count.endpoint_error: The posts endpoint returned an HTTP/API error or non-JSON content. Keep diagnostics and stop analysis; first check for page/API changes.
Writing the Markdown Report
Only when report_data.json exists and contains analyzable posts, fully read the references/report-template.md and write $OUT/report.md following the nine-section structure.
Classify content into three types and use precise wording:
- Facts: Public fields from the JSON and their computed results.
- Observations: Titles, bios, collection names, cover images, or content of videos actually opened.
- Inferences: Persona, monetization path, content formula; write down evidence and confidence level, do not present as fact.
Do not write "popular comment sentiment" without comment text; do not claim camera work, voiceover, acting, or edit details without having watched the video. If the user specifies their own niche, then selectively transfer the replicable list.
Verification
account_full_raw.json,run_status.json,report_data.json,report.html, andreport.mdmust have consistent statuses.- Both HTML and Markdown should display the actual collected sample size, total homepage post count, coverage, and
capture_status. - On an error, check
capture.diagnostics.page: only record the title, path, login/challenge/service-error boolean flags, and video link count; do not save full page content or cookies. - Numbers must match
report_data.jsonitem by item; missing values display—/Unknown. - The HTML file must be openable offline; external text must be escaped. Failed cover images do not affect the main content.
- No horizontal overflow on the HTML page; the latest post titles must be readable. Posting frequency should show the last 12 months based on individual posts; only fill missing months with 0 if a full capture was performed.
- The Markdown report must have all nine sections, including data sources, evidence boundaries, a replicable list, and items that should not be blindly copied.
Configuration
DOUYIN_PROFILE_DIR: Dedicated login state directory; default is~/.douyin-benchmark/chrome-profile.DOUYIN_BROWSER_CHANNEL: Default ischrome; set tochromiumto install Playwright Chromium first.DOUYIN_HEADLESS=true: Explicitly enable headless mode; default is headed.DOUYIN_SCROLL_WAIT_MS: Minimum scroll interval, default 1600ms; do not use it for high-frequency scraping.