news 2026/9/17 14:31:02

小红书数据采集实战:公开页面解析与反爬应对全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小红书数据采集实战:公开页面解析与反爬应对全指南

“小红书爬取实战指南”,说白了就是教你怎么把小红书上的公开笔记数据,包括文案、图片、视频、点赞收藏这些信息,按你自己的需求批量采集下来。这事的难点从来不在“发请求”本身,而在小红书的反爬策略和前端渲染机制,很多人一上来就卡在签名校验、风控验证上,折腾几天颗粒无收。这篇文章我会分享一套我自己一直在用的方案,不碰复杂逆向,不依赖付费解析接口,主要走公开页面解析和常规HTTP请求配合移动端适配的路线,稳定性和可复现性都经过实测,适合有Python基础、想自己做数据采集和分析的读者参考。

不管你是想采集某个话题下的优质内容做选题调研,还是想备份自己发过的笔记素材,或者像我一样需要把小红书作为灵感库同步到本地笔记系统,这套思路都能帮上忙。我会把从分享链接到笔记详情、再到资源文件下载的完整流程都拆开讲清楚,包括其中关键参数从哪来、为什么这么写、踩坑了怎么排查,尽量让每一步你都能照着自己实现一遍。

1. 小红书数据采集的难点与整体方案选型

1.1 为什么小红书爬取比普通站点麻烦

做过爬虫的人都知道,普通网站采集通常就是找到列表页和详情页的URL规律,然后用requests请求HTML,再用解析库提取内容。小红书不一样,它有两个最核心的问题摆在前面。

第一是渲染机制不是服务端直出。小红书PC端的页面大部分内容由JavaScript动态渲染,直接curl拿下来的HTML代码里,只有一堆空的DOM节点和打包后的JS文件,正文、图片地址、点赞数全都看不见。早期很多人靠Selenium模拟浏览器渲染来绕过这个问题,但这种方式慢、占资源、且WebDriver特征明显,很容易被检测。

第二是接口风控。小红书的数据接口有完整的签名校验机制,请求头里少了几个关键参数,或者参数的生成算法不对,服务器直接拒绝响应。很多教程一上来就让你去逆向JS、补环境、搞补环境框架,这对新手来说门槛太高,而且说实话,你想要采集的数据,根本不值得去冒封号的风险做接口逆向。

所以我的思路是绕开这两座大山,选择一条“半公开”的路。

1.2 整体方案:合法合规的公开数据解析路线

既然PC端详情页有渲染障碍,那我们换个思路,利用小红书对外分享逻辑中的一个公开环节——分享链接。无论你在App里点分享,还是在网页端复制链接,最终都会得到一个形如http://xhslink.com/a/xxxx的短链。这个短链本身不携带笔记ID,但它会通过HTTP重定向指向一个包含真实笔记ID的长链接。

在移动端,小红书的H5分享页出于搜索引擎收录和社交平台外显的需求,会直接在服务端渲染出页面关键数据,并且把笔记的JSON数据注入到HTML里。这就是整条采集链路的突破口。

整体流程拆解下来其实只有三步:

  1. 通过分享短链拿到重定向后的长链,从中解析出笔记ID;
  2. 用移动端UA请求笔记H5页面,提取HTML里嵌入的JSON数据;
  3. 解析JSON中的标题、正文、图片、视频地址,按需下载保存。

这个方案的好处是请求量小、逻辑简单、稳定性高。因为步骤都在公开页面的正常访问逻辑内,不涉及任何接口加密参数破解,触发风控的概率也低很多。我实际跑了小半年,采集量每天在几百篇的量级,一直没出过问题。

2. 动手前的准备:环境配置与合规边界

2.1 技术栈选型与依赖安装

这套方案用到的技术栈非常轻量,Python 3.8以上就行,核心依赖只有三件套:

  • requests:负责发送HTTP请求和跟随重定向;
  • BeautifulSoup4 或 lxml:负责从HTML中提取JSON片段;
  • json + re:Python标准库,负责数据解析。

安装命令也很简单:

pip install requests beautifulsoup4 lxml

