爬虫反爬策略与应对方案实战指南

详解 User-Agent、IP 代理、验证码等常见反爬机制及破解方法

一、反爬与反反爬的博弈

做爬虫这几年,最大的感受就是:爬虫和反爬是一场永无止境的博弈。你写了一个能用的爬虫,过两天就不能用了;你升级了反爬策略,对方又找到了新的突破口。

这篇文章把我遇到的常见反爬机制整理出来,不是为了教你怎么攻破别人的网站,而是帮你理解这些机制的原理,在合法合规的前提下完成数据采集任务。

重要声明
  • 本文仅供学习交流,请勿用于非法用途
  • 采集数据前请阅读网站的 robots.txt 和服务条款
  • 控制请求频率,不要对目标服务器造成过大压力
  • 尊重数据版权,不要用于商业盈利

二、User-Agent 检测

这是最简单也是最基础的反爬手段。服务器通过检查 User-Agent 来判断请求是否来自真实浏览器。

2.1 反爬原理

很多爬虫框架默认的 User-Agent 是这样的:

python-requests/2.28.1 Python-urllib/3.9

服务器一看就知道这是程序发的请求,直接返回 403 或者跳转到验证码页面。

2.2 应对方案:使用真实浏览器的 User-Agent

import random # 准备一个 User-Agent 池 user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15', ] headers = { 'User-Agent': random.choice(user_agents) }

2.3 进阶方案:随机轮换

