Token导航 LogoToken导航TokenDH.com
研究检索执行命令clawhub未标认证来源可访问clear审计通过

xhs-downloadxhs 下载

Agent Skill

xhs-download 用于查找、检索和筛选相关信息,适合在 OpenClaw 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

2,046

周安装

87

GitHub Stars

公开资料未说明

下载量

717
OpenClaw

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT-0

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:xhs-download(xhs 下载)
来源仓库:https://github.com/weilixiong/xhs-download
安装命令:
openclaw skills install xhs-download
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install xhs-download

简介

小红书笔记批量下载。通过已登录 Chrome 的 DevTools Protocol 自动化下载小红书笔记(图片+文字)到本地。

SKILL.md

name
xhs-download
description
小红书笔记批量下载。通过已登录 Chrome 的 DevTools Protocol 自动化下载小红书笔记(图片+文字)到本地。

小红书笔记批量下载

通过 Chrome DevTools Protocol (CDP) 批量下载小红书笔记到本地文件夹。

前置条件

  1. Chrome 已登录小红书(任何方式启动均可)
  2. Chrome 开启远程调试:启动时加 --remote-debugging-port=9222
  3. Python3 环境pip3 install websocket-client requests
如果 Chrome 已启动但没开调试端口,关闭后重启即可。

使用方法(4步)

第1步:找到目标账号的 profile_id

打开小红书网页版,进入目标账号主页,复制 URL 最后一段:

https://www.xiaohongshu.com/user/profile/64902d2d000000001c0294eb
                                                 ↑ 这个就是 profile_id

第2步:获取 Chrome tab_id

在终端运行:

curl -s http://127.0.0.1:9222/json | python3 -m json.tool | grep -E '"id"|"url"'

找到包含 xiaohongshu.com 的那条记录,复制其 id 值。

第3步:修改脚本配置

将下方脚本开头的三个变量改成你的值:

变量填什么示例
PROFILE_ID第1步获取的账号ID"64902d2d000000001c0294eb"
TAB_ID第2步获取的tab ID"4C23291E2F8B1524..."
SAVE_DIR你想保存到的文件夹"~/Downloads/我的笔记/"

第4步:运行脚本

把脚本保存为 download.py,然后运行:

python3 download.py

完整脚本

import json, time, requests, os, subprocess, websocket, re

# ===== 改这里 =====
PROFILE_ID = "你的目标账号ID"
TAB_ID = "你的Chrome tab_id"
SAVE_DIR = "~/Downloads/你的文件夹/"
# =================

SAVE_DIR = os.path.expanduser(SAVE_DIR)
os.makedirs(SAVE_DIR, exist_ok=True)

def send(ws, method, params={}):
    """CDP 命令发送。3个参数:ws连接对象, 方法名, 参数字典"""
    msg_id = int(time.time()*1000) % 100000
    msg = {"id": msg_id, "method": method, "params": params}
    ws.send(json.dumps(msg))
    while True:
        resp = json.loads(ws.recv())
        if resp.get("id") == msg_id:
            return resp

def download_image(url, path):
    """下载图片,必须带 Referer"""
    r = requests.get(url, headers={"Referer": "https://www.xiaohongshu.com/"}, timeout=30)
    if len(r.content) > 100:
        with open(path, 'wb') as f:
            f.write(r.content)
        return True
    return False

# 1. 连接 Chrome
ws = websocket.create_connection(
    f"ws://127.0.0.1:9222/devtools/page/{TAB_ID}", timeout=30)
print(f"已连接 Chrome tab: {TAB_ID}")

# 2. 导航到目标主页
url = f"https://www.xiaohongshu.com/user/profile/{PROFILE_ID}"
send(ws, "Page.navigate", {"url": url})
time.sleep(6)
print(f"已导航到: {url}")

# 3. 滚动加载所有笔记(30次,覆盖绝大多数账号)
print("滚动加载中...")
for i in range(30):
    send(ws, "Input.synthesizeScrollGesture", {
        "x": 500, "y": 600,
        "xDistance": 0, "yDistance": -800,
        "speed": 2000
    })
    time.sleep(2)
    if (i + 1) % 10 == 0:
        print(f"  已滚动 {i+1}/30 次")

# 4. 从 DOM 提取笔记列表
result = send(ws, "Runtime.evaluate", {"expression": """
(() => {
    const cards = document.querySelectorAll(".feeds-container .note-item");
    return JSON.stringify(Array.from(cards).map(c => {
        const a = c.querySelector("a[href*='/explore/']");
        return {
            href: a ? a.getAttribute("href") : "",
            title: a ? a.innerText.trim().substring(0, 60) : ""
        };
    }));
})()
"""})
notes = json.loads(result["result"]["result"]["value"])
print(f"\
找到 {len(notes)} 篇笔记\
")

