| name | js-xiaohongshu-ops-skill |
| description | 小红书内容读取 skill,提供笔记详情、评论、搜索、用户主页等多 profile READ + 受控 INTERACTIVE 导航能力。 |
| version | 3.0.0 |
| metadata | {"openclaw":{"emoji":"🌸","homepage":"https://github.com/imjszhang/js-eyes","requires":{"bins":["node"]}}} |
js-xiaohongshu-ops-skill
面向小红书内容读取的 skill。v2.1 起按 skills/js-x-ops-skill 的 PAGE_PROFILES + Bridges + Session 架构重构,支持笔记 / 搜索 / 用户 / 首页四个 profile,并按 v2.1 → v3.x 阶段渐进交付:
- v2.1(当前):架构铺底,落
note-bridge 与 xhs_get_note / xhs_get_note_comments / xhs_session_state 三个 READ 工具。
- v2.2:搜索域(
search-bridge + xhs_search_notes + 4 个 INTERACTIVE 导航工具)。
- v2.3:用户域(
user-bridge + xhs_get_user / xhs_get_user_notes)。
- v3.0:监控子系统(accounts + searches 两类 target)。
- v3.x:限流 / 反爬 / visual-bridge-kit / cookie sanitize 治理。
安全分级
- READ:
xhs_get_note、xhs_get_note_comments、xhs_session_state(v2.1)。
- INTERACTIVE:
xhs_navigate_*(v2.2 起)。仅 location.assign,不模拟点击、不改 DOM。
- DESTRUCTIVE:永不引入。本 skill 不提供发笔记 / 评论 / 点赞 / 收藏 / 关注。
READ 调度(readMode)
xhs_get_note 默认 readMode='auto',与 X 取反:
auto:DOM 优先 → API 兜底(小红书 DOM 覆盖更广,feed JSON 不稳定)。
dom:仅 DOM 抽取。
api:仅 API(笔记详情 stub,主用于评论/调试)。
xhs_get_note_comments 默认 readMode='api'(基于 edith /api/sns/web/v2/comment/page,分页稳定)。
提供的 AI 工具(v3.0)
| 工具 | 类型 | 说明 |
|---|
xhs_get_note | READ | 笔记详情 + 可选评论 |
xhs_get_note_comments | READ | 评论分页(API 主路径) |
xhs_session_state | READ | 登录态读取(cookie a1/web_session + DOM 昵称) |
xhs_search_notes | READ | 搜索(频道 / 筛选 / 滚动;可 extractDetails 串行点开详情) |
xhs_get_user | READ | 用户主页资料(昵称、签名、关注/粉丝/获赞) |
xhs_get_user_notes | READ | 用户笔记列表(滚动分页) |
xhs_navigate_* | INTERACTIVE | navigate-note / search / user / home(仅 location.assign) |
xhs_monitor_list_targets | READ | 列出 monitor accounts + searches + channels |
xhs_monitor_get_status | READ | daemon pid + 各 target lastCheck/notesCount |
xhs_monitor_add_target | READ | 增加一个 user/search target(仅写 config,不发通知) |
xhs_monitor_remove_target | READ | 删除一个 user/search target |
xhs_monitor_test_target | READ | 单 target dry run(不写 state、不发通知) |
monitor 红线:monitor init / monitor check / monitor daemon / monitor stop 会触发 webhook,仅 CLI 暴露,不进 AI 工具列表。
Monitor 子系统
node skills/js-xiaohongshu-ops-skill/index.js monitor init
node skills/js-xiaohongshu-ops-skill/index.js monitor add user <userId>
node skills/js-xiaohongshu-ops-skill/index.js monitor add search "穿搭" --channel-type 图文
node skills/js-xiaohongshu-ops-skill/index.js monitor check --pretty
node skills/js-xiaohongshu-ops-skill/index.js monitor daemon --interval 1800
node skills/js-xiaohongshu-ops-skill/index.js monitor stop
node skills/js-xiaohongshu-ops-skill/index.js monitor status --pretty
支持 feishu / discord / generic_webhook / console 4 种通知渠道,schema 在 ~/.js-eyes/skill-data/js-xiaohongshu-ops-skill/monitor/config.json,可由 JS_XHS_MONITOR_HOME 覆盖。
| xhs_navigate_note | INTERACTIVE | 仅 location.assign 导航到笔记详情 |
| xhs_navigate_search | INTERACTIVE | 仅 location.assign 导航到搜索页 |
| xhs_navigate_user | INTERACTIVE | 仅 location.assign 导航到用户主页 |
| xhs_navigate_home | INTERACTIVE | 仅 location.assign 导航到探索流首页 |
CLI
node skills/js-xiaohongshu-ops-skill/index.js note "https://www.xiaohongshu.com/explore/xxxx" --with-comments --max-comment-pages 2 --pretty
node skills/js-xiaohongshu-ops-skill/index.js comments "https://www.xiaohongshu.com/explore/xxxx" --max-comment-pages 5 --pretty
node skills/js-xiaohongshu-ops-skill/index.js session-state --pretty
node skills/js-xiaohongshu-ops-skill/index.js doctor --pretty
JS_XHS_DISABLE_BRIDGE=1 node skills/js-xiaohongshu-ops-skill/index.js note "https://www.xiaohongshu.com/explore/xxxx"
xhs_search_notes(v3.9,注入 + 串行详情 + visual HUD + type / publishTime + 「大家都在搜」相关搜索词 + 联想词 best-effort)
UI 路径与 agent-js DeepSearchWorkflow/lib/mcp/tools/xhsSearch.js 对齐;xhs 实际是 Vue(不是 React),且 visual-bridge-kit 会装 HP overlay,所有 click 都需绕开 [data-hp-installed] 优选 [data-hp-bound] 真节点。
| 阶段 | 选择器 / 行为 |
|---|
| 频道 Tab | #channel-container 内优先 [data-hp-bound]、按文本(全部 / 图文 / 视频 / 用户)匹配;fallback 跳过 [data-hp-installed] 的 HP overlay |
| 打开筛选面板 | 点击 textContent==='筛选' 的 span → 等 .filters-wrapper 出现 |
| 筛选选项 | 在 .filters-wrapper > .filters 行(按 title 文本「排序依据/笔记类型/发布时间/搜索范围」定位)→ 在 .tag-container .tags[data-hp-bound] 中按选项文本严格匹配 → click → 等 [data-hp-bound].active 切到目标值(appliedFilters.<group>_activated=false 暴露未真切的情况) |
| 关闭面板 | document.body.click() + 等 .feeds-container .note-item 重出现 |
| 滚动收集 | .feeds-container section.note-item 去重(noteId) |
| 详情串行 | 当 extractDetails=true:找带 xsec_token= 的 sibling <a> 点开 → 等 #noteContainer 或 .note-container → 等 .engage-bar .like-wrapper(防 stats/img lazyload 漏抓)→ 内联抽 → 路由模式 history.back() / 模态点 .close-circle .close → 等列表重现 |
| Visual | CLI 默认开(与 js-x-ops-skill 对齐):bridge 在切频道 / 打开筛选面板 / 每个筛选项 / 每条详情(点开 → 等互动栏 → 抽完 → 回列表)共 ~22 次 HUD pending/success + 17 次元素 flash,覆盖整个 19s 串行详情流程,无中段空白;--no-visual 关闭;--visual-record 才落帧;--visual-trace 智能 eat(下个参数以 - 开头视作 truthy)。监控不传 visualConfig,自动 noop |
参数
node skills/js-xiaohongshu-ops-skill/index.js search "美食" --limit 10 --pretty
node skills/js-xiaohongshu-ops-skill/index.js search "美食" --sort-by 最多点赞 --content-type 图文 --time-range 一周内 --pretty
node skills/js-xiaohongshu-ops-skill/index.js search "美食" --limit 5 --extract-details --details-limit 3 --pretty
node skills/js-xiaohongshu-ops-skill/index.js search "美食" --limit 5 --collect-suggest --pretty
返回结构
notes[i]:卡片层除 noteId / url / title / author / likeCount / cover / xsec_token 外,新增 type: 'video' | 'normal'(按 a.cover .play-icon 判定)。
notes[i].detail:当 extractDetails=true 且本条点开成功时含 { ok:true, title, description, content, image_urls, stats, author, publishTime, publishLocation, ... };publishTime 形如 刚刚 / 3天前 / 昨天 12:31 / 04-22 / 编辑于 2024-12-01;publishLocation 形如 上海 / 福建 / 意大利(无则 null)。失败时 { ok:false, error: 'card_anchor_not_found' | 'no_note_container' | 'route_navigated' | 'click_failed' },不会中断主流程。
- 顶层
details: { requested, succeeded, failed }:详情统计;extractDetails=false 时为 null。
appliedFilters: { channelType, sortBy, contentType, timeRange, searchScope, *_error? }:UI 真实落到的值;某筛选项失败仅写 <group>_error,主流程继续。
filterPanelUsed:是否实际打开了筛选面板(无任何筛选项时 false)。
relatedSearchKeywords: string[]:「大家都在搜」相关搜索词(拍平、去重;穿插在 feed 里的 .query-note-wrapper 块中收集,深度调研用)。
relatedSearch: [{ header, words }]:按 header 分组的原始结构(同一 keyword 翻 30 条会出现 2 组,每组 4 个词)。
suggestKeywords: string[]:搜索框下拉联想词。需 --collect-suggest (collectSuggest:true) 显式开启,开启后会增加 ~6.5s 耗时。实测 xhs 当前对 programmatic input.click()/focus() 严格 gating(要求 event.isTrusted),模拟事件 + InputEvent 全套都拿不到 popup → 多数情况返回 []。仅当浏览器是真用户在前台键盘操作时才能成功。
suggestKeywordsAttempt: { keywords, hitSelector, durationMs, error }:联想词采集尝试的元数据,error: 'no_dropdown_visible' / 'no_input' 帮助调用方区分「试了拿不到」vs「根本没试」。
注意
extractDetails 开销显著,建议 detailsLimit ≤ 10;硬上限 20。
- 详情阶段若小红书弹窗实测被改成新 route(非模态),bridge 会自动
history.back 并把 detail.error: route_navigated 透出,不破坏主路径。
- monitor
searches[] 默认 extractDetails=false(长跑保守);可在 monitor config 的 search 项里显式 "extractDetails": true, "detailsLimit": 5 启用,monitor 内部会把 timeoutMs 自动从 240s 上调到 360s。
评论与登录态(v3.1)
小红书评论 API(/api/sns/web/v2/comment/page 等)必须有登录态,否则会回 code:300011 "Account abnormal"。skill 在两处做了门控:
- 入口预检:
runTool 在调用 xhs_get_note_comments 或 args.withComments===true 时,会先调一次 sessionState,若 cookieFlags.hasWebSession === false 则直接短路返回 error: 'login_required' + loginUrl: 'https://www.xiaohongshu.com/login',避免无谓打扰风控。
- bridge 内层兜底:
api_getComments 自身也会再校验一次,返回 login_required 与 loginUrl,便于 AI/上游识别。
CLI 提供 xhs login 引导子命令(仅 CLI,不暴露给 AI):
node skills/js-xiaohongshu-ops-skill/index.js login --pretty
node skills/js-xiaohongshu-ops-skill/index.js login --timeout-ms 600000 --pretty
成功后输出 { ok:true, cookieFlags:{ hasWebSession:true, ... }, elapsedMs },超时则 { ok:false, error:'login_timeout' }。
如何查看抓到的数据(v3.1)
每次 READ 工具跑完会把 history 行 append 到 ~/.js-eyes/skill-data/<skillId>/history/<YYYY-MM>.jsonl,cache 与 debug bundle 同目录其它子目录。runReadTool 会在 stderr 输出一行提示(除非加了 --quiet):
[xhs] records: /Users/x/.js-eyes/skill-data/.../history/2026-05.jsonl (notes=20)
result 顶层 run.paths.{historyFile,historyDir,cacheDir,debugDir} 也包含完整路径,便于上游脚本/AI 直接定位。
CLI 提供 xhs records 子命令快速查看最近的 records:
node skills/js-xiaohongshu-ops-skill/index.js records --last 5 --pretty
node skills/js-xiaohongshu-ops-skill/index.js records --tool xhs_get_note --last 10 --pretty
输出每条包含:run_id / tool / timestamp / status / duration_ms / cache_hit / input(摘要) / error / debugBundlePath。
runToolAudit 字段(v2.1)
每次 READ 工具返回顶层包含:
triedMethods – 尝试过的 bridge 方法序列(如 ['dom_getNote','api_getNote'])。
usedMethod – 最终用了哪个 bridge 方法。
readMode – 'dom' / 'api'。
requestedReadMode – 调用方传入的 readMode('auto' / 'dom' / 'api')。
fallback – 是否触发跨档位回退。
antiCrawlState – { paused, pauseUntil, consecutiveRiskHits },由 bridge 共享。
Visual(v3.1,硬依赖)
完整接入 @js-eyes/visual-bridge-kit(与 @skills/js-x-ops-skill 等价):bridge 内核装 window.__jse_visual,runTool 用 wrapCallApi + drainVisualEvents,工具粒度 hint 由 lib/visualHint.js 提供,可选写 trace JSONL + JPEG 帧序列 + visual session bundle 给 hyperframes 等下游消费。
CLI 旋钮
--visual / --no-visual:总开关。
--visual-hud / --no-visual-hud:右上角 HUD 卡片。
--visual-flash / --no-visual-flash:元素 flash overlay。
--visual-trace [path]:单文件 JSONL trace;不传 path 时落到默认 visualDir。
--visual-record [dir]:完整 session bundle(events.jsonl + meta.json + JPEG 帧序列)。
- 高级:
--visual-flash-ms / --visual-linger-ms / --visual-pinned-hold / --visual-error-pin / --visual-list-stride / --visual-prefix,详见 @js-eyes/visual-bridge-kit/node/visualConfig.js::DEFAULTS。
- deprecated:
--visual-mode(v0.6 拆成 hud/flash)、--visual-ms(v0.7 改名 --visual-flash-ms)。被 parseVisualFlags 检出后 stderr 一次性告警。
输出位置
- 默认 visualDir:
~/.js-eyes/skill-data/js-xiaohongshu-ops-skill/visual/<runId>/(与 history/cache/debug 同级)。
<visualDir>/trace.jsonl:所有 drain 出的 events 一行一条 JSON(type/ts/anchor/payload …)。
<visualDir>/events.jsonl + <visualDir>/meta.json:visual session bundle。
<visualDir>/frame-*.jpg:开 --visual-record 后才生成(与 events.jsonl/meta.json 同目录),每次 callApi 边界一帧(默认上限 60 帧、quality 82,hyperframes 默认在同目录找帧)。
跑完后 stderr 输出一行 [xhs] visual: <path> (events=N),与 records 路径并列。
顶层结果字段
{
"visual": {
"enabled": true,
"hint": { "kind": "item", "toolName": "xhs_get_note", "label": "笔记 · ...", "anchor": { "noteId": "..." } },
"events": [],
"eventsCount": 12,
"traceFile": ".../visual/<runId>/trace.jsonl",
"recordDir": ".../visual/<runId>",
"framesEnabled": true
}
}
验收链路
node skills/js-xiaohongshu-ops-skill/index.js note "https://www.xiaohongshu.com/explore/<id>?xsec_token=..." \
--visual --visual-hud --visual-flash --pretty
node skills/js-xiaohongshu-ops-skill/index.js note "<url>" --visual --visual-trace --pretty
node skills/js-xiaohongshu-ops-skill/index.js note "<url>" --visual --visual-record --pretty
node skills/js-xiaohongshu-ops-skill/index.js doctor --pretty
node skills/js-xiaohongshu-ops-skill/index.js note "<url>" --visual --json | python -c "import json,sys; r=json.load(sys.stdin); print('visual.events=', len(r['visual']['events']))"
站点 anchor resolver(v3.1.1)
bridges/_visual-xhs.js 通过 window.__jse_visual.setSiteAnchorResolver(fn) 把 hint.anchor 映射到具体 DOM:
| anchor | 当前页 | 优先 selector | fallback |
|---|
{ noteId } | 笔记详情页(URL 含同名 noteId) | #noteContainer / .note-container | .feeds-page |
{ noteId } | 搜索 / explore 列表 | a[href*='/explore/<noteId>'].closest('section.note-item') | 直接 <a> |
{ userId } | 用户主页(URL 含 /user/profile/<userId>) | .user-info-card / .user-info-wrapper / .user-page | — |
{ userId } | 列表页 | a[href*='/user/profile/<userId>'].closest('.author-wrapper') | 直接 <a> |
{ commentId } | 笔记详情页 | .comments-container / .comments-el(整片,commentId 不暴露在 DOM) | — |
resolver 命中时会多 emit 2 条 flash(pending) / flash(success) 事件(默认 hud-only 时只有 hud + before/after,共 4 条;resolver 命中后 6 条 + frame=7)。
与 hyperframes 联动
<visualDir> 即 @js-eyes/visual-replay-hyperframes 的 jse-replay 输入;anchorId 命名空间统一为 note:<noteId> / user:<userId> / comment:<commentId>,与 lib/visualHint.js 的 extractPayload 卡片字段对齐,离线 composition 时 .flash-active 能正确附到节点。
Recording
接入统一 skill recording 底座(@js-eyes/skill-recording):
- 跟随
js-eyes 全局配置中的 recording.mode。
- CLI 可覆盖:
--recording-mode off|history|standard|debug
--debug-recording
--no-cache
--recording-base-dir /absolute/path
--run-id custom-id
v3.x:a1 / web_session / webId 等敏感 cookie 在写入 history / debug 落盘前强制 mask(lib/xhsUtils.js::sanitizeForRecording)。
架构
skills/js-xiaohongshu-ops-skill/
SKILL.md
package.json
index.js 委托 cli/index.js
skill.contract.js 工厂化 + AI 工具表
cli/index.js dispatcher(读 lib/commands.js)
lib/
api.js 编程 API(默认 useBridge)
session.js 主调度器(PAGE_PROFILES + bridge 注入)
runTool.js READ 主管道(readMode + audit + history)
config.js PAGE_PROFILES(note/search/user/home)
commands.js CLI 声明式映射
toolTargets.js 工具参数 → URL
runtimeConfig.js server/recording 配置
bridgeAdapter.js noteViaBridge / commentsViaBridge / 等
js-eyes-client.js BrowserAutomation
xhsUtils.js URL 规整 / 字段标准化 / sanitize
bridges/
common.js fetchXhsApi + parseNoteMeta + DOM helpers + detectAntiCrawl
note-bridge.js VERSION='0.1.0' getNote/getComments + 五件套
scripts/
xhs-note.js fallback(JS_XHS_DISABLE_BRIDGE=1 时使用)
tests/ node --test
与 X / Reddit 主线的差异
- readMode 默认:xhs
auto = DOM 优先;X auto = GraphQL 优先。
- DESTRUCTIVE:xhs 永不引入;X 在 v3.1 拆 compose-bridge。
- 反爬:xhs 有 og:xhs:note_* 三件齐全判定 + 连续 3 次 risk hit 暂停 5 分钟;X 仅 429 暂停。
- 监控对象(v3.0+):xhs 同时支持「用户」与「关键词搜索」两类 target;X 只有 account。