下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/org-95lzstri--shortvideo-batch-downloader.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @org-95lzstri/shortvideo-batch-downloader。按账号批量下载抖音/快手公开账号的全部短视频,输出视频文件 + 1:1 元数据 + metadata.csv 清单。需本机 Chrome 装 Playwright 扩展并已登录目标平台。¥0.20/次,微信支付,首次使用需为 AI 专属卡小额充值(可转回零钱)。
阅读全文 (展开查看完整说明)
概述
本技能只有一种启动方式:用户发送 「执行」 二字即开始引导。任何自然语言的"帮我下载/整理一下"都不直接执行,先请用户发送「执行」。
收到「执行」后,第一件事是走下面的「〇-1、付费前置流程」,不是问平台也不是问账号。
顺序是:先确认宿主支持微信 AI 支付(P0)→ 报价并等确认(P1)→ 下单(P2)→ 支付(P3)→ 查单(P4)→ 用同一条请求重试执行(P5)。
确认支付成功后才进入「执行流程」,且执行必须使用 P5 重放出来的原请求 body。
〇-1、付费前置流程(启动后第一件事)
定价
| 项 | 内容 |
|---|---|
| 单价 | ¥0.20 / 次 |
| 计费单位 | 一个账号的一次完整交付(采集列表 + 下载视频 + 生成元数据清单) |
| 支付方式 | 微信支付「AI 专属卡」(Native 扫码) |
| 服务提供方 | 南京爱优服商业管理有限公司 |
| 退款 | 未开始采集:全额退;已产出文件:不支持退 |
计费口径(必须向用户讲清,否则会引起纠纷)
- 一次办理 = 一笔订单。同一个账号的补抓、失败重试、补写元数据、重跑下载脚本,全部属于这同一笔订单,不重复收费。
- 换一个账号,或者用户重新发起一次新的交付,才需要重新下单付费。
- 下载量不另计费:一个账号 20 条还是 2000 条,都是这一笔 ¥0.20。
首次使用提示(必须说,否则用户在支付页会流失)
本技能按次收费 ¥0.20,通过微信「AI 专属卡」支付。若卡内余额为 0,需先小额充值;
充值余额可随时转回微信零钱。这笔钱是单次服务费,不是押金、不是会员费、不是预付费套餐。
P0 · 支付能力前置检查(比报价更靠前,不做这一步等于白忙)
本技能通过微信 AI 支付收款,付款只能由智能体宿主弹出的支付卡片完成。下单前必须先确认当前智能体具备这项能力。
- 判断依据只看你自己的可用工具列表:是否存在名称包含
weixinpay_的工具
(例如 mcp__weixinpay__weixinpay_register,带前缀也算;存在任意一个即视为支持)。本技能文档里出现的工具名不算证据。
- 支持 → 继续 P1。
先预热支付服务(P0 通过后立即执行一次):
"$VENV_PY" scripts/pay_client.py warmup支付服务无流量时会缩容到 0,冷启动实测需要 1~2 分钟。提前预热能把这段等待
藏在用户阅读价格、确认订单的时间里。返回
"awake": false表示容器仍在启动,属正常现象,不要因此中断流程,继续 P1。
- 不支持 → 立即终止:不得创建订单、不得让用户去别处扫码、不得"先干活后补款"。
向用户说明结论与解决办法(不必解释你如何检查,也不要罗列具体工具名):
- 合作智能体(如 WorkBuddy):请升级智能体到最新版本后重试;
- 开放智能体(如 OpenClaw / DeepSeek Harness):按 https://skillhub.cn/install/skillhub.md 安装
@tencent-adm/weixinpay-installer; - 无法判断类型:升级智能体或安装微信 AI 支付插件后重试。
- 无法确认时按"不支持"处理。宁可让用户先升级,也不要走到"收了钱却弹不出卡片"那一步。
注意:本技能还有另一项前置依赖(Playwright 浏览器扩展 + 已登录目标平台),那属于业务前置检查,在「执行流程」第 0 步做。
两者都要过,但支付能力检查必须排在最前面——不然用户付了钱却卡在跑不通的环境上,退款比拒单贵得多。
支付流程(P0 → P5,顺序不可颠倒)
| 步 | 动作 | 要点 |
|---|---|---|
| P0 | 支付能力前置检查 | 可用工具列表里须存在名称含 weixinpay_ 的工具;不存在则终止且不得下单 |
| P1 | 价格披露与用户确认 | 明示 ¥0.20/次、收款主体、退款规则、首次充值提示,等用户明确确认 |
| P2 | 创建订单并固化原请求 | "$VENV_PY" scripts/pay_client.py create --body-file <原请求.json> |
| P3 | 拉起支付 | 把返回的 WeixinPay-Required 值原样交给系统,由系统弹出支付卡片 |
| P4 | 校验支付结果 | "$VENV_PY" scripts/pay_client.py status → 状态为 PAID 才继续 |
| P5 | 重试原请求(body 不变) | "$VENV_PY" scripts/pay_client.py replay → 取回与下单时一致的原请求 body,仅随请求携带支付信息后重发执行 |
P5 · 支付后重试原请求(body 不变,只加支付信息)
这是本技能唯一的交付入口,也是最容易做错的一步:支付前后必须是同一条请求,不是两套流程。
- P2 之前先把业务参数固化成「原请求」,一次采集任务 = 一个 body:
{"platform":"douyin","account":"账号A","mode":"all","out_dir":"D:/videos/账号A","limit":0}create --body-file 会把这份 body 的内容与 sha256 随订单一起留存在本机。之后业务参数只从这份 body 里读,不再重新拼。
- P4 拿到
PAID后,用replay取回原请求:
"$VENV_PY" scripts/pay_client.py replay返回结构:
{
"request_body": { "...与下单时逐字节一致..." },
"payment": { "out_trade_no": "sv...", "entitlement_token": "ent_...", "paid_at": 1792... },
"replay_ok": true
}- 重发同一条请求:把
request_body原样作为本次执行参数(平台、账号、模式、输出目录一个都不改),payment作为"本单已付费"的凭据随请求带上,然后进入「执行流程」。
- ✅ 正确:body 不变 + 携带支付信息重试原请求。同一次交付不重复收费,也不会因参数漂移跑错账号。
- ❌ 禁止:付款后另起一个脚本、用一份新拼的参数对象(哪怕"看起来一样")。那不是同一条请求,参数一旦漂移就出错,也无法证明这次执行对应哪笔订单。
- 幂等:同一个
out_trade_no重放任意次都不再加收费用。 - 若
replay返回request_body_changed:说明下单后业务字段被改过(换账号 / 换平台 / 换输出目录),这属于新事务,必须重新下单,不能拿旧订单的 token 跑新参数。
防丢单配套:
create会在用户本机写下~/.workbuddy/pay_orders/shortvideo-batch-downloader.json(只留订单号、金额与原请求 body 的 sha256,不留支付凭据),
status/replay省略单号时自动读取。服务端还有第二层保险:查单时若本地库查不到这笔订单,接口会回源微信支付核对真实交易状态,
trade_state=SUCCESS就当场重建已支付记录并返回entitlement_token。判断标准很简单:
status返回PAID就继续,返回PENDING就让用户稍候,接口报 404 说明微信侧也没有这笔交易——此时不要收钱办事,也不要让用户重复支付,先核对订单号。
支付红线(7 条,违反任何一条都算交付事故)
- 未确认
PAID前,不得执行任何业务动作:不得attach浏览器、不得调用任何采集脚本、不得下载任何视频、不得创建输出目录、不得写feed_index.jsonl。 - P0 未通过不得下单。宿主不具备微信 AI 支付能力时,不能创建订单、不能让用户另找渠道付款、不能"先干活后补款"。
- 不得展示
WeixinPay-Required的原值,只能原样交给系统触发支付卡片;也不得写入日志或文件。 - 不得编造支付状态。查单必须调接口,以返回的
status为准;接口报错就如实说查不到,不要假设"应该付好了"。 - 支付后必须重试原请求:执行时用的业务参数必须来自 P5 重放的
request_body,不得改动平台 / 账号 / 模式 / 输出目录。要改就是新事务,必须重新下单,不能拿旧订单的entitlement_token跑新参数。 - 支付失败/超时/用户取消:引导用户重新
create一笔新订单,不得复用上一笔已失效的payment_code。 - 不得向用户索要支付密码、短信验证码、银行卡号等任何支付凭证信息。
支付成功但跑不起来怎么办
这是本技能最常见的售后场景,先排错再谈退款:
- 未装 Playwright 浏览器扩展 / 未登录 / 未停在账号主页 → 按「执行流程」第 0 步补齐后重试,同一笔订单内不重复收费。
- 反爬账号少量缺条目(如 572/602)→ 属已知限制,用已有条目交付,不退款。
- 确实无法交付(如目标账号已注销、全部视频私密)→ 凭订单号联系服务方,由运营方在微信支付商户平台发起退款。
安全约束与数据流向
- 唯一联外网的支付环节:
scripts/pay_client.py只在 P1/P3 向下单、查单接口发送订单号与金额,不发送任何业务数据。 - 业务数据全部在本机:接管的是用户自己的浏览器、下载的是平台 CDN 直链、视频与元数据只写进用户指定目录,不上传任何服务器。
- 不接触账号密码:技能不读取、不存储用户的平台账号与密码;登录态由浏览器自身维持,技能只接管已登录的页面会话。产物目录里也不写 Cookie。
- 本机留存文件:
~/.workbuddy/pay_orders/shortvideo-batch-downloader.json,内容仅out_trade_no/amount_fen/created_at。
已知限制(如实告知,不要夸大能力)
- 必须本机装 Chrome + Playwright 浏览器扩展,两者缺一不可(扩展负责握手,
playwright-cli.js负责驱动)。Chrome ≥ 136 无法用 CDP 直连,只能走扩展方案。 - 必须已登录目标平台,且浏览器当前标签页停在目标账号主页。未登录时采集会返回空或触发风控。
- 需要本机有 Node:
playwright-cli.js由 Node 运行(技能自带pwcli.py自动探测路径,找不到会明确报错)。 - 抖音产物带水印:平台对第三方只下发带水印直链,
download_addr通常为空。快手通常无水印。 - 反爬严格的账号可能少量缺条目:实测某账号目标 602 条、实收 572 条(约 5%),属平台分页限流,不保证 100% 全量。
- 直链有时效:采集完成后应尽快下载,隔太久需要重新采集。
- 仅支持公开账号的公开视频:私密账号、付费内容、直播回放、图文作品不在能力范围内。
交互铁律(加载后必须遵守)
- 付费在前,且顺序固定:先做 P0 支付能力检查(宿主不支持就终止,不下单)→ 报价格与计费口径 → 下单 → 支付 → 查单 → 用 P5 重放的原请求 body 重试执行。任何业务动作(attach 浏览器 / 采集 / 下载 / 建输出目录)都必须在确认
PAID之后;未拿到PAID前只允许做三件事:检查支付能力、报价格、下单。执行阶段不得重新拼参数,一律用request_body里的原值。 - 顺序不可颠倒、一步一问:严格按下述「执行流程」逐步推进,每步只确认一件事,禁止跳步、禁止提前问后续参数、禁止合并提问。先确认前置(CLI 路径 / Python 路径 / 浏览器状态),才能问平台与账号;先问平台,才能问账号标识;先问账号,才能问方法参数。
- 全程选项卡(AskUserQuestion):凡需用户提供信息或做选择,一律用交互式选项卡呈现(2~4 选项 + 输入框),文本类(CLI 路径、sec_user_id、userId、输出目录等)给「在下方输入框填写」选项。严禁用"请把 X 发我 / 把链接贴给我"这类自然语言索要。用户答"还没准备好"时,重展该步卡片后再弹同一张。
- 全面脱敏:技能包内不得出现真实邮箱、手机号、授权码、密钥、真实姓名/公司名、含真实用户名的路径(统一用
C:/Users/用户名/...占位);也不得出现任何真实社交账号昵称 / ID(示例一律用账号A~H + 占位符)。CLI / Python 路径只经执行流程第 0 步由用户填写或自动探测,绝不写死。 - 能力对齐:技能描述与底层脚本/MCP 真实能力一致。本包 Python 脚本依赖
requests(已含)与 Playwright CLI(PW_CLI,可自动探测);脚本不对平台签名做纯 Python 复现,一律接管已登录浏览器去签。
执行流程(严格按顺序,全程选项卡)
第 0 步 · 前置条件确认(必须先做)
弹卡片,分三项确认 + 两项填写(均给「在下方输入框填写」):
- 确认清单(多选/逐张确认):① 本机 Chrome 已安装 Playwright 浏览器扩展;② 已登录目标平台;③ 浏览器当前标签页停在目标账号主页(抖音
.../user/<sec_user_id>,快手.../profile/<userId>)。 - PW_CLI:Playwright CLI 的
playwright-cli.js绝对路径。首选自动探测(技能自带scripts/pwcli.py,会依次查环境变量PW_CLI、WorkBuddy 托管 Node 工作区、npm 全局、npx 缓存)。探测到就直接用,不要盲目npm install -g @playwright/cli(很慢且常常本机早已存在)。 - VENV_PY:运行脚本的 Python 可执行路径,默认指向 WorkBuddy 托管 venv(示例
C:/Users/用户名/.workbuddy/binaries/python/envs/default/Scripts/python.exe),需含requests。
AI 在后续每条 Bash 命令前都要先
export PW_CLI="<探测或用户填的路径>",并用"$VENV_PY"调用脚本。未拿到这两项前,绝不执行采集。
第 1 步 · 选择平台
卡片:抖音 / 快手(二选一)。若用户不确定,按主页 URL 判断(douyin.com vs kuaishou.com)。
第 2 步 · 账号标识
卡片 + 输入框:
- 抖音:请填写主页 URL 里的 sec_user_id(「在下方输入框填写」)。
- 快手:请填写主页 URL 里的 userId(或完整主页链接,脚本会自动解析)。
第 3 步 · 方法与参数
- 抖音:默认方法一(稳健版,本版已修复标签页漂移等问题);仅当账号"一导航就关标签页"时用方法二。参数卡片:输出目录(默认
./videos_douyin_<账号简称>,可自定义)、是否限制数量。 - 快手:无需选方法——先方法 A(GraphQL),遇
result:50自动转方法 B(脚本与流程都会自动处理,不必让用户预判)。方法 A 需 Cookie(第 0 步浏览器已登录,运行时用state-save导出)。参数卡片:同上输出目录、是否限制数量。 - 两个平台共同提醒:抖音第三方直链带水印、快手无水印;直链有有效期,采集后应尽快下载。
第 4 步 · 自动执行
- 抖音方法一:一条命令跑完(脚本内部已处理 attach + run-code 同进程 + 重试):
"$VENV_PY" scripts/douyin_collect.py --sec <sec_user_id> --out ./videos_douyin_xxx随后单独一条命令下载(走 HTTP,不依赖浏览器会话):
"$VENV_PY" scripts/douyin_feed_download.py --indir ./videos_douyin_xxx --workers 6- 抖音方法二(反爬关标签页账号):
runjs.py+douyin_capture_single.js(见技术手册)。 - 快手:先方法 A(
kuaishou_batch_download.py),遇result:50转方法 B(kuaishou_feed_collect.py+kuaishou_feed_download.py)。 - 务必:
attach与run-code/采集必须放在同一条命令(同一进程)内;下载脚本走 HTTP 不依赖浏览器会话,可单独跑。逐条打印进度,遇错按「排错速查」处理。
第 5 步 · 汇报
告知:输出目录、采集条数、下载成功/失败数、产物清单(mp4 + .meta.json + metadata.csv/jsonl)。提醒水印差异与直链时效。
技术参考手册(执行时按需查阅)
平台选择
| 平台 | 主页特征 | 核心难点 | 解决方案 |
|---|---|---|---|
| 抖音 | https://www.douyin.com/user/<sec_user_id> | a_bogus 签名(wasm 生成,纯 Python 复现不了) | 接管已登录浏览器,滚动列表容器触发抖音原生签名翻页,用响应监听器抓包 |
| 快手 | https://www.kuaishou.com/profile/<userId> | GraphQL 需登录 Cookie;部分账号被限 result:50 | 方法A 带 Cookie 走 GraphQL;result:50 转方法B 浏览器原生 feed 接口 + 实时令牌兜底 |
核心方法论(两平台通用)
1. 准备:本机 Chrome 装好 Playwright 浏览器扩展;登录目标平台;打开目标账号主页并停在该页。
2. 接管:node "$PW_CLI" -s=<sess> attach --extension chrome
3. 采集列表:用对应平台的 collect 脚本,从浏览器原生接口拿到 feed_index.jsonl(含直链 + 元数据)
4. 下载:用对应平台的 download 脚本,按 feed_index.jsonl 逐条下视频 + 1:1 写元数据不要试图用纯 Python 复现平台签名(a_bogus / kww+hxfalcon)——一律让已登录浏览器去签。
★ Playwright 接管稳健化手册(本项目实测沉淀,务必逐条遵守)
这一节是"能不能跑通"的关键。踩过的坑全部在此,按此执行可避免"接管后过一会就断连"类顽疾。
- PW_CLI 先找现成的,别盲目重下。
playwright-cli.js是接管浏览器的 Node 端驱动,与 Chrome 里的「Playwright 浏览器扩展」是两码事、缺一不可。很多机器装扩展时已用npx @playwright/cli跑过,缓存里就有;WorkBuddy 托管 Node 工作区通常也已装。用scripts/pwcli.py自动探测即可,npm install -g往往又慢又没必要。 - 会话绑定「单条命令 / 单进程」生命周期。
attach与run-code必须写在同一条 Bash 命令(同一进程树)里;分两次命令跑,会话在中间就死了,报Browser 'xxx' is not open。下载脚本走 HTTP(带 Cookie/Referer),不依赖浏览器会话,可单独跑。 - attach 会抓错标签页 → 必须主动挑标签。
attach --extension chrome有时抓到目标页,有时却抓到扩展自己的chrome-extension://.../connect.html(连接页)。抓到连接页时,该页握手完即自动关闭 → 会话死亡 → 报Target page, context or browser has been closed。
修复(已写进脚本):在 run-code 内遍历 page.context().pages(),按 URL 匹配目标主页标签;找不到就 ctx.newPage() 新建标签并 goto 目标主页(Cookie 是浏览器级共享,新标签同样是登录态)。本项目的 douyin_collect.js / kuaishou 采集脚本均已内置"找标签 + 新建兜底"逻辑。
- 外层带重试循环。每轮换一个新会话名,最多 4 次;某轮失败(attach 超时 / 会话死亡 / 抓到连接页)就换名重来。偶然的一次失败不影响最终跑通(本版
douyin_collect.py内置)。 - 绝对不要强杀长任务。强行 kill 正在跑的长任务会连带杀掉 CLI 的守护进程(daemon),Chrome 里的扩展当时正连着那个 daemon,此后新
attach会一直等扩展握手 → 挂起超时,且不重连。若已发生:重启 Chrome 让扩展重载即可恢复;或清理残留 node 进程后再试。长任务请让它自然结束,或改短/加超时。 - Chrome ≥ 136 默认配置禁用远程调试端口,
--remote-debugging-port配默认用户目录不生效 → CDP 直连方案不可用,只能走浏览器扩展方案。(可用chrome://version查版本。) - 滚动容器不是 window。抖音/快手列表都在某个
overflow:auto的 div 里(抖音该容器 class 含route-scroll-conta)。用「全局扫描所有 div,取可滚动高度最大的那个再scrollTop=scrollHeight」最稳;window.scrollTo无效。 - 第 1 页请求发生在接管之前,监听器赶不上。解决:接管后在 run-code 内重新加载主页一次(已有标签用
reload,新建标签用goto),让第 1 页请求重新发出,由挂在 Page 对象上的 CDP response 监听器捕获(监听器挂在 Page 上,导航/翻页都不会失效)。 - 下载器务必幂等。下载脚本对"已存在且 >1KB 的 mp4"直接跳过、"有 mp4 但缺 .meta.json"自动补写。因此失败后直接重跑同一条下载命令即可补漏,不必清目录重下。
- 授权页弹不出 Allow(connect.html 底部空) → 多半是 Playwright 扩展待更新。解法:重启 Chrome 完成扩展更新,授权页即正常;或用
export PLAYWRIGHT_MCP_EXTENSION_TOKEN=<页面显示的token>后 attach 绕过(token 随扩展重载变化,仅当次有效)。别硬猜 token。
平台一:抖音(Douyin)
接口与签名
- 视频列表:
GET https://www.douyin.com/aweme/v1/web/aweme/post/?sec_user_id=<SEC>&max_cursor=0&count=18&... - 带双重反爬签名:
a_bogus(安全 SDK 的 wasm 生成)+x-secsdk-web-signature。纯 Python 无法复现 a_bogus。 - 解决:接管已登录浏览器,滚动视频容器触发抖音自带签名的下一页加载,用响应监听器捕获。
脚本清单(在 scripts/ 下)
pwcli.py PW_CLI 自动定位器(环境变量→WorkBuddy工作区→npm全局→npx缓存)
douyin_collect.py 抖音·方法一编排(稳健版):找标签/新建标签 + 重试 + 落盘 feed_index.jsonl
douyin_collect.js 抖音·方法一采集片段(__SEC__ 占位,编排器自动替换)
douyin_capture_single.js 抖音·方法二(反爬关标签页账号专用,单 run-code 内完成)
runjs.py 通用助手:attach 会话 → 跑 *.js → 解析 Result → 落盘 jsonl
douyin_feed_download.py 抖音·下载器(读 feed_index.jsonl 逐条下视频 + 1:1 元数据)方法一:普通账号(稳健版,默认)
# 1) 采集列表(--sec 填主页 URL 里的 sec_user_id;Chrome 已登录并停在抖音主页)
"$VENV_PY" scripts/douyin_collect.py --sec <sec_user_id> --out ./videos_douyin_xxx
# 2) 下载
"$VENV_PY" scripts/douyin_feed_download.py --indir ./videos_douyin_xxx --workers 6
# 可选:--limit N 只下前 N 个;--resume 跳过已存在 mp4 只补元数据;失败直接重跑即可补漏- 采集脚本内部:attach + run-code 同进程、4 次重试、找标签/新建标签兜底、CDP 监听 + 重载抓第 1 页、全局扫描滚动容器。
- 打印
唯一视频 N 条 (lastHasMore=0 …)中lastHasMore=0表示已到列表末尾(即全量)。
方法二:反爬关标签页账号专用
背景:部分账号对程序化 page.goto / page.reload 触发反爬、直接关闭标签页;此时方法一的"重载抓第 1 页"会让标签页/会话一起死掉。试过但均失败的路:被动监听 + 手动 F5(run-code 执行上下文遇导航即销毁)、CDP/requests 记录、setTimeout 等待。唯一跑通的路:放弃"导航",改用单 run-code 内:① 装 window.fetch/XMLHttpRequest 猴子补丁捕获 /aweme/post;② 程序化改 scrollTop 滚动(非导航,执行上下文不死)逐页触发原生分页;③ 等待只能用 page.waitForTimeout(Node 侧、跨刷新存活),绝不能用 setTimeout;④ 收集完返回数组由 runjs.py 落盘。
"$VENV_PY" scripts/runjs.py scripts/douyin_capture_single.js <session> ./videos_douyin_xxx/feed_index.jsonl
"$VENV_PY" scripts/douyin_feed_download.py --indir ./videos_douyin_xxx --workers 6- 字段转换(下载前必做,方法二产物字段名为
video.play_addr.url_list[0]):见「排错速查」末条。 - 缺口说明:此类账号反爬会对个别分页跳过/限流,单次采集可能缺少量条目;强行补齐易再次触发关标签页,建议先用已有条目下载交付。
抖音专属坑(务必注意)
- 第 1 页必须在接管后重新加载才能被监听器捕获(接管瞬间页面已加载完,第 1 页请求发生在接管前);本版脚本已自动处理。
- 滚动容器不是 window;须全局扫描取最大可滚动 div(见稳健化手册第 7 条)。
duration单位是毫秒(如 19344ms≈19.3s),下载脚本已÷1000换算秒。- 水印:抖音 web 对第三方账号只给
play_addr(带水印)直链,download_addr通常为空 → 产物带水印。 - 直链有效期:
video_url含过期参数,采集后尽快下载。 - 字段结构:
video.play_addr.url_list[0](视频)、video.cover.url_list[0](封面)、desc(标题+#话题)、statistics.{digg_count,comment_count,play_count}。
抖音实战(账号已匿名)
| 账号 | 方法 | 结果 |
|---|---|---|
| 账号A | 方法一(稳健版) | 102 视频(2.9 GB,0 失败,带水印;lastHasMore=0 全量) |
| 账号B(反爬关标签页代表) | 方法二 | 572 视频(1.7 GB,0 失败,带水印;目标 602,缺 ~30 为反爬分页跳过) |
平台二:快手(Kuaishou)
决策流程(方法A 主路,遇 50 转方法B)
1. 接管并停在快手主页 → node "$PW_CLI" -s=kuaishou attach --extension chrome
2. 方法A 主脚本 GraphQL(需登录 Cookie):
先导出 cookie:node "$PW_CLI" -s=kuaishou state-save ks_state.json → 拼 ks_cookie.txt
"$VENV_PY" scripts/kuaishou_batch_download.py "<userId或主页链接>" -o ./videos -c "$(cat ks_cookie.txt)"
→ result:1:完成,产物在 ./videos。
→ result:50(feeds 空):GraphQL 该路受限,**转方法B**,不要停。
3. 方法B 兜底·浏览器原生 feed 接口(无需单独传 cookie,靠已登录浏览器上下文):
"$VENV_PY" scripts/kuaishou_feed_collect.py --uid <userId> --out ./videos_<userId>
"$VENV_PY" scripts/kuaishou_feed_download.py --out ./videos_<userId> --workers 6关键认知:
result:50是接口级限制,不是账号封禁。遇 50 一律转方法B。
方法A:主脚本(GraphQL,多数账号适用)
"$VENV_PY" scripts/kuaishou_batch_download.py "https://www.kuaishou.com/profile/<userId>" -o ./videos -c "$(cat ks_cookie.txt)"
# 仅测试前3个:--test 3 ;断点续下/仅补元数据:--resume ;限制数量:-n 50userId从主页 URL.../profile/<userId>自动解析(也可直接传 userId)。- 文件名:
序号_时间戳_标题.mp4(标题含 #话题,超 80 字截断)。 - GraphQL schema(
page是 String 类型;视频字段在... on PhotoEntity { caption manifest timestamp duration ... };直链在manifest.adaptationSet[i].representation[j].url)。
方法B:兜底(浏览器原生 feed,GraphQL 返回 50 时用)
"$VENV_PY" scripts/kuaishou_feed_collect.py --uid <userId> --out ./videos_<userId>
# 可选:--limit 5 只翻5页试跑;--resume 从 last_pcursor.txt 续采;--session ks2 换会话
"$VENV_PY" scripts/kuaishou_feed_download.py --out ./videos_<userId> --workers 6
# 缺省读同目录 ks_cookie.txt;也可 --cookie 指定- 该接口依赖快手前端动态令牌
kww(请求头)+__NS_hxfalcon(URL参数),纯 Python 难复现,由 collect 脚本自动从浏览器实时重抓新鲜令牌后在page.evaluate(fetch)上下文发请求。
快手关键技术结论(已实测)
- 匿名请求被风控:GraphQL 对匿名请求返回
result:400002+ 验证码,必须带已登录 Cookie。 - 无水印:manifest 直链是源站视频,通常不含水印——与抖音不同。
- 标题/描述字段:快手
PhotoEntity无独立title/description,文本全在caption(含 #话题)。 - duration 单位:接口返回毫秒,脚本已换算为秒。
- 令牌可会话级复用:方法B 抓到的一组
kww+hxfalcon可在翻页中复用。 result返回码:result:1=成功;result:50=GraphQL 该路受限(转方法B);result:400002=未登录/风控。
快手实战(多账号,已全量跑通,账号已匿名)
| 账号 | 方法 | 结果 |
|---|---|---|
| 账号C | 方法A | 673 视频 |
| 账号D | 方法A | 1331 视频 |
| 账号E | A→B | 237 视频(B 跑通) |
| 账号F | A→B | 223 视频 |
| 账号G | A→B | 673 视频 |
| 账号H | A→B | 1872 视频 |
结论:快手先方法A,遇
result:50必转方法B。
统一元数据产物(两平台一致)
序号_时间戳_标题.mp4+ 同名.meta.json:单视频结构化元数据(title/description、hashtags、publish_time、点赞、播放、评论、时长、封面、直链、文件名)。metadata.csv:总清单(序号,视频文件,标题(含话题),话题标签,发布时间,点赞,评论,播放,时长秒),utf-8-sig 编码,Excel 直接打开不乱码。metadata.jsonl:每行一条 JSON,便于程序消费。- 两平台文件名格式、meta 字段、清单列结构完全一致,仅「水印」一项抖音带、快手不带。
排错速查
Browser 'xxx' is not open(attach 后跑 run-code) → 会话不跨命令/进程复用。把attach与run-code/采集放在同一条命令(同一进程)内执行。Target page, context or browser has been closed→ attach 抓到了扩展connect.html连接页(该页握手完即关)。解法:run-code 内遍历标签页找目标主页标签,找不到就newPage()+goto(本版脚本已内置)。- attach 一直挂起 / 超时 → 先前强杀长任务杀了 CLI daemon,扩展不肯重连。重启 Chrome 恢复,或清理残留 node 进程后重试;别再强杀长任务。
result:400002/ 验证码(快手) → Cookie 失效或未带;重新attach+state-save取新鲜登录态。- GraphQL
result:50(快手) → 不是封禁,立刻转【方法B】。 - 抖音滚动不翻页 / 采集 0 条 → 滚错容器(须全局扫描取最大可滚动 div,非 window);或第 1 页未重载(须重载主页触发)。
- 抖音
goto/reload后标签页被关、会话失效 → 该账号触发反爬关标签页,立刻改用【方法二】(单 run-code + 猴子补丁 + 程序化 scrollTop),不要再走 goto/F5 路线。 - 方法二报
setTimeout is not defined→ run-code 沙箱无 Node 全局定时器,改用page.waitForTimeout。 page.evaluate报URL is not defined(快手) → 同会话多次 reload 后执行上下文损坏。解决:换新会话名重新attach,collect 加--session ks2。- CDN 下载失败(抖音) → 直链只需
Referer: https://www.douyin.com/+ Chrome UA(无需 Cookie);失败多为链接过期,重新采集。 - CDN 下载失败(快手) → 直链需带
Cookie+Referer: https://www.kuaishou.com/(脚本已默认加)。 - 个别视频 403 / 失败 → 直接重跑下载命令(幂等,跳过已下、补下缺失、补写 meta)。
- duration 显示异常大 → 记得毫秒÷1000;脚本已处理。
attach失败 → Chrome 未装 Playwright 浏览器扩展,先安装。- 方法二产物字段不匹配下载器 → 下载前手工转换字段:
import json
p="videos_douyin_xxx/feed_index.jsonl"
items=[json.loads(l) for l in open(p,encoding="utf-8") if l.strip()]
for it in items:
v=it.get("video") or {}
pa=v.get("play_addr") if isinstance(v,dict) else None
url=(pa.get("url_list") or [None])[0] if isinstance(pa,dict) else None
if url: it["video_url"]=it["download_url"]=url
st=it.get("statistics") or {}
st.update({"digg":st.get("digg_count"),"comment":st.get("comment_count"),"play":st.get("play_count")})
it["statistics"]=st
open(p,"w",encoding="utf-8").write("".join(json.dumps(it,ensure_ascii=False)+"n" for it in items))合规
仅用于个人备份/存档自己有权处理的账号内容,遵守平台 ToS 与版权法,勿搬运牟利或绕过版权。