简介:这份资源面向希望入门网络数据采集与小程序开发的开发者,聚焦小红书平台数据抓取与微信小程序场景下的流量分析。包内共5个文件,以txt说明、py脚本、md文档及快照抓取文件为主,压缩包约6KB,体积轻量便于快速查阅。核心内容围绕mitmdump抓包工具展开,讲解如何拦截、记录并分析HTTP与HTTPS流量,进而解析小红书请求中的加密headers参数;同时涉及将抓取结果写入CSV表格,并通过判断去重避免重复插入头信息,保证数据准确完整。配套的Python脚本可用于处理小红书相关抓取任务,说明文档与标签、资源内容文件则提供使用指引和关键词参考。目前已有435人学习,适合想了解抓包分析、数据去重与小程序数据处理的初中级开发者参考借鉴。
1. 拆开这个抓包工具包:小红书小程序数据怎么落到 CSV 里
做小红书数据的朋友大概率都遇到过这个场景:小程序里刷到的笔记、商品、用户信息,想批量存下来做选品分析或竞品监控,结果发现接口全是加密参数,浏览器 F12 抓不到,复制粘贴又太慢。这个压缩包就是冲着这个痛点来的——它把小红书微信小程序的抓取脚本、抓包配置、CSV 落库逻辑和百度快照兜底方案打包在一起,核心文件是小红书微信小程序.py,配套标签.txt、资源内容.txt、README.md和一个百度快照抓取模块。适合谁?做小红书选品、内容运营、竞品分析的技术同学,以及想搞明白小程序抓包到底怎么落地的开发者。它不解决"绕过风控"这种玄学问题,但能把"抓什么、怎么抓、怎么存、怎么去重"这条链路讲清楚。
2. 抓包工具选型与 mitmdump 拦截链路:为什么不是 Fiddler
2.1 小程序抓包和网页抓包的本质差别
很多人第一反应是用 Fiddler 或 Charles 抓包,这在小程序场景下会翻车。原因在于微信小程序的网络请求走的是微信自己的通信层,PC 端 Fiddler 默认只能看到 HTTP/HTTPS 的明文流量,而小程序的请求往往带证书校验,直接抓会看到一堆 CONNECT 隧道或者干脆空白。更关键的是,小红书小程序的接口参数(比如shield、x-sign这类签名)是在客户端侧动态生成的,你抓到了 URL 也未必能复现请求。
mitmdump 的优势在于它是 mitmproxy 的命令行版本,支持用 Python 脚本实时处理流量。这意味着你可以在流量经过代理的瞬间,用脚本修改请求头、提取响应体、甚至动态计算签名参数。对于小红书这种参数加密的平台,mitmdump 的脚本能力比 Fiddler 的图形化断点强太多。常见做法是:手机端设置代理指向跑 mitmdump 的机器,安装 mitmproxy 的 CA 证书,然后让脚本自动把匹配到的响应写入 CSV。
2.2 mitmdump 脚本的骨架和参数含义
下面是一个典型的 mitmdump 抓取脚本结构,对应压缩包里小红书微信小程序.py的核心逻辑。我把它拆成可复现的版本,你照着改域名和字段就能用:
# mitmdump -s xhs_capture.py -p 8080 from mitmproxy import http import csv import os import json CSV_PATH = "xhs_data.csv" # 目标接口特征,小红书的笔记列表/详情接口一般带这些路径关键词 TARGET_HOSTS = ["edith.xiaohongshu.com", "www.xiaohongshu.com"] TARGET_PATHS = ["/api/sns/web/v1/feed", "/api/sns/web/v1/note"] def response(flow: http.HTTPFlow): # 只处理目标域名和路径,避免把无关流量写进 CSV if not any(h in flow.request.host for h in TARGET_HOSTS): return if not any(p in flow.request.path for p in TARGET_PATHS): return try: data = json.loads(flow.response.text) except Exception: return # 从响应里提取笔记列表,字段名以实际接口为准 items = data.get("data", {}).get("items", []) if not items: return # 判断文件是否存在,决定是否写表头 file_exists = os.path.isfile(CSV_PATH) with open(CSV_PATH, "a", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) if not file_exists: writer.writerow(["note_id", "title", "user", "likes", "url"]) for it in items: note = it.get("note_card", {}) writer.writerow([ it.get("id", ""), note.get("display_title", ""), note.get("user", {}).get("nickname", ""), note.get("interact_info", {}).get("liked_count", ""), f"https://www.xiaohongshu.com/explore/{it.get('id', '')}" ])逻辑说明:response函数是 mitmdump 的钩子,每条响应都会进来。先按域名和路径过滤,避免把图片、静态资源也写进去。json.loads解析响应体,如果接口返回的不是 JSON 就跳过。file_exists判断是为了只在第一次写入时写表头,这就是标题里说的"判断不重复插入头信息"——不是去重数据,而是去重表头。参数方面,-p 8080是代理端口,-s指定脚本,CSV_PATH可以改成绝对路径方便后续处理。
2.3 手机端代理配置和证书安装
脚本跑起来只是第一步,手机端得把流量导过来。步骤是:手机和电脑连同一个 Wi-Fi,手机 Wi-Fi 设置里手动代理填电脑 IP 和 8080 端口;然后浏览器访问mitm.it下载对应平台的证书,iOS 需要在"关于本机-证书信任设置"里手动信任,Android 7 以上需要把证书装到系统证书区(这一步通常要 root,或者用模拟器)。装完证书后打开小红书小程序,刷几条笔记,终端里就能看到 mitmdump 打印的请求日志,CSV 也会开始写入。
注意:小红书对代理流量有检测,部分版本会直接拒绝请求。如果发现小程序加载不出来,先关掉代理确认网络正常,再检查证书是否装对。
3. CSV 实时落库与去重逻辑:表头判断和字段映射
3.1 为什么用 CSV 而不是直接入库
这个包选择 CSV 作为落库格式,不是偷懒,而是有实际考量。抓取阶段数据量不确定,字段也可能随接口调整,CSV 的 schema 灵活,用 Excel 或 pandas 打开就能看,不需要提前建表。而且 CSV 是纯文本,方便后续用脚本做二次清洗。常见做法是先用 CSV 落地,确认字段稳定后再导入 SQLite 或 MySQL。
但 CSV 有个坑:追加写入时如果每次都写表头,文件就会变成"表头-数据-表头-数据"的混乱结构。压缩包里提到的"判断不重复插入头信息"就是解决这个问题的。上面的代码用os.path.isfile判断文件是否存在,只有第一次才写表头。更稳妥的做法是用一个单独的标记文件,或者检查文件大小是否为零:
# 更稳妥的表头判断:文件不存在或大小为 0 才写表头 write_header = (not os.path.exists(CSV_PATH)) or os.path.getsize(CSV_PATH) == 0 with open(CSV_PATH, "a", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) if write_header: writer.writerow(["note_id", "title", "user", "likes", "url"]) # ... 写入数据行utf-8-sig这个编码很关键,它会在文件开头加 BOM,这样 Excel 打开中文不会乱码。如果你用 pandas 读取,pd.read_csv(CSV_PATH, encoding="utf-8-sig")也能正确解析。
3.2 数据去重的两种策略
抓取过程中重复数据很常见,比如同一条笔记在推荐流和搜索结果里都出现。去重有两种做法:一是在写入前用内存集合记录已见过的note_id,二是写入后用 pandas 去重。内存集合适合单次会话,pandas 适合事后清洗:
import pandas as pd # 事后去重:按 note_id 保留第一条 df = pd.read_csv("xhs_data.csv", encoding="utf-8-sig") df.drop_duplicates(subset=["note_id"], keep="first", inplace=True) df.to_csv("xhs_data_dedup.csv", index=False, encoding="utf-8-sig") print(f"去重前 {len(df)} 条,去重后 {len(df.drop_duplicates(subset=['note_id']))} 条")subset指定按哪列判重,keep="first"表示保留第一次出现的记录。如果要做增量抓取,可以在写入前先读一遍已有 CSV 的note_id集合,新数据里如果note_id已存在就跳过。这个逻辑在小红书微信小程序.py里通常会有体现,具体实现看 README 的说明。
3.3 字段映射和接口变动应对
小红书的接口字段名会变,今天叫display_title,明天可能改成title。脚本里最好做一层兼容:
def pick(d, *keys, default=""): """从字典里按优先级取第一个存在的 key""" for k in keys: if k in d and d[k]: return d[k] return default title = pick(note, "display_title", "title", "desc")这样接口小改不用重写脚本。标签.txt和资源内容.txt里如果有关键词列表,可以加载进来做过滤,只抓包含特定标签的笔记,减少无关数据。
4. 百度快照兜底与常见问题排查
4.1 百度快照抓取的适用场景
压缩包里有个"小红书百度快照抓取"模块,这不是主力方案,而是兜底。当小程序接口抓不到(比如风控升级、证书校验加强)时,百度快照里可能还保留着笔记的文本内容。快照抓取的逻辑是构造百度搜索 URL,解析搜索结果页里的快照链接,再请求快照页面提取正文。这个方案的局限很明显:快照更新滞后,图片和视频拿不到,只适合做文本层面的补充。常见做法是用它来补全标题和正文,互动数据还是得靠接口。
4.2 抓包过程中的五个血泪坑
现象一:mitmdump 启动后手机无法上网。原因通常是手机代理 IP 填错,或者电脑防火墙拦了 8080 端口。解决:确认电脑和手机在同一网段,ifconfig或ipconfig查电脑 IP,防火墙临时关闭测试。
现象二:小程序提示"网络异常",但浏览器能打开网页。这是证书没装好或没信任。iOS 要在"设置-通用-关于本机-证书信任设置"里打开 mitmproxy 证书的开关;Android 高版本需要把证书放到系统证书目录,或者用模拟器。
现象三:CSV 里中文乱码。编码问题,写入时用utf-8-sig,读取时也用同样编码。如果已经乱码了,用iconv或 pandas 重新编码。
现象四:抓到的响应是加密的或者空的。小红书部分接口返回的是加密体,需要客户端解密。这种情况脚本层面解决不了,得看README.md里有没有说明解密方式,或者换用百度快照兜底。
现象五:CSV 表头重复出现。就是前面说的表头判断问题,检查os.path.isfile或getsize逻辑,确保只在文件为空时写表头。
4.3 抓取频率和合规边界
抓取频率太高会触发风控,表现为返回 461、471 状态码或者直接封 IP。建议在脚本里加延时:
import time import random # 在每次写入后随机休眠,模拟人工浏览节奏 time.sleep(random.uniform(1.5, 4.0))这个延时加在response函数末尾,或者用 mitmdump 的--set delay参数。另外,抓取的数据自己分析用没问题,但别公开传播或商用,这是基本的边界。
5. 从 CSV 到分析:pandas 清洗和标签过滤的进阶技巧
抓到 CSV 只是开始,真正有价值的是后续分析。我一般会先用 pandas 做一轮清洗,把点赞数从"1.2万"这种字符串转成数值,再按标签过滤:
import pandas as pd import re df = pd.read_csv("xhs_data.csv", encoding="utf-8-sig") # 把 "1.2万" "3456" 统一转成整数 def parse_count(s): s = str(s).strip() if "万" in s: return int(float(s.replace("万", "")) * 10000) try: return int(s) except ValueError: return 0 df["likes_num"] = df["likes"].apply(parse_count) # 加载标签文件做过滤 with open("标签.txt", "r", encoding="utf-8") as f: tags = [line.strip() for line in f if line.strip()] # 标题里包含任一标签的留下 pattern = "|".join(re.escape(t) for t in tags) df_filtered = df[df["title"].str.contains(pattern, na=False)] df_filtered.to_csv("xhs_filtered.csv", index=False, encoding="utf-8-sig") print(f"过滤后剩余 {len(df_filtered)} 条")parse_count处理了中文数量单位,re.escape防止标签里有正则特殊字符。标签.txt的格式一般是一行一个词,如果你的文件是逗号分隔的,改成split(",")就行。
验证抓取是否完整,可以对比小程序里实际刷到的笔记数和 CSV 行数。如果差距大,检查是不是有分页接口没覆盖到,或者部分响应被过滤掉了。我习惯在脚本里加一个计数器,每写入一条就打印一次,这样能实时看到抓取进度。
从那以后我每次跑抓取脚本,都会先拿 10 条数据做小样本验证,确认字段映射、编码、去重逻辑都对了,再放开量跑。这个习惯帮我省了很多事后清洗的麻烦。希望帮到你。
本文还有配套的精品资源,点击获取