我把整个流程写成过一个 Demo,核心模块大概长这样:

import re import json import requests from bs4 import BeautifulSoup HEADERS = { "User-Agent": ( "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) " "AppleWebKit/605.1.15 (KHTML, like Gecko) " "Version/16.6 Mobile/15E148 Safari/604.1" ), "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", } def get_redirect_url(short_url: str) -> str: resp = requests.get(short_url, headers=HEADERS, allow_redirects=True, timeout=10) return resp.url

注意这里有两个细节。第一,User-Agent必须用移动端的,因为桌面端UA会被重定向到PC版页面,拿到的HTML结构完全不同,解析逻辑就失效了。第二,allow_redirects=True是requests的默认行为,但有的教程建议手动关闭再逐跳处理,我实测直接跟随重定向更省事,最后拿到的就是经过多层跳转后的最终URL。

2.2 合规红线检查清单

在讲具体实现之前,我必须先把合规边界说清楚。爬取公开数据虽然有技术可行性,但使用场景一定要守住底线。我自己在实际使用中给自己定了几条硬性规则,建议你也照这个标准来:

  • 只采集用户主动公开的笔记内容,绝不碰需要登录才能看到的用户主页、私信、粉丝列表等非公开数据;
  • 不对单个用户做高频、大规模的数据采集,不用于任何形式的用户画像分析;
  • 不尝试逆向或破解任何签名参数,不绕过平台的反爬机制,不用虚假账号批量刷接口;
  • 采集到的数据仅用于个人学习、素材整理或学术研究,不用于商业搬运、洗稿、二次创作发布等侵权行为。

这些规则不是客套话。小红书对爬虫的容忍度其实很低,真正触发风控封禁的,绝大多数不是单纯的访问频次,而是特征明显的接口高频调用。走公开页面解析这条路,天然就避开了最敏感的加密参数,再加上合理的采集频率,安全性会高很多。

3. 核心实现:从短链到笔记详情页的数据解析

3.1 短链重定向获取真实链接与笔记ID

短链解析是整个流程的入口。小红书的分享短链一般长这样:http://xhslink.com/a/AbCdEf,你需要请求一次拿到最终跳转地址。

最终地址一般有两种形态:

https://www.xiaohongshu.com/discovery/item/64a1b2c3000000001f03d4e2 https://www.xiaohongshu.com/explore/64a1b2c3000000001f03d4e2?xsec_token=...

不管是discovery/item还是explore这种路径,后面跟的这一长串32位十六进制字符串,就是笔记的唯一ID。用正则提取即可:

def extract_note_id(url: str) -> str: match = re.search(r"/item/([0-9a-f]{24})", url) if not match: match = re.search(r"/explore/([0-9a-f]{24})", url) return match.group(1) if match else ""

这里有个容易踩的坑。短链跳转得到的URL里,经常还会跟着xsec_tokenxsec_source这类追踪参数。有人会自作聪明把参数清掉再请求详情页,但我实测发现,在这个H5方案里带着这些参数一起请求反而更稳,因为个别笔记的访问权限校验与xsec_token有关,去掉可能导致返回页面里没有有效数据。所以我的建议是:解析出笔记ID的同时,把原URL完整保留,详情页请求直接用原始跳转URL,不做参数裁剪。

还有一种常见场景是批量采集。你手里有一堆短链,只需要循环执行上面的逻辑,把每个短链映射成笔记ID,存到一个列表或字典里即可。如果短链数量大,建议在循环里加一个随机延时,比如0.5到1.5秒之间,避免连续请求触发频率限制。

3.2 构造详情页请求并解析内嵌JSON数据

拿到携带参数的长链之后,接下来就是用移动端UA请求这个详情页。这是我整个方案里最关键的一步,因为很多教程在这里翻车,原因是他们请求之后发现HTML里根本没有数据,直接以为被反爬了,实际上是UA不对。

请求代码很简单:

def fetch_note_html(url: str) -> str: resp = requests.get(url, headers=HEADERS, timeout=10) resp.encoding = "utf-8" return resp.text

拿到HTML之后,搜索一下window.__INITIAL_STATE__,这是Next.js框架注入到页面里的全局状态对象,包含笔记详情的绝大部分数据。用BeautifulSoup解析出包含这个变量的script标签,然后用正则或json解析把数据取出来。

def parse_note_data(html: str) -> dict: soup = BeautifulSoup(html, "lxml") scripts = soup.find_all("script") raw = "" for s in scripts: if s.string and "window.__INITIAL_STATE__" in s.string: raw = s.string break match = re.search(r"window\.__INITIAL_STATE__\s*=\s*(\{.*?\})\s*</script>", raw, re.S) if not match: return {} state = json.loads(match.group(1)) # 不同页面层级结构略有差异,需要逐层取数据 note_data = state.get("note", {}).get("noteDetailMap", {}) if not note_data: return {} note_id = next(iter(note_data)) return note_data[note_id].get("note", {})

这里要特别说明parse_note_data的两点经验。

第一,整个JSON里最外层叫noteDetailMap,它的key是笔记ID,value里嵌套着一个note字段,这个note才是你真正要的数据对象。部分老教程会教你直接取state.note.noteDetailMap[noteId].note,但实际返回的key不一定是你之前解析出的ID,所以我用next(iter(note_data))动态取第一个key,这样更通用。

第二,JSON解析时不要迷信一次性写死层级。小红书前端有时候会调整字段结构,我建议在写完整解析逻辑之前,先把这个JSON整体dump下来肉眼看一下结构,再决定怎么取值。把state保存为json文件这一步,在新手阶段能帮你少走很多弯路。

3.3 图片与视频资源地址的提取

进入note对象之后,里面最有价值的几个字段是:

  • title:笔记标题;
  • desc:笔记正文文案;
  • imageList:笔记配图列表,每项是一个对象,里面的urlDefaulturlPreurlOrigin就是不同尺寸的图片地址;
  • video:视频笔记的视频信息对象,里面包含urldurationcover等字段;
  • interactInfo:点赞、收藏、评论数等互动数据。

以图片为例,提取逻辑如下:

def extract_images(note: dict) -> list: images = [] for img in note.get("imageList", []): url = img.get("urlDefault") or img.get("urlPre") or img.get("urlOrigin") if url: images.append(url) return images

视频的提取要稍微注意一点。video对象里通常有一个url字段,这可能是直接可播放的mp4地址,也可能是一个带签名的临时地址。实测下来,公开视频笔记的url一般可以直接下载。但这里有个很关键的坑:视频地址可能不是永久的,它是带时效的签名URL,过一段时间会失效。所以如果你要下载视频,最好在拿到地址后尽快下载,不要存在数据库里过几天再去取。

图片地址相对稳定,但也别太大意。我遇到过urlDefault返回的空值的场景,所以写提取函数时做了三层fallback,urlDefault取不到就取urlPre,再不行就用urlOrigin。这三种后缀对应的图片尺寸不同,urlOrigin是原图,urlDefault是压缩后的展示图,根据自己的需求选就行。

4. 数据落地:批量下载与本地素材库整理

4.1 批量下载的工程化处理

数据结构解析出来后,下载就变成纯粹的体力活了。但工程化处理还是有几个细节值得分享。

首先是文件命名。直接用笔记ID做文件名虽然简单,可读性太差。我习惯用“发布时间_笔记ID_序号”的结构,既保证唯一性,又方便回溯。配合Pandas先建一个笔记信息表,里面存标题、正文、链接、图片本地路径,再根据表内容批量下载图片,整个流程会非常清晰。

import os import time import requests def download_image(url: str, save_path: str): resp = requests.get(url, headers=HEADERS, timeout=15) if resp.status_code == 200: with open(save_path, "wb") as f: f.write(resp.content) def batch_download(notes: list, base_dir: str): for note in notes: note_id = note["note_id"] images = note["images"] save_dir = os.path.join(base_dir, note_id) os.makedirs(save_dir, exist_ok=True) for idx, img_url in enumerate(images): ext = os.path.splitext(img_url.split("?")[0])[1] or ".jpg" save_path = os.path.join(save_dir, f"{idx:02d}{ext}") download_image(img_url, save_path) time.sleep(0.3)

