Skill v3.0.0
currentAutomated scan96/100+25 new
name: js-xiaohongshu-ops-skill description: 小红书内容读取 skill,提供笔记详情、评论、搜索、用户主页等多 profile READ + 受控 INTERACTIVE 导航能力。 version: 3.0.0 metadata: openclaw: emoji: "\U0001F338" 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 治理。
浏览器兼容性与登录
- 要求 Node.js 22+、JS Eyes 2.8.5+、已连接的浏览器扩展与运行中的 Server。
- 页面 bridge 需要宿主设置
security.allowRawEval: true;JS Eyes 2.5+ 会同步到扩展,扩展侧显式false仍可强制关闭。 - Chrome 要求 135+;Chrome 138+ 还需开启浏览器控制的 Allow User Scripts。
- 笔记公开内容可匿名读取;评论 API、部分用户页和风控后的页面要求浏览器已有登录态。本 Skill 提供 CLI 登录引导,但不向 AI 暴露登录操作。
安全分级
- 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 子系统
# 初始化默认 confignode 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 图文# 同步跑一次完整 check(会发 webhook)node skills/js-xiaohongshu-ops-skill/index.js monitor check --pretty# 启动循环 daemonnode skills/js-xiaohongshu-ops-skill/index.js monitor daemon --interval 1800node 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# Doctor(4 profile 一站诊断)node skills/js-xiaohongshu-ops-skill/index.js doctor --pretty# 老路径 fallback(关闭 bridge)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# 选排序 + 内容类型(sortBy 合法值:综合/最新/最多点赞/最多评论/最多收藏)node skills/js-xiaohongshu-ops-skill/index.js search "美食" --sort-by 最多点赞 --content-type 图文 --time-range 一周内 --pretty# 串行点开前 3 条详情(同 tab + back)node skills/js-xiaohongshu-ops-skill/index.js search "美食" --limit 5 --extract-details --details-limit 3 --pretty# 额外采集搜索框下拉联想词(best-effort,+6.5s;xhs 当前多数返回空,看 suggestKeywordsAttempt.error)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):
# 引导登录:打开登录页 + 轮询 web_session cookie 出现(默认最多 5 分钟)node skills/js-xiaohongshu-ops-skill/index.js login --prettynode 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 --prettynode 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": [/* drain 出的 emit 事件 */],"eventsCount": 12,"traceFile": ".../visual/<runId>/trace.jsonl","recordDir": ".../visual/<runId>","framesEnabled": true}}
验收链路
# 1) HUD + flash 高亮node skills/js-xiaohongshu-ops-skill/index.js note "https://www.xiaohongshu.com/explore/<id>?xsec_token=..." \--visual --visual-hud --visual-flash --pretty# 2) trace JSONL(默认目录)node skills/js-xiaohongshu-ops-skill/index.js note "<url>" --visual --visual-trace --pretty# → tail -1 ~/.js-eyes/skill-data/.../visual/<runId>/trace.jsonl# 3) 完整录制(含 JPEG 帧序列)node skills/js-xiaohongshu-ops-skill/index.js note "<url>" --visual --visual-record --pretty# → ls ~/.js-eyes/skill-data/.../visual/<runId>/frames/# 4) doctor 看 4 profile bridge 都注入了 __jse_visualnode skills/js-xiaohongshu-ops-skill/index.js doctor --pretty# 5) 顶层结果字段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.mdpackage.jsonindex.js 委托 cli/index.jsskill.definition.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 工具参数 → URLruntimeConfig.js server/recording 配置bridgeAdapter.js noteViaBridge / commentsViaBridge / 等@js-eyes/client-sdk BrowserAutomationxhsUtils.js URL 规整 / 字段标准化 / sanitizebridges/common.js fetchXhsApi + parseNoteMeta + DOM helpers + detectAntiCrawlnote-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 优先;Xauto = GraphQL 优先。 - DESTRUCTIVE:xhs 永不引入;X 在 v3.1 拆 compose-bridge。
- 反爬:xhs 有 og:xhs:note_* 三件齐全判定 + 连续 3 次 risk hit 暂停 5 分钟;X 仅 429 暂停。
- 监控对象(v3.0+):xhs 同时支持「用户」与「关键词搜索」两类 target;X 只有 account。