第46课:Python|爬虫反爬入门【请求头、代理IP、Cookie会话维持实战】

文章目录
📖 开篇导读
在前两节课中,我们掌握了 Requests、BeautifulSoup、正则表达式、XPath 和 lxml 等爬虫工具,能够抓取许多网站的公开数据。然而,随着我们爬取的网站越来越多,你一定会遇到请求被拒绝、返回 403 状态码、跳转到验证码页面、数据被加密等情况。这些都是网站为了保护自己而设置的反爬虫机制。
💡 工作场景:
- 数据采集工程师:需要绕过各种反爬策略,稳定获取数据。
- 市场分析:抓取竞争对手价格、评论时,往往要应对严格的反爬。
- 舆情监控:持续抓取新闻、社交媒体数据,IP 容易被封。
- 搜索引擎:百度、Google 等搜索引擎的爬虫也会遵守协议,但恶意爬虫会被封锁。
本课将学习常见的反爬手段以及对应的绕过技巧:
- 请求头伪装:模拟真实浏览器的 User-Agent、Referer、Accept 等。
- 代理 IP:通过代理池轮换 IP,避免单 IP 请求过快被封。
- Cookie 会话维持:登录后保持会话,抓取需要认证的数据。
- 验证码识别:基础识别思路(OCR、打码平台)。
- 其他反爬:字体反爬、JavaScript 混淆等简介。
学完本课,你将能够编写具有一定反爬对抗能力的爬虫程序,应对大部分初级和中级反爬措施。
🎯 学习目标
| 目标编号 | 具体掌握内容 | 对应面试/工作价值 |
|---|---|---|
| 1️⃣ | 了解常见的反爬虫手段(User-Agent、IP 限速、验证码、动态 Token) | 面试常见问题 |
| 2️⃣ | 掌握请求头伪装,使用浏览器指纹工具生成完整 headers | 避免被识别为爬虫 |
| 3️⃣ | 掌握代理 IP 的使用,搭建简单代理池 | 突破 IP 频率限制 |
| 4️⃣ | 掌握 Session 的 Cookie 持久化与会话维持 | 模拟登录和保持登录状态 |
| 5️⃣ | 了解验证码识别的基本思路(OCR、打码平台) | 处理简单验证码 |
| 6️⃣ | 综合运用:爬取需要登录的网站,并应对反爬 | 提高爬虫稳定性 |
🔥 面试考点:“网站常见的反爬手段有哪些?”“如何应对 IP 被封?”“Session 和 Cookie 的关系?”“如何处理需要登录的网站?”“什么是浏览器指纹?”
📚 知识点理论精讲
一、常见的反爬虫机制
| 反爬机制 | 描述 | 应对策略 |
|---|---|---|
| User-Agent 检查 | 验证请求的 UA,非浏览器直接拒绝 | 设置常见浏览器 UA |
| IP 频率限制 | 同一 IP 短时间内请求过多封禁 | 使用代理 IP 轮换,添加延时 |
| Referer 检查 | 检查请求来源是否合法 | 设置 Referer 为网站主页 |
| Cookie/Session | 需要登录或保持会话 | 使用 Session 携带 Cookie |
| 验证码 | 弹出图形验证码、滑块验证码 | OCR 识别、打码平台、机器学习 |
| 动态 Token | 页面生成 token 校验请求 | 分析 JS 生成逻辑,模拟获取 |
| 字体反爬 | 使用自定义字体映射数字/文字 | 解析字体文件,映射还原 |
| JavaScript 加密 | 数据通过 JS 加密后渲染 | 使用 Selenium/Playwright 或逆向 JS |
| 返回假数据 | 返回错误数据或 HTML 扰乱 | 检测校验逻辑 |
本课重点讲解前四种最常见、也最容易处理的反爬措施。
二、请求头伪装
2.1 User-Agent(UA)
网站通过 UA 识别客户端类型。默认的 python-requests/x.x.x 很容易被识破。我们需要模拟常见浏览器 UA。
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
response = requests.get(url, headers=headers)
建议使用 fake_useragent 库随机生成 UA:
from fake_useragent import UserAgent
ua = UserAgent()
headers = {'User-Agent': ua.random}
2.2 其他重要请求头
| Headers | 作用 | 示例 |
|---|---|---|
Referer |
标识请求来源页面,防盗链 | https://www.baidu.com/ |
Accept |
客户端接受的内容类型 | text/html,application/xhtml+xml |
Accept-Language |
接受语言 | zh-CN,zh;q=0.9 |
Accept-Encoding |
压缩编码(一般 requests 自动处理) | gzip, deflate |
Origin |
跨域请求来源 | https://example.com |
可以完整复制浏览器的请求头:
headers = {
'User-Agent': 'Mozilla/5.0 ...',
'Referer': 'https://www.google.com/',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Connection': 'keep-alive'
}
2.3 浏览器指纹
除了请求头,网站还可能收集更丰富的客户端信息:屏幕分辨率、时区、WebGL 渲染器、字体列表等。普通爬虫难以完美模拟,对于此类反爬,建议使用无头浏览器(Selenium、Playwright)。
三、代理 IP 的使用
3.1 为什么需要代理?
- 突破 IP 访问频率限制:每 IP 每分钟请求次数限制。
- 隐藏真实 IP:避免被封锁。
- 访问地理限制内容(如某些视频仅限国内 IP)。
3.2 代理类型
- HTTP 代理:支持 HTTP 请求。
- HTTPS 代理:支持 HTTPS 请求。
- SOCKS 代理:更底层,支持 TCP/UDP。
3.3 Requests 中使用代理
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}
response = requests.get(url, proxies=proxies)
如果需要认证:
proxies = {
'http': 'http://user:pass@127.0.0.1:8080'
}
3.4 代理池的简单实现
代理池:从免费或付费代理网站抓取代理 IP,验证可用性后存入列表,每次请求随机选择。
import requests
import random
proxy_list = [
'http://123.123.123.123:8080',
'http://111.111.111.111:3128',
]
def get_random_proxy():
return {'http': random.choice(proxy_list), 'https': random.choice(proxy_list)}
注意:免费代理不稳定,建议使用付费代理服务(如快代理、芝麻代理)或自建代理池。
四、Cookie 与会话维持
4.1 Cookie 的作用
Cookie 是服务器发送给客户端的键值对,客户端下次请求时会自动携带,用于维持状态(如登录状态)。
4.2 使用 Session 自动管理 Cookie
requests.Session() 会自动处理 Cookie,同一会话内保持登录状态。
session = requests.Session()
# 第一次登录请求
login_data = {'username': 'test', 'password': '123'}
session.post('https://example.com/login', data=login_data)
# 后续请求自动携带登录后的 Cookie
profile = session.get('https://example.com/profile')
4.3 手动携带 Cookie
如果已经有了 Cookie 字符串,可以手动设置:
cookies = {'session_id': 'abc123'}
response = requests.get(url, cookies=cookies)
或者从浏览器复制 Cookie 字符串:
cookies_str = 'session_id=abc123; user=test'
cookies = {k.strip(): v.strip() for k, v in (item.split('=') for item in cookies_str.split(';'))}
4.4 登录流程分析
对于简单的表单登录,可以模拟 POST 请求。需要分析登录提交的 URL、数据字段(有些网站有动态 token 等)。
session = requests.Session()
# 先访问登录页获取 token(如有)
login_page = session.get('https://example.com/login')
token = extract_token(login_page.text)
data = {'username': 'test', 'password': '123', 'csrf_token': token}
session.post('https://example.com/login', data=data)
五、验证码识别初步
5.1 图形验证码
OCR 识别(简单)
使用 pytesseract + PIL 识别简单数字字母验证码。但对干扰线、扭曲复杂的验证码准确率低。
import pytesseract
from PIL import Image
img = Image.open('captcha.png')
code = pytesseract.image_to_string(img)
打码平台
使用第三方打码平台(如超级鹰、图鉴、打码兔),上传图片,平台返回识别结果。
# 示例代码(需注册获取账号)
import requests
api_url = 'http://api.tuyan.com/ocr/'
files = {'image': open('captcha.png', 'rb')}
data = {'api_key': 'your_key'}
resp = requests.post(api_url, files=files, data=data)
code = resp.json()['result']
5.2 滑块验证码
更复杂的验证码需要模拟鼠标轨迹。通常推荐使用 Selenium 或 Playwright 模拟浏览器行为,配合 OpenCV 识别缺口位置。
六、动态请求头与延时策略
6.1 随机 User-Agent 策略
使用 fake_useragent 库:
from fake_useragent import UserAgent
ua = UserAgent()
headers['User-Agent'] = ua.random
6.2 随机延时
模拟人类行为,设置随机间隔:
import time
import random
time.sleep(random.uniform(0.5, 2))
6.3 重试机制
遇到网络错误或 403 时自动重试。
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
💻 代码案例实操
案例1:完整的请求头伪装 + 随机 UA
"""
random_ua_headers.py
演示使用 fake_useragent 生成随机 UA 并添加其他常见头
"""
import requests
from fake_useragent import UserAgent
def get_common_headers():
ua = UserAgent()
headers = {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
return headers
url = 'https://httpbin.org/headers'
resp = requests.get(url, headers=get_common_headers())
print(resp.json()['headers'])
案例2:使用代理 IP 轮询
"""
proxy_rotation.py
使用代理列表轮询,防止 IP 被封
"""
import requests
import random
import time
proxy_pool = [
'http://111.11.111.111:8080',
'http://222.22.222.222:3128',
'http://333.33.333.333:9999',
]
def get_proxy():
return {'http': random.choice(proxy_pool), 'https': random.choice(proxy_pool)}
def fetch(url):
try:
proxy = get_proxy()
resp = requests.get(url, proxies=proxy, timeout=10, headers={'User-Agent': 'Mozilla/5.0'})
if resp.status_code == 200:
return resp.text
else:
print(f"状态码 {resp.status_code}, 切换代理")
return None
except Exception as e:
print(f"请求失败: {e}")
return None
if __name__ == '__main__':
test_url = 'https://httpbin.org/ip'
for i in range(5):
content = fetch(test_url)
if content:
print(content)
time.sleep(1)
案例3:使用 Session 模拟登录 GitHub(无验证码)
"""
github_login_session.py
使用 Session 登录 GitHub(需要真实账号,此处仅演示流程)
"""
import requests
from lxml import etree
session = requests.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0'})
# 1. 获取登录页,提取 authenticity_token
login_url = 'https://github.com/login'
login_page = session.get(login_url)
tree = etree.HTML(login_page.text)
token = tree.xpath('//input[@name="authenticity_token"]/@value')[0]
# 2. 构造登录数据
login_data = {
'commit': 'Sign in',
'authenticity_token': token,
'login': 'your_username',
'password': 'your_password'
}
# 3. 提交登录
session.post('https://github.com/session', data=login_data)
# 4. 访问需要登录的页面
profile_resp = session.get('https://github.com/settings/profile')
print("登录后状态码:", profile_resp.status_code)
案例4:Cookie 持久化(保存到文件)
"""
cookie_persistence.py
将 Session 的 Cookie 保存到文件,下次加载复用
"""
import requests
import pickle
def save_cookies(session, filepath):
with open(filepath, 'wb') as f:
pickle.dump(session.cookies, f)
def load_cookies(session, filepath):
with open(filepath, 'rb') as f:
session.cookies.update(pickle.load(f))
# 使用示例
session = requests.Session()
# 登录后保存
save_cookies(session, 'cookies.pkl')
# 下次创建新 session 加载
new_session = requests.Session()
load_cookies(new_session, 'cookies.pkl')
案例5:处理简单验证码(OCR 识别)
"""
ocr_captcha.py
使用 pytesseract 识别简单验证码
需要安装 tesseract-ocr 和 pytesseract
"""
import requests
from PIL import Image
import pytesseract
def solve_captcha(img_url):
# 下载验证码图片
resp = requests.get(img_url, stream=True)
with open('temp.png', 'wb') as f:
f.write(resp.content)
# 识别
img = Image.open('temp.png')
code = pytesseract.image_to_string(img, config='--psm 7').strip()
return code
# 假设某网站验证码 URL
captcha_url = 'https://example.com/captcha'
code = solve_captcha(captcha_url)
print("验证码识别结果:", code)
案例6:完整防反爬综合案例
"""
advanced_anti_anti_spider.py
综合使用随机 UA、代理、Cookie 维持、延时重试的爬虫模板
"""
import requests
import random
import time
from fake_useragent import UserAgent
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class AntiSpiderCrawler:
def __init__(self, proxy_list=None):
self.session = self._create_session()
self.proxy_list = proxy_list or []
self.ua = UserAgent()
def _create_session(self):
session = requests.Session()
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
def _get_headers(self):
return {
'User-Agent': self.ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Connection': 'keep-alive',
}
def _get_proxy(self):
if self.proxy_list:
proxy = random.choice(self.proxy_list)
return {'http': proxy, 'https': proxy}
return None
def get(self, url, max_retries=3):
for attempt in range(max_retries):
try:
headers = self._get_headers()
proxies = self._get_proxy()
resp = self.session.get(url, headers=headers, proxies=proxies, timeout=10)
if resp.status_code == 200:
return resp
else:
print(f"状态码 {resp.status_code}, 重试 {attempt+1}/{max_retries}")
time.sleep(2 ** attempt)
except Exception as e:
print(f"请求异常: {e}, 重试 {attempt+1}/{max_retries}")
time.sleep(2 ** attempt)
return None
if __name__ == '__main__':
proxy_list = ['http://127.0.0.1:8080'] # 填入有效代理
crawler = AntiSpiderCrawler(proxy_list)
resp = crawler.get('https://httpbin.org/ip')
if resp:
print(resp.text)
⚠️ 易错点避坑总结
| 序号 | 坑点描述 | 后果 | 解决方案 |
|---|---|---|---|
| 1 | 只设置 User-Agent,忽略其他头 | 仍可能被识别为爬虫 | 复制完整浏览器 headers |
| 2 | 代理 IP 没有验证可用性 | 大量请求失败 | 每次使用前测试代理连通性 |
| 3 | 忘记使用 Session,每次新建请求 | 无法维持 Cookie,不能保持登录 | 使用 requests.Session() |
| 4 | 伪造 Referer 不准确 | 防盗链资源无法获取 | 设置为正确来源页面 |
| 5 | 请求间隔固定 | 易被检测为机器 | 使用随机延时 |
| 6 | 验证码识别库未安装 tesseract | 导入报错 | 安装 tesseract 并配置路径 |
| 7 | Cookie 文件未处理好编码 | 保存/加载失败 | 使用 pickle 或 json |
| 8 | 忽略重定向导致登录失败 | Session 中 Cookie 丢失 | Requests 默认跟随重定向,无需担心 |
| 9 | 代理 IP 被目标网站封锁 | 请求失败 | 更换代理,使用高匿代理 |
| 10 | 登录时忽略动态 token | 登录失败 | 分析页面获取 token 并提交 |
📝 课后实战练习题
第1题:请求头伪装
编写一个函数,使用随机 User-Agent 和完整的浏览器头访问 https://httpbin.org/headers,输出服务器接收到的请求头。
第2题:代理 IP 测试
从免费代理网站(如 https://www.kuaidaili.com/free/)抓取 10 个代理 IP,验证其可用性(访问 https://httpbin.org/ip),将有效代理存入列表。
第3题:Session 模拟登录
选择一个支持简单表单登录的网站(如 https://quotes.toscrape.com/login),使用 Session 模拟登录(用户名 test,密码 test),然后抓取登录后才能看到的页面内容。
第4题:Cookie 持久化
完成登录后,将 Cookie 保存到文件,然后退出程序。重新启动程序,从文件加载 Cookie 并访问需要登录的页面,验证是否成功。
第5题:验证码识别(OCR)
找一个包含简单数字验证码的网站(或自己生成),编写程序自动下载验证码并使用 pytesseract 识别,输入到表单中完成登录(模拟)。
第6题:综合反爬
模仿电商网站的反爬策略:要求随机 User-Agent、随机延时、代理轮询,并实现重试机制。爬取一个目标网站(如 https://books.toscrape.com/)的所有图书标题和价格,记录每次请求使用的 IP。
第7题:动态请求头分析(可选)
使用浏览器开发者工具(F12)分析一个热门网站的请求头,找出哪些字段是必须的,哪些是可选的,并用 Python 模拟访问。
🧠 知识点思维导图总结
🔜 下节课预告
本节我们学习了如何应对网站的反爬虫措施,掌握了请求头伪装、代理IP、会话维持、验证码识别等技巧。下一节课我们将正式进入Web开发领域,学习 Flask 框架,搭建自己的 Web 应用。
第47课:Flask框架零基础入门:路由、模板、表单与数据库开发
内容包括:
- Flask 安装与第一个应用
- 路由与请求方法
- Jinja2 模板引擎
- 表单处理与文件上传
- 使用 SQLAlchemy 操作数据库
- 实战:简易博客系统
Web 开发是 Python 的重要应用方向,学完后你将能独立搭建网站后端。
🌟 学习鼓励:反爬是一场持续的攻防战。本课的学习不是为了让你恶意爬取,而是帮助你理解如何编写健壮的采集程序,同时也能更好地为自己的网站设计反爬措施。请遵守法律法规和网站协议,合理使用爬虫技术。下一阶段我们将进入 Web 开发,同样精彩!
🔗《50节课 Python 从入门到精通》系列课程导航
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~
更多推荐
所有评论(0)