网页能用、脚本空结果:别急着怪账号风控

仅供技术分享。
本文只讨论排障思路与接口对照方法,不提供可直接滥用的完整请求样例、Cookie 或绕过方案;请遵守平台规则与当地法律法规,勿用于未授权抓取或商业侵权。

写在前面:这篇文章适合谁

如果你也在做这些事,这篇可能有用:

  • 维护过「曾经能跑」的xhs脚本 / MCP / 爬虫
  • 看到 success=trueitems=[],或 300011 Account abnormal
  • 怀疑是账号风控,又发现网页明明搜得动
  • 想复盘:逆向到底该怎么排优先级,而不是一上来就猜算法

这不是「教你绕过平台」的教程,是我(和协作工具)把一套搜索+评论语音下载链路从半死修通时,真实走过的弯路。
文中只写现象、对照方法和结论;具体 Cookie、完整签名串、可复制 payload 一律不贴——那些东西过期快,贴了也只会害人。

一句话先说在前面:

网页能搜、脚本报账号异常,优先怀疑请求形态没对齐,不要急着给账号判死刑。


0. 我们一开始在修什么

业务目标很朴素:

  1. 按关键词搜笔记
  2. 拉评论里的语音
  3. 把音频落盘,顺带写 asr_list.json

工具栈大概是:

用什么
HTTPcurl_cffi(模拟 Chrome TLS)
签名本地 JS(GetXs)+ xhshow
辅助Playwright 打开真页面,抓现网头

现象却很拧巴:

  • CDN 音频:有 URL 时,加固 Referer 后能下
  • 搜索:经常 items=[],有时 300011
  • 评论:code=-1 / 偶发 406
  • 同 Cookie 在浏览器里,搜索正常

所以问题不在「文件下不下来」,而在「脚本根本拿不到正常 JSON」。


1. 先分层,别一口咬死「风控」

排障时我习惯把症状拆成三层:

问什么典型信号
A. 账号/会话Cookie 还活着吗?get_me 成功 /「登录已过期」
B. 请求形态Host、Path、Method、签名头是否像网页?网页 200 有数据,脚本同号空结果
C. 频控短时间是不是打太狠?300013 访问频次异常

早期我们犯过一个错:

搜索返回 300011 Account abnormal → 直接结论「号废了」。

后来用户一句「网页能搜」把结论掀翻了。

更准确的说法是:

  • 300011 可以是真异常号
  • 也可以是服务端对「可疑客户端」的软拒绝文案
  • 必须以「同 Cookie、同账号在官方网页是否正常」做对照实验

对照结果出来之前,别把锅全扣在风控上。


2. 签名对齐:做对了一半

在怀疑「账号废了」之前,我们先把签名链路往现网靠:

  1. 删掉写死的旧 x-s-common
    改成:启动时打开真实xhs页,从真实请求里捕获一套
  2. GetXs 字段对齐
    • 版本号 x0 升到现网常见值
    • 平台 x2 不再写死 Mac,按实际环境取 Windows/Mac
  3. 签名格式试 XYW_ / XYS_
    不同接口偏好不一样;user/me 曾用一种格式先跑通
  4. UA / impersonate 统一
    避免「签名页说自己是 A 浏览器,HTTP 层却装成 B」

这些改完后出现过一个很有启发的组合拳:

接口结果
user/me签名过了,成功
search/notes(旧路径)立刻 300011

这说明:

签名不是「全错」或「全对」。
简单接口松、搜索接口严——严的那一侧,往往还差别的东西。

只修签名、不对照网页真实搜索请求,会在半山腰原地打转。


3. 真正的破局:一张抓包表

破局不是又调了一个参数,而是用户贴出网页搜索成功那一跳的 Request Headers。

对照之后,差异几乎「一眼脏」:

项目脚本旧实现网页真实搜索
Hostedith.xiaohongshu.comso.xiaohongshu.com
Path/api/sns/web/v1/search/notes/api/sns/web/v2/search/notes
MethodPOSTPOST(一致)
x-s 前缀常走 XYW_ 或旧逻辑XYS_
额外头常缺x-rap-param
Cookie可能过期浏览器现网会话

读完这张表,之前很多「玄学」都落地了:

  • 为什么 me 能过、搜索不行?
    → 搜索已经换了网关/版本,你还在打旧门。
  • 为什么网页正常却报账号异常?
    → 异常文案打在「形态不对」的客户端上。
  • 为什么空 items300011 会交替出现?
    → 都是「没被当成正经网页客户端」的不同表情。

