1. 为什么爬虫需要伪装和会话管理
第一次用Requests库写爬虫时,我兴冲冲地直接请求知乎首页,结果返回的却是403 Forbidden。这个经历让我明白:现代网站都有完善的反爬机制,裸奔的爬虫连门都进不去。这就像穿着睡衣去高档餐厅——门卫根本不会让你进去。
HTTP协议中的Headers、Session和Cookie就是我们的"正装"。服务器通过检查这些信息来判断请求是否来自真实浏览器。以知乎为例,它的反爬系统会检查:
- User-Agent:判断客户端类型(手机/PC/爬虫)
- Referer:验证请求来源页面
- Cookie:维持登录状态和用户标识
- Accept-Language:识别用户语言偏好
提示:根据《网络安全法》和相关法规,爬取公开数据时务必遵守robots.txt规则,控制请求频率,禁止绕过付费墙等行为。本文所有技术仅用于学习合规爬取技术。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Headers伪装实战:从入门到翻车
2.1 基础Headers配置
最简单的伪装就是设置User-Agent。这是我早期常用的配置:
python复制headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get('https://www.zhihu.com', headers=headers)
但很快发现,仅设置UA会被更聪明的反爬系统识别。完整的基础Headers应该包含:
python复制base_headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
}
2.2 动态Headers策略
当爬取频率较高时,需要更高级的伪装技巧:
- User-Agent轮换池:准备20+个不同浏览器版本的UA随机使用
- Referer伪装链:模拟真实用户的浏览路径(首页→列表页→详情页)
- 设备指纹模拟:添加X-Requested-With、Sec-CH-UA等新式头
实测案例:爬取某电商网站时,添加以下头后成功率从40%提升到92%:
python复制dynamic_headers = {
'Sec-CH-UA': '"Google Chrome";v="91", "Chromium";v="91", ";Not A Brand";v="99"',
'Sec-CH-UA-Mobile': '?0',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1'
}
3. Session会话管理:比你想的更复杂
3.1 Session对象的核心价值
很多新手会疑惑:为什么不用全局变量保存Cookie?看这个对比实验:
python复制# 错误示范:手动管理Cookie
cookies = {}
for i in range(3):
r = requests.get('https://example.com/login', cookies=cookies)
cookies.update(r.cookies.get_dict())
# 正确做法:使用Session
with requests.Session() as s:
for i in range(3):
r = s.get('https://example.com/login')
Session对象自动处理:
- Cookie的存储和回传
- 连接池复用(提升性能)
- 持久性参数配置(headers/auth等)
3.2 高级Session技巧
- 连接超时优化:
python复制session = requests.Session()
adapter = requests.adapters.HTTPAdapter(
pool_connections=10,
pool_maxsize=50,
max_retries=3
)
session.mount('http://', adapter)
session.mount('https://', adapter)
- 请求重试策略:
python复制from urllib3.util.retry import Retry
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504]
)
- DNS缓存问题:
遇到"Temporary failure in name resolution"时:
python复制session = requests.Session()
session.trust_env = False # 禁用系统代理配置
4. Cookie的合规使用与陷阱
4.1 Cookie处理最佳实践
从浏览器获取Cookie的两种安全方式:
方法一:手动复制(适合临时调试)
- Chrome开发者工具 → Application → Cookies
- 复制为cURL命令格式
- 使用
curl_cffi库转换:
python复制from curl_cffi import Curl
curl = Curl()
curl.setopt('COOKIE', 'a=1; b=2')
headers, body = curl.perform()
方法二:自动化登录(适合生产环境)
python复制session = requests.Session()
login_data = {
'username': 'your_username',
'password': 'your_password'
}
session.post('https://example.com/login', data=login_data)
# 后续请求会自动携带登录后的Cookie
4.2 常见Cookie陷阱
-
HttpOnly Cookie:
- 无法通过document.cookie获取
- 解决方案:使用Selenium等浏览器自动化工具
-
Cookie时效性:
python复制# 检查Cookie过期时间 for cookie in session.cookies: if cookie.expires and cookie.expires < time.time(): print(f"Cookie {cookie.name} 已过期") -
域名绑定问题:
python复制# 强制设置域名范围 session.cookies.set('key', 'value', domain='.example.com', path='/')
5. 反反爬实战:处理429 Too Many Requests
当看到这个错误时,说明触发了网站限流:
python复制try:
response = session.get(url)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
if err.response.status_code == 429:
retry_after = int(err.response.headers.get('Retry-After', 60))
time.sleep(retry_after + random.uniform(0, 5))
完整解决方案:
- 请求限速:
python复制import time
import random
def throttled_request(url):
time.sleep(1 + random.random()) # 1-2秒随机间隔
return session.get(url)
- IP轮换策略:
python复制proxies = [
'http://proxy1.example.com:8080',
'http://proxy2.example.com:8080'
]
response = session.get(url, proxies={'http': random.choice(proxies)})
- 请求指纹混淆:
python复制headers['X-Forwarded-For'] = f"{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}"
6. 真实案例:知乎首页爬取全流程
下面是通过Headers+Session+Cookie爬取知乎首页的完整代码:
python复制import requests
from bs4 import BeautifulSoup
# 1. 初始化Session
session = requests.Session()
# 2. 配置基础Headers
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15',
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
'Referer': 'https://www.zhihu.com/',
})
# 3. 添加必要Cookie(从浏览器手动获取)
session.cookies.update({
'_zap': 'your_cookie_value',
'd_c0': 'your_cookie_value',
'__snaker__id': 'your_cookie_value'
})
# 4. 发起请求
try:
response = session.get('https://www.zhihu.com', timeout=10)
response.raise_for_status()
# 5. 解析内容
soup = BeautifulSoup(response.text, 'html.parser')
hot_list = soup.select('.HotList-item')
for item in hot_list[:5]:
title = item.select_one('.HotList-itemTitle').get_text(strip=True)
print(f"热门话题:{title}")
except requests.exceptions.RequestException as e:
print(f"请求失败:{str(e)}")
finally:
session.close()
关键点说明:
- 使用
with语句自动管理Session生命周期 - 从浏览器复制真实Cookie值替换
your_cookie_value - 添加了完整的异常处理和超时控制
- 使用CSS选择器精准定位内容元素
7. 爬虫工程师的调试技巧
7.1 请求日志记录
python复制import logging
from http.client import HTTPConnection
HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
7.2 使用mitmproxy调试
- 启动mitmproxy:
bash复制mitmproxy -p 8080
- 配置爬虫使用代理:
python复制proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}
session.proxies.update(proxies)
7.3 浏览器复制为cURL
Chrome开发者工具 → Network → 右键请求 → Copy → Copy as cURL
然后用curlconverter转换为Python代码:
bash复制pip install curlconverter
8. 我的五个血泪教训
- Cookie时效性:某次爬虫运行3小时后突然失效,原因是Cookie过期时间设置错误。现在我会在代码中添加自动检查:
python复制if 'expires' in session.cookies.get_dict():
expires = session.cookies.get_dict()['expires']
if datetime.strptime(expires, '%a, %d-%b-%Y %H:%M:%S GMT') < datetime.now():
relogin()
-
User-Agent陷阱:使用过时的UA会被直接拦截。维护一个实时更新的UA池很重要。
-
TLS指纹识别:某些网站会检测TLS握手特征。解决方案:
python复制# 使用curl_cffi模拟浏览器TLS指纹
from curl_cffi import requests as curl_requests
resp = curl_requests.get(url, impersonate="chrome110")
-
IP被封处理流程:
- 立即停止当前IP的所有请求
- 切换备用IP
- 分析触发原因(通常是请求频率或行为异常)
-
法律风险规避:
- 绝不爬取用户隐私数据
- 遵守robots.txt规则
- 控制请求间隔≥3秒
- 商业用途前务必咨询法律顾问
