四条取页链路对抗不同强度反爬,选择器失效后还能靠特征库把数据找回来。
一个 URL 进,一份干净文件出。纯 HTTP、无头浏览器、过盾、真 Firefox 四条链路按反爬强度递增,输出 Markdown / 纯文本 / 结构化 JSON 任选。选择器改版失效后,靠存过的元素指纹把同一批数据原样找回。
采集网页时,最容易被忽略的事实是:状态码都是 200,拿到的内容可能完全是空的。
| 页面类型 | 用纯 HTTP 请求的结果 | 实际需要 |
|---|---|---|
| 静态 HTML | 拿到全部数据,0.75 秒 | 纯 HTTP 就够,开浏览器是浪费 |
| JS 动态渲染 | HTTP 200,抓到 0 条 —— 只是空壳 | 必须真跑浏览器渲染 |
| 带访问限制(如人机验证) | 403,正文仅 58 字「Just a moment…」 | 需要过盾链路 |
所以这个工具不做「一种万能抓法」,而是把四条链路摆出来,按目标站点的防护强度选一条;并且把「这次拿到的到底是不是真内容」变成可验证的判断,而不是靠状态码猜。
http 纯请求 · 最快|browser 无头浏览器 · 能跑 JS|stealth 隐身内核 · 过人机验证|firefox Firefox 内核 · 换指纹
防护强度从左到右递增,耗时也是。同一批目标四种链路全部实测跑通,每种都有原始输出留档。
| 采集岗常见要求 | 这个项目里的对应证据 |
|---|---|
| 能写采集程序(Python) | 单文件 CLI scrape.py(224 行):参数化选择器、列表项 + 字段抽取、按后缀决定输出格式、全程类型注解 |
| 熟悉网络数据获取原理,能应对各类数据源访问限制 | 四条链路各自的适用边界都用对照实验划清了:JS 页 HTTP 拿 0 条 / 浏览器拿 10 条;人机验证 HTTP 403 58 字 / 隐身链路 200 拿到 1588 字。并排掉了一个非浏览器层面的坑(见下方排障案例) |
| 数据清洗、结构化转换 | 三种输出形态按需切换:Markdown 正文 / 纯文本 / 结构化 JSON;-i 指定列表项 + -f 指定字段,一次抽出整批记录 |
| 采集任务运维、数据源台账与规范沉淀 | 自适应特征库(SQLite):--learn 存一次元素特征,站点改版后 --relocate 把数据找回来;库按注册域分区,同一站点改版前后自动接上 |
全部为本机实测,产物留档。
一、JS 渲染页必须有浏览器 —— 最有区分力的一条对照:
| 链路 | 状态码 | 抓到条目 | 耗时 |
|---|---|---|---|
| 纯 HTTP(伪装请求头) | 200 | 0 条 | 0.73 s |
| 无头浏览器渲染 | 200 | 10 条 | 2.71 s |
状态码完全一样,只有数条数才知道 HTTP 那趟拿到的是空壳。
二、隐身链路确实能过人机验证:
| 链路 | 结果 | 正文 | 耗时 |
|---|---|---|---|
| 纯 HTTP | 403 | 58 字(拦截提示页) | 快 |
| 隐身链路(自动解验证) | 200 | 1588 字(真实内容) | 11.4 s |
三、选择器自愈是真的 —— 把页面里 10 处 class="quote" 改成 class="card item",等价于网站重构了 CSS:
| 做法 | 命中 | 结果 |
|---|---|---|
| 改版后照旧用原选择器 | 0 个 | ——(证明改版确实让选择器失效) |
| 同一份页面 + 自适应找回 | 10 个 | 找回的文本与改版前逐字一致 |
四、四条链路全过(复跑):
| 链路 | 目标类型 | 结果 |
|---|---|---|
http | 静态列表页 | 200 · 1.5 s · 20 条 |
browser | JS 渲染页 | 200 · 4.0 s · 10 条 |
stealth | 带访问限制的页面 | 200 · 16.2 s · 26565 字 Markdown |
firefox | 同上 JS 渲染页(换内核) | PASS · 5.1–6.5 s · 10 条 · 内核 Firefox/155.0 |
现象:同一个页面,纯请求 1.6 秒拿到 200,Chromium 链路 4 秒拿到 10 条,换成 Firefox 内核却 120 秒超时、条目恒为 0。看起来像「Firefox 不能用」。
实际链条:本机浏览器内核版本引入了一项局域网访问管控 → 页面样式表里的一条外链字体请求被拒 → 那条请求一直挂着 → 样式没建完 → 页面里后置的脚本按规范必须等样式表 → 页面加载事件始终不触发 → 选择器自然一条都命不中。
没有去改系统设置,而是用单变量 A/B 定位到那一个开关:
| 组 | 改动 | 结果 |
|---|---|---|
| A · 默认 | 无 | 25 s 超时 · 0 条 · 页面停在加载中 |
| B · 只关掉这一个开关 | 一个浏览器偏好 | 3.0 s 返回 · 10 条 · 状态正常 |
同时保留了一个兜底开关:把解析到内网地址的子资源直接掐掉,让它快速失败而不是干等。两种修法跑同一个页面,出的 10 条数据逐字段相等。
这个案例体现的不是「会用某个库」,而是:把「工具报错」一路查到「浏览器内核行为 + 页面加载规范」这一层,再用单变量实验确认因果。
做对了的
踩过并写进文档的坑
运行环境:Python 3.13 + uv 虚拟环境;浏览器内核复用本机已有构建,未重复下载。
本页所有数字均来自项目内的实测产物,可逐项复核;不含任何真实账号凭据、个人隐私信息。演示与实验使用的目标站点均为公开的采集练习站点与公开演示页。对外展示版本不含任何具体业务站点的适配实现。