改到 so + v2 + XYS_ + x-rap-param 后,验收非常干脆:

  • 搜索:code=0,二十多条
  • 评论:能拉到含语音的评论
  • 下载:语音批量成功

业务输出逻辑(目录命名、asr_list.json、Excel)不用动——动的是入口,不是落盘


4. 一张「逆向优先级」清单

如果再遇到「网页行、脚本不行」,我建议按这个顺序,而不是先开逆向算法深潜:

第 1 优先:同号对照

  1. 浏览器能搜/能看评论吗?
  2. 脚本用同一份刚抓的 Cookieme、搜索、评论

任一失败,先分清是会话死了,还是形态不对。

第 2 优先:抄网页的「门牌」

打开 DevTools → Network,过滤成功那次业务请求,先抄:

  • 完整 URL(Host + Path + 版本号)
  • Method
  • 关键 Request Headers 名字列表(不必贴值到博客里)
  • Request Payload 的字段名(keyword / page / page_size…)

门牌错了,算法对了也没用。

第 3 优先:签名成对出现

常见坑:

  • 自己生成了新的 x-s,却拿另一套旧的 x-s-common 去配
  • 页面 live 捕获的 x-s-common 去覆盖库生成的成对签名

原则:

谁生成的 x-s,就尽量用谁成套的 x-s-common / x-rap-param

第 4 优先:再谈频控与节奏

通了之后才会遇到 300013(访问频次异常)。
那是另一场仗:间隔、批次、长歇、单帖语音条数、自动冷却重试。
别和「打错 Host」混在一章里治。


5. 通了之后:频控才是下一座山

接口对齐后,业务侧又撞上 300013

  • 第一次:大约 100 个作品停
  • 加密间隔后:大约 50 个就停(更早)

这不矛盾。

因为瓶颈往往不是「作品间隔秒数」,而是单作品背后的请求扇出

1 个作品 ≈ 详情 + 评论翻页 + 正文媒体 + N 条语音 CDN

以前每帖语音目标可以到几十条。
50 帖 × 二十条语音,请求量轻松上千——作品间睡 5 秒也挡不住累计频控。

后来更稳的策略是组合拳:

手段作用
作品间隔拉长(十秒级)降峰值
每帖语音上限下调砍扇出
每 N 帖强制长歇(分钟级)降累计
300013 冷却再试,而不是立刻自杀退出可恢复
同一 Cookie 不要多开 exe避免自己打自己

记住:

频控没有银弹,只有把触发点往后推。
要量大,就分天、分目录续跑(已下载跳过)。


6. Cookie 怎么拿更省事

手拷 F12 能用,但容易漏字段、也难教给同事。

更合理的日常方案是:

  1. 本地弹出浏览器
  2. 手机扫码登录一次
  3. 检测到 web_session + a1 后写入 cookie.txt
  4. 窗口不要闪退,方便人确认

注意:

  • Cookie 会过期,过期再扫
  • 不要cookie.txt 提交仓库或发群
  • 扫码脚本和业务 exe 分开打包,职责清晰

7. 这次复盘里最值钱的三句口诀

口诀 1:错误文案不等于根因

Account Abnormal 看起来像封号。
同号网页能搜时,它更像:你这请求不像网页。

口诀 2:先对门牌,再对算法

Host / Path / 版本号 / 必备头,比「再逆向一版 WASM」便宜一百倍。
一张成功抓包,往往比三天猜签名值钱。

口诀 3:通了才配谈风控

接口都打错时,谈间隔、谈换号、谈指纹,容易变成噪音。
通了之后,300013 才是该认真做节流的信号。


8. 如果你也要复盘自己的链路,最少看这几样

不必先上复杂平台。本地这几样就够讲清故事:

材料能告诉你什么
网页成功搜索的一条 network真 host / path / 头字段
脚本同 cookie 的 me / search / comment 返回哪一层先裂
errors.log 里的 300013 / 300011频控还是形态拒绝
业务落盘目录是否仍按旧规则改的是入口还是产品逻辑

我建议把结论写成一张表钉在仓库里:

搜索:so + v2 + XYS + x-rap
评论:edith + v2 comment/page + 签名 GET
下载:CDN 带 Referer/Origin,URL 会过期要现拉
节奏:作品间隔拉长 + 限语音条数 + 批次长歇 + 冷却重试

9. 总结

再遇到「脚本挂了」,我会先问三个问题:

  1. 网页同号现在还能搜吗?
  2. 成功那一跳的 URL 还是不是我们代码里的那个?
  3. 失败码是形态拒绝,还是频次拒绝?

再次声明:仅供技术分享,请合法合规使用。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