# 5. 从 __INITIAL_STATE__ 获取所有笔记的 xsecToken
state_result = send(ws, "Runtime.evaluate", {"expression": """
(() => {
    const state = window.__INITIAL_STATE__ || {};
    const user = state.user || {};
    const notes = user.notes || {};
    const items = (notes._value && notes._value.items) ? notes._value.items : [];
    return JSON.stringify(items.map(n => ({
        id: (n.note && n.note.id) || '',
        xsecToken: (n.note && n.note.xsecToken) || ''
    })));
})()
"""})
token_map = {}
for item in json.loads(state_result["result"]["result"]["value"]):
    if item["id"]:
        token_map[item["id"]] = item["xsecToken"]

# 6. 逐篇下载
downloaded = 0
skipped = 0
for note in notes:
    href = note.get("href", "")
    title = note.get("title", "").strip()
    if not href or not title:
        continue

    note_dir = os.path.join(SAVE_DIR, title)
    if os.path.exists(note_dir):
        print(f"⏭ 跳过(已存在): {title[:30]}")
        skipped += 1
        continue

    os.makedirs(note_dir, exist_ok=True)

    # 提取 note_id
    m = re.search(r'/explore/([a-f0-9]+)', href)
    note_id = m.group(1) if m else ""
    if not note_id:
        continue

    xsec_token = token_map.get(note_id, "")

    # 导航到详情页
    if xsec_token:
        detail_url = f"https://www.xiaohongshu.com/explore/{note_id}?xsec_token={xsec_token}"
    else:
        detail_url = f"https://www.xiaohongshu.com/explore/{note_id}"
    send(ws, "Page.navigate", {"url": detail_url})
    time.sleep(4)

    # 提取图片列表
    img_result = send(ws, "Runtime.evaluate", {"expression": """
    (() => {
        const swiper = document.querySelector('.swiper-wrapper');
        const imgs = swiper ? swiper.querySelectorAll('img') : [];
        return JSON.stringify(Array.from(imgs).map((img, i) => ({
            i, src: img.src
        })));
    })()
    """})
    images = json.loads(img_result["result"]["result"]["value"])

    # 下载图片
    img_count = 0
    for img in images:
        src = img.get("src", "")
        if src:
            path = os.path.join(note_dir, f"{img['i']+1}.jpg")
            if download_image(src, path):
                img_count += 1

    # 提取正文
    text_result = send(ws, "Runtime.evaluate", {"expression": """
    (() => {
        const desc = document.querySelector('.note-content .desc');
        return desc ? desc.innerText : document.title || '';
    })()
    """})
    text = text_result["result"]["result"].get("value", "")
    with open(os.path.join(note_dir, "内容.txt"), "w", encoding="utf-8") as f:
        f.write(text)

    downloaded += 1
    print(f"✅ {title[:30]}... ({img_count}张图)")

ws.close()
print(f"\
{'='*40}")
print(f"完成!下载 {downloaded} 篇,跳过 {skipped} 篇")
print(f"保存位置: {SAVE_DIR}")

输出格式

每篇笔记一个文件夹:

~/Downloads/你的文件夹/
├── 笔记标题1/
│   ├── 内容.txt
│   ├── 1.jpg
│   ├── 2.jpg
│   └── 3.jpg
└── 笔记标题2/
    ├── 内容.txt
    └── 1.jpg

常见问题

问题原因解决
Connection refusedChrome 没开调试端口--remote-debugging-port=9222 重启
图片下载失败/403缺少 Referer脚本已自带,别删 headers
笔记数量比预期少滚动次数不够把 30 改成 50
send() 不返回参数不对必须是 3 个参数:send(ws, method, params={})
websocket 模块找不到没装依赖pip3 install websocket-client

注意事项

  • 脚本会自动跳过已下载的笔记(文件夹已存在)
  • 每次导航详情页需要 4 秒等待加载
  • 图片下载必须带 Referer 头,否则 403
  • 大批量下载建议分段运行(避免被限流)

适合场景

01

OpenClaw 用户查找和安装 Skill 时

02

用户想查找某类 Agent Skill 时

03

需要根据任务场景推荐可安装能力包时

04

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenClaw

83.99%
按下载量换算602

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

执行命令

安装流程涉及命令执行,可能通过 openclaw skills install xhs-download 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills