突破反爬封锁:Python requests高级请求头伪装实战指南

当你兴致勃勃地写好了爬虫脚本,运行后却只得到403 Forbidden的冰冷回应,这种挫败感每个爬虫开发者都深有体会。服务器是如何识破你的爬虫身份的?关键在于那些看似不起眼却至关重要的HTTP请求头。

1. 为什么你的爬虫总被识别?

每次浏览器访问网站时,都会自动发送一组包含设备、浏览器信息的请求头。而裸奔的requests.get()只发送最基础的头部信息,就像穿着睡衣去参加正式晚宴一样格格不入。

现代反爬系统会检查这些关键指标:

  • User-Agent:暴露客户端类型(Python-urllib/3.6)
  • Accept-Language:缺失或不符合常规浏览器设置
  • Connection:保持长连接的行为异常
  • Headers完整性:缺少常见但非必须的字段如Referer
# 典型被识别的爬虫请求
import requests
response = requests.get('https://example.com')
print(response.status_code)  # 很可能返回403

2. 构建完美伪装:请求头核心要素详解

2.1 User-Agent的进化论

User-Agent是请求头中的身份证,需要定期更新以匹配主流浏览器版本。以下是2023年常见浏览器的UA模板:

浏览器类型 示例UA字符串
Chrome Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Firefox Mac Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0
Safari iOS Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1

提示:避免使用包含"Python"、"Bot"、"Spider"等明显自动化工具标识的UA

2.2 必须配置的辅助头字段

完整的伪装需要这些关键字段协同工作:

headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
    "Pragma": "no-cache",
    "Upgrade-Insecure-Requests": "1",
    "Referer": "https://www.google.com/",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1"
}

3. 动态请求头生成策略

3.1 智能轮换User-Agent库

硬编码UA字符串不够灵活,建议使用以下方法动态生成:

from random import choice

DESKTOP_AGENTS = [
    # Chrome Windows
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{0}.0.{1}.{2} Safari/537.36",
    # Firefox Mac
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.{3}; rv:{4}.0) Gecko/20100101 Firefox/{4}.0"
]

def generate_ua():
    template = choice(DESKTOP_AGENTS)
    if "Chrome" in template:
        return template.format(
            randint(110, 118),  # Chrome主版本
            randint(1000, 9999),
            randint(100, 999)
        )
    else:
        return template.format(
            randint(12, 15),    # macOS版本
            randint(100, 115)   # Firefox版本
        )

3.2 自适应Referer策略

Referer字段需要根据目标网站结构动态生成:

def generate_referer(target_url):
    domain = urlparse(target_url).netloc
    if "amazon" in domain:
        return "https://www.amazon.com/"
    elif "twitter" in domain:
        return "https://twitter.com/"
    else:
        return f"https://www.google.com/search?q={quote(domain)}"

4. 针对不同平台的优化配置

4.1 电商平台专用配置

电商网站通常检查更严格的头部信息:

amazon_headers = {
    **base_headers,
    "Accept": "text/html,application/xhtml+xml",
    "Accept-Encoding": "gzip, deflate, br",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "X-Requested-With": "XMLHttpRequest"
}

4.2 社交媒体平台技巧

社交媒体需要模拟完整的浏览行为链:

  1. 先访问主页获取初始cookies
  2. 携带cookies访问登录页
  3. 保持会话一致性
  4. 定期更新X-CSRF-Token
session = requests.Session()
session.headers.update({
    "Origin": "https://twitter.com",
    "Content-Type": "application/x-www-form-urlencoded",
    "DNT": "1",
    "TE": "Trailers"
})

5. 高级伪装检测与反制

即使配置了完善的请求头,仍可能被识别,这时需要:

  • TCP指纹伪装:使用curl_cffi等库模拟浏览器TLS指纹
  • 浏览器自动化:在关键环节切换至Selenium/Puppeteer
  • 流量模式混淆:随机化请求间隔,模拟人类操作节奏
  • IP轮换策略:结合代理IP池分散请求来源
# 使用curl_cffi模拟浏览器指纹
from curl_cffi import requests as curl_requests

resp = curl_requests.get(
    "https://target.com",
    headers=headers,
    impersonate="chrome110"
)

真正的反爬对抗是持续的过程,需要定期更新你的伪装策略。某次项目中,我发现目标网站突然开始检查Client-Hints头字段,及时添加以下配置后恢复了采集:

headers.update({
    "Sec-CH-UA": '"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99"',
    "Sec-CH-UA-Mobile": "?0",
    "Sec-CH-UA-Platform": '"Windows"'
})
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