爬虫类型及反爬措施

100 阅读7分钟

反爬总原则

目标不是“完全防住”,而是“提高爬取成本”到对方放弃

  • 监控:先埋点日志,识别异常流量,再针对性防御
  • 用户体验优先:避免对正常用户造成干扰
  • 渐进式防御:从低成本措施开始(如 Headers 校验),再叠加高阶方案(如行为分析)

主流的爬虫方式

效率、稳定性和开发成本:API → 静态 → 逆向 → RPA

方式核心思想
1. 静态解析“URL 有规律,直接拼接抓”
2. 调用 API“绕过前端,直取数据源”
3. RPA 自动化“像人一样操作浏览器”
4. JS 逆向“破解前端加密,伪造合法请求”
5. 无限滚动“要么抓 API,要么自动滚”
6. 混合爬取“浏览器探路,HTTP 冲刺”

方式 1:静态 URL 遍历 + HTML 解析

适用:传统服务端渲染网站,分页通过 ?page=1 或 /page/2/ 实现

核心思路

  • 分页由 URL 控制,每一页是独立 HTML 页面
  • 直接构造所有分页 URL → 下载 → 解析 → 提取数据

实现步骤

  1. 分析分页规律

    • 观察 URL:https://example.com/news?page=1 → page=2page=3...
    • 或路径:/news/page/1/ → /page/2/
  2. 确定总页数(可选)

    • 从第一页解析出“共 50 页”或“总条数 1000 条,每页 20 条 → 共 50 页”
  3. 循环请求每一页

  4. 用 HTML 解析器提取目标数据

方式 2:直接调用后端分页 API

适用:前端通过 AJAX 加载数据(如 React/Vue 应用)

核心思路

  • 网页内容由 JavaScript 调用 API 获取
  • 绕过前端,直接模拟浏览器向 API 发请求

