mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
4ef2cb8b1c
* enrich(toutiao): hot board + bug fixes (silent column drop, partial render) Per WAWQAQ "丰富现有 adapter" pivot — Phase 2 site #3. ## New command - `toutiao hot` (Strategy.PUBLIC, browser:false) — public homepage hot board via the toutiao.com hot-event/hot-board endpoint. No login required. Returns 8 stable columns (rank/id/title/query/hot_value/label/url/image). ## Bug fixes for `toutiao articles` - **Silent column drop fixed**: `parseToutiaoArticlesText` previously did `if (title && stats) push(...)`, silently dropping any row where the stats span hadn't finished rendering by the time page.innerText was read. Slow-render bugs were invisible — adapter looked "complete" while writers saw extra rows in the dashboard. Partial rows now surface with `null` stat columns. - **Silent clamp on `--page` removed**: out-of-range / non-integer values raise `ArgumentError` with explicit bounds [1, 4]. Same validation reused by both `articles` and `hot` via `parseArticlesPage` / `parseHotLimit` in `utils.js`. - **Empty result typed**: zero-row scrape now raises `EmptyResultError` instead of returning `[]` silently (would otherwise look like a legitimate "no articles" response). ## Refactor - Parser logic extracted to `clis/toutiao/utils.js` (alongside hot-row mapping, validators, and the hot-board URL constant). - `articles.js` switches from declarative `pipeline:` to imperative `func` form so `parseArticlesPage` validation can run before the navigation step (declarative pipeline can't pre-validate args). - Strategy is now explicit: `Strategy.COOKIE, browser: true` for articles (creator dashboard is logged-in only). ## hot field map `ClusterIdStr` (or numeric `ClusterId`) → id; `Title` → title; `QueryWord` → query (falls back to title); `HotValue` → hot_value (non-negative numeric, else null); `Label`, `Url`, `Image` → respective columns. `pickImage` walks `Image.url` → first truthy `Image.url_list[]`. Empty-title rows are dropped (returns null) before ranks are densely re-assigned 1..N. ## Tests 29 contract assertions across `parseArticlesPage` / `parseHotLimit` / `parseToutiaoArticlesText` / `mapHotRow` + registry-level shape checks + `hot` adapter func behaviour (typed errors / no silent clamp / fetch failure paths / dense-rank). ## Audits - typed-error-lint: 196 = 196 (unchanged baseline) - silent-column-drop: 103 = 103 (unchanged baseline) - listing-id-pairing: hot has `id` column (round-trippable when a detail command lands later); advisory list unchanged. ## Manifest 757 → 758 entries (+1 for `hot`). ## Doc - index.md: toutiao mode 🔐 → 🌐/🔐 (hot is public, articles is logged-in) - toutiao.md: per-command mode/domain table + column docs + prerequisites * fix(toutiao): tighten hot and articles contracts
2.3 KiB
2.3 KiB
Toutiao (今日头条)
Mode: 🌐 / 🔐
| Command | Mode | Domain | Description |
|---|---|---|---|
opencli toutiao articles |
🔐 Browser | mp.toutiao.com |
头条号创作者后台文章列表及数据(需登录) |
opencli toutiao hot |
🌐 Public | www.toutiao.com |
今日头条首页热榜(公开,无需登录) |
Usage Examples
# Public hot board (no login)
opencli toutiao hot
opencli toutiao hot --limit 10
opencli toutiao hot -f json
# Creator dashboard articles (logged-in)
opencli toutiao articles
opencli toutiao articles --page 2
opencli toutiao articles --page 1 -f json
Output
hot
| Column | Type | Notes |
|---|---|---|
rank |
int | 1-based, dense after empty-title rows are dropped |
group_id |
string | null | Topic/cluster id (ClusterIdStr, falls back to numeric ClusterId) |
title |
string | Trending topic title |
query |
string | Search keyword (QueryWord); falls back to title if absent |
hot_value |
int | null | HotValue parsed as non-negative number; null if missing |
label |
string | null | Hot tag (e.g. 热 / 新 / 沸) when present |
url |
string | null | Topic permalink |
image_url |
string | null | First non-empty image URL (Image.url → Image.url_list[*]) |
articles
title · date · status · 展现 · 阅读 · 点赞 · 评论
If a row's stats span has not finished rendering by the time the page text is read, the row still surfaces with null stat columns instead of being silently dropped — this masking previously hid creator-backend slow-render bugs.
Login/captcha pages abort with AuthRequiredError; browser render failures abort with CommandExecutionError.
Prerequisites
hot
- No login required. Uses the public
hot-event/hot-boardendpoint that powers the homepage hot panel.
articles
- Chrome running and logged into
mp.toutiao.com - Browser Bridge extension installed
Notes
hot--limitaccepts integers in[1, 50]; out-of-range values raise anArgumentError(no silent clamp).articles--pageaccepts integers in[1, 4], matching the contributor's verified range on the creator dashboard; out-of-range values raise anArgumentError.