class UARotator: def __init__(self): self.ua_list = [ # Chrome on Windows 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', # Chrome on Mac 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', # Firefox on Windows 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0', # Safari on Mac 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15', # Edge on Windows 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0', ] def get_ua(self): return random.choice(self.ua_list) # 使用 ua_rotator = UARotator() headers = {'User-Agent': ua_rotator.get_ua()}
获取最新 UA:访问 whatismybrowser.com 可以获取最新版本的浏览器 User-Agent。

三、IP 频率限制

这是最常见的反爬手段。服务器会统计每个 IP 的请求频率,如果超过阈值就封禁。

3.1 反爬原理

服务器通常有以下几种限制策略:

  • 固定时间窗口:每分钟最多 60 次请求
  • 滑动时间窗口:任意 60 秒内最多 60 次请求
  • 令牌桶:每秒生成固定数量的令牌,请求消耗令牌

3.2 应对方案:控制请求频率

import time import random def fetch_with_delay(url, min_delay=1, max_delay=3): """带随机延时的请求""" response = requests.get(url) # 随机延时 1-3 秒 time.sleep(random.uniform(min_delay, max_delay)) return response # 批量采集 for i in range(100): response = fetch_with_delay(f'https://api.example.com/page/{i}') print(f"第 {i+1} 页采集完成")

3.3 应对方案:使用代理 IP

import random # 代理池 proxy_pool = [ {'http': 'http://proxy1.example.com:8080', 'https': 'http://proxy1.example.com:8080'}, {'http': 'http://proxy2.example.com:8080', 'https': 'http://proxy2.example.com:8080'}, {'http': 'http://proxy3.example.com:8080', 'https': 'http://proxy3.example.com:8080'}, ] def get_proxy(): return random.choice(proxy_pool) # 使用代理发送请求 proxy = get_proxy() response = requests.get('https://api.example.com', proxies=proxy)

3.4 代理类型对比

代理类型 优点 缺点 适用场景
免费代理 免费 不稳定、速度慢、存活时间短 测试、学习
共享代理 价格便宜 多人共用,可能被封 小规模采集
独享代理 稳定、速度快 价格较高 商业项目
住宅代理 模拟真实用户、难被封 价格最贵 高难度反爬

很多网站会在第一次访问时设置 Cookie,后续请求必须带上这个 Cookie 才能正常访问。

4.1 反爬原理

典型的流程是:

  1. 用户第一次访问,服务器返回设置 Cookie 的响应
  2. 浏览器自动保存 Cookie,下次请求带上
  3. 服务器验证 Cookie 的合法性
  4. 如果 Cookie 缺失或无效,返回错误页面

4.2 应对方案:使用 Session

import requests session = requests.Session() # 第一次请求:获取 Cookie session.get('https://example.com') # 后续请求自动带上 Cookie response = session.get('https://example.com/data') print(response.text)

4.3 应对方案:手动设置 Cookie

# 从浏览器复制 Cookie cookies = { 'session_id': 'abc123', 'user_id': '456', 'csrf_token': 'xyz789' } response = requests.get('https://example.com', cookies=cookies)

五、验证码对抗

验证码是最让人头疼的反爬手段。不过验证码也分很多种,难度各不相同。

5.1 验证码类型

类型 特点 破解难度
图形验证码 数字、字母组合 ⭐⭐ 可用 OCR
滑块验证码 拖动滑块到正确位置 ⭐⭐⭐ 需要图像识别
点选验证码 点击指定文字或物体 ⭐⭐⭐⭐ 较难
reCAPTCHA Google 验证 ⭐⭐⭐⭐⭐ 极难

5.2 图形验证码识别

from PIL import Image import pytesseract def recognize_captcha(image_path): """识别图形验证码""" # 打开图片 image = Image.open(image_path) # 转为灰度图 image = image.convert('L') # 二值化处理 threshold = 128 table = [0 if i < threshold else 1 for i in range(256)] image = image.point(table, '1') # OCR 识别 text = pytesseract.image_to_string(image) return text.strip() # 使用 result = recognize_captcha('captcha.png') print(f"识别结果: {result}")

注意:验证码识别的准确率通常不是 100%,建议结合重试机制使用。对于高难度的验证码,建议使用第三方打码平台。

六、JavaScript 动态渲染

现在很多网站用 Vue、React 这些前端框架,页面内容是 JavaScript 动态加载的。直接用 requests 拿到的 HTML 是空的,看不到实际数据。

6.1 反爬原理

典型的单页应用(SPA)流程:

  1. 浏览器请求 HTML,得到一个几乎为空的页面框架
  2. 浏览器执行 JavaScript,发起 AJAX 请求获取数据
  3. JavaScript 将数据渲染到页面上

6.2 应对方案:分析数据接口

最直接的方法是找到 JavaScript 调用的数据接口,直接请求接口:

# 1. 在浏览器开发者工具中找到数据接口 # 2. 复制 curl 命令 # 3. 用 curl 转 Python 工具转换 # 4. 直接请求接口 import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept': 'application/json', } # 直接请求数据接口 response = requests.get('https://api.example.com/data', headers=headers) data = response.json() print(data)

6.3 应对方案:使用 Selenium

from selenium import webdriver from selenium.webdriver.chrome.options import Options # 配置 Chrome chrome_options = Options() chrome_options.add_argument('--headless') # 无头模式 chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-dev-shm-usage') # 启动浏览器 driver = webdriver.Chrome(options=chrome_options) # 访问页面 driver.get('https://example.com') # 等待页面加载完成 import time time.sleep(3) # 获取渲染后的页面内容 html = driver.page_source print(html) # 关闭浏览器 driver.quit()

七、参数签名验证

这是目前最流行的反爬手段之一。服务器要求请求参数必须带有签名,签名由特定算法生成,防止参数被篡改。

7.1 反爬原理

典型的签名流程:

  1. 客户端收集所有请求参数
  2. 按照特定规则拼接成字符串
  3. 使用密钥和加密算法生成签名
  4. 将签名附加到请求中
  5. 服务器用同样的方式验证签名

7.2 应对方案:逆向分析

参考本站的两篇实战文章:

7.3 简单示例

import hashlib import time def generate_sign(params, secret_key): """生成参数签名""" # 1. 按键排序 sorted_params = sorted(params.items()) # 2. 拼接成字符串 param_str = '&'.join([f'{k}={v}' for k, v in sorted_params]) # 3. 拼接密钥 sign_str = f'{param_str}&key={secret_key}' # 4. MD5 加密 sign = hashlib.md5(sign_str.encode()).hexdigest() return sign # 使用 params = { 'page': '1', 'size': '20', 'timestamp': str(int(time.time())) } secret_key = 'your_secret_key' sign = generate_sign(params, secret_key) params['sign'] = sign response = requests.get('https://api.example.com', params=params)

八、行为检测

高级反爬系统会分析用户的行为模式,比如鼠标轨迹、点击位置、页面停留时间等。

8.1 检测维度

  • 请求间隔:人类操作有随机性,程序请求通常很规律
  • 访问路径:人类会浏览多个页面,程序直奔目标
  • 鼠标轨迹:人类移动鼠标有曲线,程序是直线
  • 页面停留:人类会花时间阅读,程序秒开秒关

8.2 应对方案:模拟人类行为

import time import random def human_like_delay(): """模拟人类的随机延时""" # 正态分布,平均 3 秒,标准差 1 秒 delay = random.gauss(3, 1) delay = max(1, delay) # 最少 1 秒 time.sleep(delay) def random_scroll(): """模拟随机滚动""" scroll_amount = random.randint(100, 500) # 在 Selenium 中使用 # driver.execute_script(f'window.scrollBy(0, {scroll_amount})') def random_click(): """模拟随机点击""" # 随机选择页面元素点击 pass # 采集流程 for url in url_list: # 随机延时 human_like_delay() # 访问页面 response = requests.get(url) # 模拟滚动 random_scroll() # 再延时 human_like_delay()

九、总结

反爬机制越来越复杂,但核心思路是不变的:让服务器认为你是一个正常的人类用户。

反爬机制 核心思路 应对策略
User-Agent 识别爬虫特征 使用真实浏览器的 UA,定期更新
IP 限制 限制请求频率 控制延时、使用代理池
Cookie 验证会话状态 使用 Session、手动设置 Cookie
验证码 区分人机 OCR 识别、打码平台、避免触发
JS 渲染 隐藏数据接口 分析接口、使用 Selenium
参数签名 防止参数篡改 逆向分析、还原算法
行为检测 分析操作模式 模拟人类行为、随机化操作

最后再说一次:技术是中立的,关键在于怎么用。遵守规则、控制频率、尊重版权,才能长久地做下去。