这个0.3秒的延时是必要的。图片是存储在CDN上的,跟详情页接口不同,但大量并发下载同样容易被限流。保守一点,单线程加小延时,虽然慢,但胜在稳定。

其次是异常处理。网络请求一定会碰到超时、SSL错误、403等情况,不要因为这些让整个任务中断。给download_image加上 try-except,失败的URL记录到日志文件里,全部跑完后再统一重试那些失败的,比一边跑一边人工盯屏幕高效得多。

最后是断点续传。如果采集量上千,中途断网或进程被杀是常有的事。我会在下载之前检查目标文件是否已存在,存在就跳过。这样重新运行脚本时,已完成的部分不会重复下载。

4.2 与 Obsidian 等本地素材库的联动

我自己最喜欢的一个应用方式是把小红书采集到的内容同步到 Obsidian 素材库。这其实就是一个数据落地的过程:把笔记标题、正文、原链接、标签等信息拼装成 Markdown 文件,图片作为附件放在同名的附件目录里。

Markdown 文件的模板大致如下:

--- 标题: 原文链接: 采集时间: 标签: [小红书, 素材] --- # 标题 正文内容... ## 参考图片 ![[img_01.jpg]] ![[img_02.jpg]]

在 Obsidian 里,插件的附件目录设置好之后,只要把图片和 md 文件放入对应目录,就能直接渲染出图文并茂的素材页。这样做的价值在于,它把一个“临时采集动作”升华成了“素材沉淀流程”,后续你搜索、引用、二次创作都非常方便。

这个联动方案我用了很久,配合 Obsidian 的标签体系和反向链接功能,小红书采集的内容就变成了我自己的知识库资源。每次要做选题调研的时候,我直接本地搜索关键词,比在线翻页面快太多了。

5. 常见问题与反爬风控的排查经验

5.1 高频问题排查速查表

这个小节我直接以表格形式整理出我在实操中碰到的高频问题,以及对应的排查思路,方便你对照着快速定位。

问题表现可能原因排查与解决
短链请求后没有跳转到笔记页短链过期或分享被撤回换一条新的短链测试,确认链接本身有效
详情页HTML里找不到__INITIAL_STATE__UA被识别为桌面端,返回了PC页面检查UA是否为移动端,且不能带桌面浏览器的特征头
JSON解析报错或字段层级不对页面结构更新或笔记类型特殊把原始HTML和state保存下来,重新确认字段路径
图片下载409/403CDN防盗链校验请求图片时增加Referer: https://www.xiaohongshu.com
连续采集后出现滑块验证请求频率过高触发了风控停采12-24小时,降低频率,增加延时和随机化
视频地址下载后无法播放签名URL时效过期拿到地址后立即下载,重新采集获取新地址

这里面最值得展开的是图片下载的Referer问题。小红书图片CDN会校验请求来源,如果你直接用默认的requests头去下载图片,很可能返回403。加上Referer之后就正常了。很多人做完了详情页解析,最后卡在图片下载这步,十有八九是漏了这个头部信息。

5.2 风控与限流处理思路:降速比对抗更有效

关于风控,我想说一点跟主流教程不太一样的经验。

市面上很多教程喜欢教你如何模拟真实用户、随机化UA、使用代理IP池,甚至去做浏览器指纹伪装。这些操作在细节上确实能让请求更像真人,但问题在于,你用得越多,你的“伪真人”特征就越容易被更大规模的检测模型识别。我的体会是,对小红书这种风控非常严格的内容平台来说,采集频率的克制才是最好的防封策略

具体操作上,详情页请求之间的间隔不要低于3秒,最好在3到8秒之间随机化。批量下载图片时,单张间隔0.3到1秒。单次任务不要采集超过500篇笔记,任务跑完就停,不要24小时连续挂机。如果你有上千篇笔记的需求,拆成几天分批跑,每次换不同的时间段。