实现步骤

  1. 打开浏览器开发者工具(F12)
  2. 点击“下一页”,观察 Network → XHR/Fetch 请求
  3. 找到分页 API 的 URL 和参数(如 page=2limit=20
  4. 复制请求头(Headers),特别是 CookieAuthorizationX-Requested-With
  5. 用代码模拟该请求,循环翻页

方式 3:RPA / 浏览器自动化翻页

适用:无 API、JS 渲染复杂、需模拟点击的页面

核心思路

  • 启动真实浏览器(无头模式)
  • 自动点击“下一页”按钮或滚动加载
  • 等待新内容出现后,从 DOM 中提取数据

实现步骤

  1. 启动 Playwright/Selenium 浏览器

  2. 打开目标页面

  3. 循环:

    • 提取当前页数据
    • 查找并点击“下一页”按钮(或滚动到底部)
    • 等待新内容加载(用 wait_for_selector
  4. 直到“下一页”按钮消失或不可点击

方式 4:JS 逆向 + 参数生成

适用:API 请求含加密参数(如 sign、X-Bogus、msToken)

核心思路

  • 前端 JS 动态生成请求参数
  • 通过调试还原算法,用代码复现参数生成逻辑

实现步骤

  1. 在 DevTools 中定位分页请求
  2. 查看 Initiator(发起者),找到 JS 文件和函数
  3. 设置断点,观察参数如何生成(如 sign = md5(url + timestamp + secret)
  4. “扣代码”:复制关键 JS 函数
  5. 用 execjs 或重写为 Python 函数
  6. 每次请求前动态生成合法参数

方式 5:无限滚动模拟

适用:信息流页面(如微博、小红书),滚动自动加载

核心思路

  • 优先方案:抓包找到滚动触发的 API,直接调用
  • 备选方案:用浏览器自动滚动,监听 DOM 变化

实现步骤(推荐 API 方式)

  1. 滚动页面,观察 Network 中新增的 XHR 请求
  2. 找到类似 /api/feed?offset=20&count=20 的接口
  3. 循环增加 offset 值,直到返回空数据

方式 6:混合智能爬取

适用:复杂生产环境,需兼顾效率与鲁棒性

核心思路

  • 前期用浏览器:登录、获取 Token、探测 API
  • 后期用 HTTP 客户端:高效批量请求
  • 失败时回退到浏览器重试

实现步骤

  1. 用 Playwright 登录,获取 Cookie 和 Token
  2. 从页面中提取 API 地址和参数模板
  3. 后续分页全部用 requests 调用 API
  4. 若某页失败(如 403),用浏览器重新获取 Token 并重试

防御方法

防御「静态 URL 遍历 + HTML 解析」

步骤操作技术实现
1.1 埋点监控记录所有分页请求的 User-AgentIPReferer、访问频率Nginx 日志 + ELK 或自建日志系统
1.2 拒绝无 Referer 请求要求分页请求必须来自本站Nginx 配置:if ($http_referer !~* "^https://yourdomain.com") { return 403; }
1.3 检查 User-Agent拒绝常见爬虫 UA(如 python-requests后端中间件(Python/Node.js/Java):python<br>if 'python' in request.headers.get('User-Agent', '').lower():<br> return 403<br>
1.4 IP 频率限制同一 IP 每分钟最多 20 次分页请求使用 Redis 计数:bash<br>INCR user:ip:1.2.3.4<br>EXPIRE user:ip:1.2.3.4 60<br>超过阈值返回 429
1.5 动态分页 token每次渲染页面时生成一次性 token,下一页需携带后端模板中嵌入:<a href="/page/2?token={{ next_token }}">下一页</a>服务端校验 token 有效性并单次使用

防御「直接调用后端分页 API」

步骤操作技术实现
2.1 强制登录态分页 API 必须携带有效 Session 或 JWT前端登录后,API 请求自动带 Cookie 或 Authorization: Bearer <token>
2.2 添加请求签名(Sign)对参数 + 时间戳 + 密钥生成签名前端 JS 示例:js<br>const sign = md5(`${params}&timestamp=${ts}&key=SECRET`);<br>后端用相同逻辑验证
2.3 校验关键 Headers要求包含 X-Requested-With: XMLHttpRequest 和 Origin后端中间件校验:python<br>if request.headers.get('X-Requested-With') != 'XMLHttpRequest':<br> abort(403)<br>
2.4 签名密钥动态化每 24 小时轮换 SECRET_KEY(通过配置中心下发)使用 Apollo/Nacos 管理密钥,前端通过安全通道获取(如登录后返回临时 key)
2.5 接口路径混淆API 路径加入时间戳或随机字符串如 /api/v1/list_20240615/,每日更新,旧路径 404

防御「RPA / 浏览器自动化翻页」

步骤操作技术实现
3.1 检测 WebDriver (W3C 标准的浏览器自动化控制协议)特征检查浏览器是否被自动化控制前端 JS 插入检测代码:```jsif (window.webdriver
3.2 收集浏览器指纹生成唯一设备 ID(Canvas + WebGL + 字体)使用开源库 FingerprintJSjs<br>const fp = await FingerprintJS.load();<br>const { visitorId } = await fp.get();<br>sendToServer(visitorId);<br>
3.3 绑定 Token 与指纹登录 Token 与设备指纹绑定,换环境失效后端存储:token → {user_id, device_fingerprint, expire}每次 API 请求校验指纹一致性
3.4 行为埋点上报鼠标移动、点击坐标、滚动速度前端监听事件:js<br>window.addEventListener('mousemove', e => log(e.clientX, e.clientY));<br>后端分析是否为“直线匀速”等机器人特征
3.5 关键内容延迟加载数据在用户停留 1.5 秒后才渲染前端:js<br>setTimeout(() => renderData(), 1500);<br>

防御「JS 逆向 + 参数生成」

步骤操作技术实现
4.1 JS 混淆压缩使用 Webpack + JavaScript Obfuscatorwebpack.config.js 配置:js<br>plugins: [new JavaScriptObfuscator({ rotateStringArray: true })]<br>
4.2 关键函数拆分将签名逻辑分散在多个文件,增加还原难度如 part1.js + part2.js + runtime.js 动态组合
4.3 环境检测检测是否在浏览器中执行在签名函数开头加:```jsif (typeof window === 'undefined'
4.4 使用 WebAssembly (WASM)将核心算法编译为 WASM用 Rust/C++ 编写签名逻辑 → 编译为 .wasm → 前端调用(极难逆向)
4.5 动态更新算法每周自动更换签名逻辑(通过 AB 测试灰度)构建多套签名方案,通过配置开关切换

防御「无限滚动模拟」

步骤操作技术实现
5.1 改用 Cursor 分页返回 next_cursor 而非 offsetAPI 响应:json<br>{ "items": [...], "next_cursor": "abc123xyz" }<br>下一页请求:?cursor=abc123xyz
5.2 限制最大数据量未登录用户最多返回 500 条后端逻辑:python<br>if not user.is_logged_in and offset > 500:<br> return []<br>
5.3 滚动行为验证要求前端上报滚动事件前端:js<br>let lastScroll = 0;<br>window.addEventListener('scroll', () => {<br> if (window.scrollY > lastScroll + 100) {<br> sendScrollEvent();<br> lastScroll = window.scrollY;<br> }<br>});<br>后端校验是否有真实滚动
5.4 数据脱敏对未授权用户隐藏关键字段如返回 { title: "xxx", content: "***需登录查看***" }

防御「混合智能爬取」

步骤操作技术实现
6.1 多维风控引擎综合 IP、设备、行为、频率打分自建规则引擎或接入第三方(如阿里云风险识别):- 高频请求:+30 分- 无鼠标轨迹:+20 分- 新设备:+10 分总分 > 50 → 弹验证码
6.2 Token 与设备强绑定登录后 Token 仅对该设备有效后端存储结构:{ token: "abc", device_hash: "fp123", user_id: 1001 }每次请求校验 device_hash
6.3 接口灰度发布对 1% 用户启用新 API 结构通过网关(如 Kong、Nginx)路由:if (user.id % 100 == 0) use_new_api()使通用爬虫大面积失效
6.4 设置蜜罐数据对可疑 IP 返回特殊“陷阱数据”如商品价格设为 999999,若该数据出现在竞品网站,即可取证