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的获取方式主要有三种:
- 手动复制:浏览器登录后,从开发者工具里复制Cookie字符串
- 自动获取:先用requests.Session()模拟登录,自动保存Cookie
- 持久化存储:把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 请求头组合实战:一个完整的采集流程
单个请求头好处理,难的是把它们组合起来。下面这张图展示了我在实际项目中常用的请求头配置流程:
下面是一个完整的实战代码示例,把上面四个请求头整合到一起:
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解决「你有权限吗」。四个请求头配合使用,才能让服务器觉得你是一个真实的、合法的用户。
嗯,这里要注意一点:不要一次性把所有请求头都加上。有些网站会检测请求头数量,正常的浏览器不会发那么多自定义头。我一般只加必要的几个,够用就行。
说白了,请求头配置就是一场猫鼠游戏。你伪装得越像真实用户,服务器就越难识别你。但也要记住,反爬技术也在不断进化,没有一劳永逸的方案。保持学习,保持更新,才是爬虫工程师的生存之道。