还有一个小技巧,用requestsSession对象保持连接复用,比每次新建连接更不容易被风控识别为脚本。这是因为浏览器访问同一站点时会保持TCP连接,而普通爬虫每次新建连接,连接特征差异很明显。把短链请求、详情页请求、图片下载都放在同一个 Session 里,整体请求链路更接近真实浏览器行为。

5.3 关于签名参数与接口逆向的理性态度

最后聊聊很多人都会问的问题:为什么不走小红书App的接口,那个才是数据最全、字段最完整的途径?

确实,小红书App接口的数据质量是最高的,不仅有标题、文案、图片,还有笔记的搜索权重、推荐位、热门标签等额外信息。但代价是要处理x-sx-t等一系列签名参数,这些参数由加密算法动态生成,每次请求都需要重新计算。想要拿这些参数,只能通过逆向JS代码或Hook客户端的方式实现。

我的立场很明确:公开页面能拿到的数据,绝不去碰签名逆向。原因有三:

  • 合规风险高,逆向和绕过签名验证在服务条款和网络安全法规层面都有灰色甚至违法风险;
  • 逆向成本高,平台的加密算法会不定期更新,你花大量精力逆向出来的代码,过半个月可能就失效了,维护成本极高;
  • 收益不匹配,对于个人学习和素材采集来说,H5页面里的数据已经覆盖了90%以上的核心内容,没必要为了锦上添花的几个字段去冒风险。

如果你确实需要大规模、结构化的数据做学术研究,我更建议你走官方的内容合作或数据服务渠道,而不是纠结于绕过签名。爬虫说到底是个工具,用得好是效率提升,用不好是给自己挖坑。

采集方案的稳定性关键不在于技术多炫,而在于对平台规则的理解和对自身需求的清晰认识。公开页面解析这条路,技术门槛低、维护成本可控,对于绝大多数个人开发者来说已经够用了。我在这套方案上踩过不少坑,从UA选择到字段提取,从图片防盗链到采集频率控制,每一步都是实测验证过的。希望这篇笔记能帮你省下几天的摸索时间,把精力花在真正有价值的数据分析和内容创作上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 14:23:21

LTP7792低噪声LDO深度解析:国产芯片如何突破模拟供电瓶颈

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 14:23:03

智能小车外文文献翻译:Python+Pandoc生成原文中文对照PDF

简介&#xff1a;面向智能小车及智能车辆方向的学习者与研究者&#xff0c;这份外文文献翻译资料提供了英汉对照阅读材料&#xff0c;内容涵盖智能车辆发展背景、智能交通系统&#xff08;ITS&#xff09;的兴起、电动汽车替代趋势以及汽车电子控制技术的广泛应用。适合用于课程…

作者头像 李华
网站建设 2026/9/17 14:21:27

制造业SPC实战:Excel与Minitab控制图应用指南

1. 为什么制造业需要SPC实战手册在金属加工车间干了十五年&#xff0c;我最头疼的就是每天早会上品质部长拿着不良品报表质问"为什么又超差了"。直到三年前导入SPC系统后&#xff0c;我们车间的过程不良率从8.7%降到了2.3%&#xff0c;这个实战手册就是我用三十多次现…

作者头像 李华
网站建设 2026/9/17 14:20:24

Windows bat 文本清洗:删除空行、空格、制表符与末尾空行

手上有一堆从系统导出、从数据库拷出来、从聊天记录粘贴下来的纯文本&#xff0c;打开一看&#xff1a;满屏的空行、行首一串空格、行尾几个制表符、文件末尾还挂着三五行的空白——这种文件拿去给下游程序解析&#xff0c;轻则报错&#xff0c;重则静默解析出空记录。更麻烦的…

作者头像 李华
网站建设 2026/9/17 14:17:57

Fortran正压原始方程模式实习:数值天气预报入门实战

简介&#xff1a;本资源是一份面向气象、大气科学及相关专业高年级本科生或研究生的数值天气预报实践教学材料&#xff0c;聚焦正压原始方程模式的核心原理与编程实现&#xff0c;解决理论学习向工程实践转化的关键训练需求。文档完整呈现了以1973年4月29日东北—华北500hPa实测…

作者头像 李华