在这里插入图片描述


📖 开篇导读

在前两节课中,我们掌握了 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 模拟访问。


🧠 知识点思维导图总结

第46课:爬虫反爬入门

常见反爬手段

User-Agent检测

IP频率限制

Referer检查

验证码

动态Token

字体反爬

JavaScript加密

请求头伪装

随机

Referer

Accept/Accept-Language

Cookie

使用fake_useragent

代理IP

作用:突破频率/隐藏IP

类型:HTTP/HTTPS/SOCKS

Requests中使用proxies

代理池实现

会话维持

Session对象自动管理Cookie

模拟登录流程

Cookie持久化

验证码识别

简单OCR: pytesseract

打码平台

滑块验证(Selenium)

延时与重试

随机延时

请求重试机制(Retry)

综合案例

防反爬爬虫模板

面试考点

反爬手段与应对

如何防止IP被封

Session和Cookie原理

验证码处理方式


🔜 下节课预告

本节我们学习了如何应对网站的反爬虫措施,掌握了请求头伪装、代理IP、会话维持、验证码识别等技巧。下一节课我们将正式进入Web开发领域,学习 Flask 框架,搭建自己的 Web 应用。

第47课:Flask框架零基础入门:路由、模板、表单与数据库开发

内容包括:

  • Flask 安装与第一个应用
  • 路由与请求方法
  • Jinja2 模板引擎
  • 表单处理与文件上传
  • 使用 SQLAlchemy 操作数据库
  • 实战:简易博客系统

Web 开发是 Python 的重要应用方向,学完后你将能独立搭建网站后端。

🌟 学习鼓励:反爬是一场持续的攻防战。本课的学习不是为了让你恶意爬取,而是帮助你理解如何编写健壮的采集程序,同时也能更好地为自己的网站设计反爬措施。请遵守法律法规和网站协议,合理使用爬虫技术。下一阶段我们将进入 Web 开发,同样精彩!


🔗《50节课 Python 从入门到精通》系列课程导航

去订阅

🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

更多推荐