4. HTTP请求头:User-Agent、Referer、Cookie、Authorization等核心请求头实战

说实话,很多刚入行的爬虫工程师,写出来的代码能跑,但一上生产环境就崩。为什么?多半是请求头没处理好。服务器端反爬的第一道防线,就是检查你的请求头是否「像个人」。

我做了这么多年爬虫,踩过最大的坑就是——明明代码逻辑全对,却被对方服务器403拦在外面。后来才发现,是User-Agent暴露了我。今天咱们就把这几个核心请求头彻底讲透。

4.1 User-Agent:你的爬虫身份证

User-Agent(简称UA),说白了就是告诉服务器「我是谁」。浏览器会发一串很长的字符串,标明自己的版本、操作系统、内核等信息。爬虫如果不伪装,服务器一眼就能看出来。

核心原则:永远不要用默认的Python-urllib/3.x或Scrapy/2.x这种UA。那是告诉对方「快来封我」。

我个人的习惯是维护一个UA池,每次请求随机切换。这样即使某个UA被拉黑,也不影响整体采集。

# 一个简单的UA池示例
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/119.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"
]

import random
headers = {"User-Agent": random.choice(USER_AGENTS)}

小技巧:移动端UA往往比PC端UA更宽松。很多网站对移动端限制较少,遇到难啃的站点,试试换成iPhone或Android的UA。

4.2 Referer:告诉服务器你从哪来

Referer(注意拼写,HTTP标准里就是少了一个R)表示当前请求是从哪个页面跳转过来的。很多网站会校验这个字段,比如图片防盗链、表单提交来源验证。

我记得有一次爬一个电商网站的商品详情页,直接请求商品接口总是返回空数据。折腾了半天,发现对方校验了Referer——必须从商品列表页跳转过来才行。加上Referer后,数据就正常了。

场景 Referer设置 说明
直接访问首页 不设置或设为空 模拟用户直接输入网址
翻页请求 上一页的URL 模拟用户点击下一页
图片/文件下载 包含该资源的页面URL 绕过防盗链检查
表单提交 表单所在页面URL 防止CSRF校验失败
# 模拟从商品列表页进入详情页
headers = {
    "User-Agent": "Mozilla/5.0 ...",
    "Referer": "https://example.com/products?page=1"
}
response = requests.get("https://example.com/product/12345", headers=headers)

注意:有些网站会校验Referer的域名是否合法。如果Referer域名和当前请求域名不匹配,可能会被拒绝。这时候需要把Referer设为同域名下的某个页面。

4.3 Cookie:维持会话的钥匙

Cookie是服务器留在你浏览器里的小纸条。每次请求时浏览器都会带上它,告诉服务器「我还记得你」。对于爬虫来说,Cookie管理是重中之重。

我遇到过最典型的情况:登录后才能看到的数据,直接用requests.get()拿不到。为什么?因为没有带上登录后的Cookie。

Cookie的获取方式主要有三种:

  1. 手动复制:浏览器登录后,从开发者工具里复制Cookie字符串
  2. 自动获取:先用requests.Session()模拟登录,自动保存Cookie
  3. 持久化存储:把Cookie保存到文件或数据库,下次启动时加载
# 使用Session自动管理Cookie
session = requests.Session()

# 先登录
login_data = {"username": "your_account", "password": "your_password"}
session.post("https://example.com/login", data=login_data, headers=headers)

# 后续请求自动携带Cookie
response = session.get("https://example.com/user/profile", headers=headers)

避坑指南:我曾经直接把浏览器里的Cookie字符串硬编码到代码里,结果第二天就失效了。后来改用Session自动管理,配合定时刷新登录状态,才彻底解决这个问题。

4.4 Authorization:访问受保护资源的通行证

Authorization头主要用于API鉴权。现在大多数Web API都采用Token认证方式,最常见的就是Bearer Token和JWT。

你想想看,如果每次请求都要带上用户名密码,那多不安全。所以现在流行用Token——登录时拿一次,后续请求都带这个Token就行。

认证方式 Authorization值示例 适用场景
Basic Auth Basic base64(username:password) 简单内部API
Bearer Token Bearer eyJhbGciOiJIUzI1NiIs... RESTful API
Digest Auth Digest realm="...", nonce="..." 旧系统兼容
API Key ApiKey your-api-key-here 第三方开放平台
# Bearer Token认证示例
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
headers = {
    "Authorization": f"Bearer {token}",
    "User-Agent": "Mozilla/5.0 ..."
}
response = requests.get("https://api.example.com/user/info", headers=headers)

经验之谈:Token一般有过期时间。我习惯在代码里加一个Token过期检测,如果返回401就自动重新登录获取新Token,避免手动干预。

4.5 请求头组合实战:一个完整的采集流程

单个请求头好处理,难的是把它们组合起来。下面这张图展示了我在实际项目中常用的请求头配置流程:

请求头配置核心流程 初始化请求头字典 随机选取User-Agent 判断是否需要Referer 设置Referer值 加载/管理Cookie 添加Authorization(如需) 关键点 • 顺序不重要,但缺一不可 • 每个请求头都要动态生成 • 不要硬编码任何值

下面是一个完整的实战代码示例,把上面四个请求头整合到一起:

import requests
import random
import time

class SmartCrawler:
    def __init__(self):
        self.session = requests.Session()
        self.token = None
        self.ua_list = [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
            # 更多UA...
        ]

    def get_headers(self, referer=None):
        headers = {
            "User-Agent": random.choice(self.ua_list),
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
            "Accept-Encoding": "gzip, deflate, br",
            "Connection": "keep-alive"
        }
        if referer:
            headers["Referer"] = referer
        if self.token:
            headers["Authorization"] = f"Bearer {self.token}"
        return headers

    def fetch_with_cookies(self, url, referer=None):
        headers = self.get_headers(referer)
        # 使用session自动管理Cookie
        response = self.session.get(url, headers=headers, timeout=10)
        return response

    def login_and_get_token(self, login_url, credentials):
        # 模拟登录获取Token
        headers = self.get_headers()
        resp = self.session.post(login_url, json=credentials, headers=headers)
        if resp.status_code == 200:
            self.token = resp.json().get("access_token")
            print("Token获取成功")
        return self.token

# 使用示例
crawler = SmartCrawler()
crawler.login_and_get_token("https://api.example.com/login", 
                            {"username": "user", "password": "pass"})
data = crawler.fetch_with_cookies(
    "https://api.example.com/data",
    referer="https://example.com/dashboard"
)

核心总结:User-Agent解决「你是谁」,Referer解决「你从哪来」,Cookie解决「你还记得我吗」,Authorization解决「你有权限吗」。四个请求头配合使用,才能让服务器觉得你是一个真实的、合法的用户。

嗯,这里要注意一点:不要一次性把所有请求头都加上。有些网站会检测请求头数量,正常的浏览器不会发那么多自定义头。我一般只加必要的几个,够用就行。

说白了,请求头配置就是一场猫鼠游戏。你伪装得越像真实用户,服务器就越难识别你。但也要记住,反爬技术也在不断进化,没有一劳永逸的方案。保持学习,保持更新,才是爬虫工程师的生存之道。