爬虫工程师学习路径 · 阶段四:反爬虫对抗(完整学习文档)

🕷️ 爬虫工程师学习路径 · 阶段四:反爬虫对抗(完整学习文档)

      • [📌 阶段目标](#📌 阶段目标)
    • [🌐 第1章 IP 代理池](#🌐 第1章 IP 代理池)
      • [1.1 为什么需要代理?](#1.1 为什么需要代理?)
      • [1.2 代理的类型](#1.2 代理的类型)
      • [1.3 获取代理的途径](#1.3 获取代理的途径)
      • [1.4 使用 `requests` 的代理参数](#1.4 使用 requests 的代理参数)
      • [1.5 搭建简易代理池](#1.5 搭建简易代理池)
      • [1.6 付费代理示例(以阿布云为例)](#1.6 付费代理示例(以阿布云为例))
    • [🔄 第2章 User-Agent 轮换与 Cookie 池](#🔄 第2章 User-Agent 轮换与 Cookie 池)
      • [2.1 User-Agent 轮换](#2.1 User-Agent 轮换)
      • [2.2 Cookie 池](#2.2 Cookie 池)
    • [🔐 第3章 验证码识别](#🔐 第3章 验证码识别)
      • [3.1 验证码的类型](#3.1 验证码的类型)
      • [3.2 使用 OCR 识别简单验证码](#3.2 使用 OCR 识别简单验证码)
      • [3.3 使用打码平台](#3.3 使用打码平台)
    • [⏱️ 第4章 请求频率控制](#⏱️ 第4章 请求频率控制)
      • [4.1 为什么需要控制频率?](#4.1 为什么需要控制频率?)
      • [4.2 随机延时](#4.2 随机延时)
      • [4.3 限速](#4.3 限速)
    • [🖐️ 第5章 指纹浏览器](#🖐️ 第5章 指纹浏览器)
      • [5.1 什么是浏览器指纹?](#5.1 什么是浏览器指纹?)
      • [5.2 使用 puppeteer-extra-stealth](#5.2 使用 puppeteer-extra-stealth)
    • [🎯 第6章 实战一:爬取某招聘网站(带 IP 频率限制)](#🎯 第6章 实战一:爬取某招聘网站(带 IP 频率限制))
      • [6.1 目标](#6.1 目标)
      • [6.2 任务要求](#6.2 任务要求)
      • [6.3 代码框架](#6.3 代码框架)
      • [6.4 扩展要求](#6.4 扩展要求)
    • [🎯 第7章 实战二:识别简单验证码并自动登录](#🎯 第7章 实战二:识别简单验证码并自动登录)
      • [7.1 目标](#7.1 目标)
      • [7.2 步骤分析](#7.2 步骤分析)
      • [7.3 示例:使用 OCR 登录测试站](#7.3 示例:使用 OCR 登录测试站)
      • [7.4 打码平台版本](#7.4 打码平台版本)
      • [7.5 处理验证码错误](#7.5 处理验证码错误)
    • [📚 学习资源推荐](#📚 学习资源推荐)
    • [⚠️ 法律与道德提醒](#⚠️ 法律与道德提醒)
    • [🎯 下一步](#🎯 下一步)

欢迎进入阶段四!在前三个阶段,你已经学会了如何爬取静态和动态网页,处理登录和会话。然而,真实的爬虫战场上,网站会采用各种反爬手段来阻止自动化访问。本阶段将带你深入理解常见的反爬虫机制,并掌握突破它们的方法。你将学习如何搭建代理池、轮换 User-Agent、识别验证码、模拟人类行为,最终能够应对大部分反爬挑战。


📌 阶段目标

  • 理解 IP 封禁的原理,能够搭建并使用代理池(免费/付费)
  • 掌握 User-Agent 轮换和 Cookie 池的使用
  • 学会验证码识别的基础方法(OCR、打码平台)
  • 能够控制请求频率,模拟人类浏览行为
  • 了解指纹浏览器(如 puppeteer-extra-stealth)的基本用法
  • 综合运用以上技术,突破实际网站的反爬
  • 实战:完成两个练习------爬取某招聘网站(带 IP 频率限制)和识别简单验证码并自动登录

🌐 第1章 IP 代理池

1.1 为什么需要代理?

网站通常会监控同一 IP 的请求频率,如果短时间内请求过多,就会封禁该 IP。使用代理可以将请求分散到不同的 IP,从而绕过 IP 封禁。

1.2 代理的类型

  • 透明代理:不隐藏客户端真实 IP,一般不用。
  • 匿名代理:隐藏真实 IP,但会声明自己是代理。
  • 高匿代理:完全隐藏代理特征,目标网站看不出是代理。

按协议分:

  • HTTP 代理:适用于 HTTP 网站。
  • HTTPS 代理:适用于 HTTPS 网站。
  • SOCKS 代理:支持更多协议(如 SOCKS5),更通用。

1.3 获取代理的途径

  1. 免费代理网站 :如 https://free-proxy-list.net/,但可用性低,不稳定。
  2. 付费代理服务:如阿布云、芝麻代理,提供稳定、高匿的代理 IP。
  3. 自建代理池:从免费源爬取代理,验证后存入池中。

1.4 使用 requests 的代理参数

python 复制代码
import requests

proxies = {
    'http': 'http://127.0.0.1:8888',
    'https': 'https://127.0.0.1:8888'
}
response = requests.get('http://example.com', proxies=proxies)

对于需要认证的代理:

python 复制代码
proxies = {
    'http': 'http://user:password@127.0.0.1:8888',
}

1.5 搭建简易代理池

代理池的核心功能:

  • 从多个免费源爬取代理 IP。
  • 验证代理的可用性(如访问测试网站)。
  • 提供 API 接口,随机返回可用代理。

这里我们只实现一个简单版本:从免费网站爬取代理,验证后存入列表,并随机使用。

代码示例:

python 复制代码
import requests
from bs4 import BeautifulSoup
import random
import time

def fetch_free_proxies():
    """从免费代理网站获取代理列表"""
    url = 'https://free-proxy-list.net/'
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'lxml')
    table = soup.find('table')
    rows = table.find_all('tr')[1:20]  # 取前20条
    proxies = []
    for row in rows:
        cols = row.find_all('td')
        if len(cols) >= 7:
            ip = cols[0].text
            port = cols[1].text
            https = cols[6].text
            if 'yes' in https.lower():
                proxies.append({'ip': ip, 'port': port, 'type': 'https'})
            else:
                proxies.append({'ip': ip, 'port': port, 'type': 'http'})
    return proxies

def verify_proxy(proxy):
    """验证代理是否可用,通过访问一个测试网站"""
    test_url = 'http://httpbin.org/ip'
    proxy_dict = {
        proxy['type']: f"{proxy['type']}://{proxy['ip']}:{proxy['port']}"
    }
    try:
        response = requests.get(test_url, proxies=proxy_dict, timeout=5)
        if response.status_code == 200:
            print(f"代理可用: {proxy_dict}")
            return True
    except:
        pass
    return False

def get_proxy_pool():
    """构建代理池,返回可用代理列表"""
    proxies = fetch_free_proxies()
    valid_proxies = []
    for proxy in proxies:
        if verify_proxy(proxy):
            valid_proxies.append(proxy)
        time.sleep(1)
    return valid_proxies

# 使用代理池
proxy_pool = get_proxy_pool()
if proxy_pool:
    proxy = random.choice(proxy_pool)
    proxies = {proxy['type']: f"{proxy['type']}://{proxy['ip']}:{proxy['port']}"}
    response = requests.get('https://httpbin.org/ip', proxies=proxies, timeout=5)
    print(response.json())

注意:免费代理通常质量不高,且可能包含恶意代理(如窃取数据)。生产环境建议使用付费代理。

1.6 付费代理示例(以阿布云为例)

python 复制代码
# 阿布云代理隧道
proxyHost = "proxy.abuyun.com"
proxyPort = "9020"
proxyUser = "your_username"
proxyPass = "your_password"

proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {
    "host": proxyHost,
    "port": proxyPort,
    "user": proxyUser,
    "pass": proxyPass,
}

proxies = {
    "http": proxyMeta,
    "https": proxyMeta,
}

response = requests.get("http://httpbin.org/ip", proxies=proxies)
print(response.json())

2.1 User-Agent 轮换

很多网站会检查 User-Agent,如果发现是爬虫常用的 UA 就会拦截。通过随机切换 User-Agent,可以伪装成不同浏览器。

使用 fake-useragent 库可以方便地生成随机 UA:

bash 复制代码
pip install fake-useragent
python 复制代码
from fake_useragent import UserAgent
import requests

ua = UserAgent()
headers = {'User-Agent': ua.random}
response = requests.get('https://httpbin.org/headers', headers=headers)
print(response.json())

注意fake-useragent 有时会请求远程服务器更新数据,如果网络不好可以本地缓存。

对于需要登录的网站,可以维护一个 Cookie 池,多个账号轮流使用,避免单个账号被检测到异常行为。Cookie 池的实现与代理池类似:从多个账号获取 Cookie,验证有效性,然后随机选择。

python 复制代码
import pickle
import os

def save_cookies(session, filename):
    with open(filename, 'wb') as f:
        pickle.dump(session.cookies.get_dict(), f)

def load_cookies(filename):
    with open(filename, 'rb') as f:
        cookies = pickle.load(f)
        session = requests.Session()
        session.cookies.update(cookies)
        return session

# 使用示例
session1 = login_with_account('user1', 'pass1')
save_cookies(session1, 'cookies_user1.pkl')

# 随机使用
import random
cookie_files = ['cookies_user1.pkl', 'cookies_user2.pkl']
chosen = random.choice(cookie_files)
session = load_cookies(chosen)
response = session.get('https://example.com/profile')

🔐 第3章 验证码识别

3.1 验证码的类型

  • 数字/字母验证码
  • 中文验证码
  • 滑块验证码
  • 点选验证码
  • 行为验证码(如极验、腾讯验证码)

本阶段主要学习简单的数字/字母验证码识别,作为入门。

3.2 使用 OCR 识别简单验证码

Tesseract OCR 是开源的 OCR 引擎,可以识别图片中的文字。

安装 Tesseract:

  • Windows:下载安装包,并将安装路径加入 PATH。
  • Linux:sudo apt install tesseract-ocr
  • Mac:brew install tesseract

安装 Python 库:

bash 复制代码
pip install pytesseract pillow

识别示例

python 复制代码
import pytesseract
from PIL import Image

# 打开验证码图片
image = Image.open('captcha.png')

# 预处理:转为灰度,二值化
image = image.convert('L')
threshold = 140
table = []
for i in range(256):
    if i < threshold:
        table.append(0)
    else:
        table.append(1)
image = image.point(table, '1')

# 识别
code = pytesseract.image_to_string(image, config='--psm 8')  # 单行文本
print(f'识别结果:{code}')

常见问题

  • 识别率不高,可以通过图像预处理(去噪、膨胀、腐蚀)提高。
  • 针对特定网站的验证码,可以训练自己的模型(进阶)。

3.3 使用打码平台

对于复杂的验证码(如中文、滑块),OCR 基本无效,可以使用打码平台。打码平台提供人工识别服务,API 调用方便。

常用打码平台:

示例(超级鹰)

python 复制代码
import requests
from hashlib import md5

class Chaojiying:
    def __init__(self, username, password, soft_id):
        self.username = username
        self.password = md5(password.encode('utf-8')).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def post_pic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def report_error(self, im_id):
        """
        im_id: 报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()

# 使用示例
chaojiying = Chaojiying('username', 'password', 'soft_id')
with open('captcha.png', 'rb') as f:
    im = f.read()
result = chaojiying.post_pic(im, 1004)  # 1004 是验证码类型
print(result)

⏱️ 第4章 请求频率控制

4.1 为什么需要控制频率?

即使使用了代理,如果请求频率过高,仍然会被网站识别为爬虫。因此需要模拟人类浏览行为,控制请求间隔。

4.2 随机延时

使用 time.sleep(),但固定间隔容易被识别,应该引入随机性。

python 复制代码
import time
import random

def random_sleep(min_sec=1, max_sec=3):
    time.sleep(random.uniform(min_sec, max_sec))

在每次请求后调用 random_sleep()

4.3 限速

可以使用令牌桶算法控制请求速率,但简单的随机延时已经足够应对大部分情况。


🖐️ 第5章 指纹浏览器

5.1 什么是浏览器指纹?

网站可以通过收集浏览器的各种属性(如 User-Agent、屏幕分辨率、语言、插件、Canvas 指纹等)来生成一个唯一标识,即使更换 IP 也能识别出同一浏览器。

5.2 使用 puppeteer-extra-stealth

puppeteer-extra 是一个基于 Node.js 的库,puppeteer-extra-stealth 插件可以隐藏自动化特征,绕过指纹检测。Python 中可以通过 pyppeteer 调用,但更推荐直接使用 Node.js。这里简单介绍概念。

如果想在 Python 中使用类似功能,可以使用 undetected-chromedriverplaywright-stealth(第三方)。

undetected-chromedriver 示例

bash 复制代码
pip install undetected-chromedriver
python 复制代码
import undetected_chromedriver as uc

driver = uc.Chrome()
driver.get('https://nowsecure.nl')  # 测试是否被检测为自动化
print(driver.page_source)
driver.quit()

该库会自动修改 Chrome 的参数,隐藏自动化特征。


🎯 第6章 实战一:爬取某招聘网站(带 IP 频率限制)

6.1 目标

  • 选择一个有反爬的招聘网站,如 Boss 直聘、拉勾网(需注意法律风险,建议使用测试站或用公开 API)
  • 由于商业网站有严格反爬,本练习可以使用 测试网站 或自己搭建的简单站点模拟。如果必须用真实网站,请务必遵守 robots.txt,控制频率,且仅用于学习。

假设我们有一个模拟的目标网站 http://test.com/jobs,它对同一 IP 每分钟最多允许 30 次请求,超过则返回 429 状态码。

6.2 任务要求

  • 搭建简易代理池(或使用付费代理)
  • 每次请求随机选择代理
  • 控制请求间隔(随机 1-3 秒)
  • 处理请求失败(如代理失效,更换代理重试)
  • 爬取至少 10 页职位信息

6.3 代码框架

python 复制代码
import requests
import random
import time
from fake_useragent import UserAgent

# 假设的代理池(需要你自己填充可用代理)
proxy_pool = [
    {'http': 'http://111.111.111.111:8888'},
    {'http': 'http://222.222.222.222:8888'},
    # ...
]

ua = UserAgent()

def fetch_page(page_num, proxy):
    """爬取一页数据,使用指定代理"""
    url = f'http://test.com/jobs?page={page_num}'
    headers = {'User-Agent': ua.random}
    try:
        response = requests.get(url, headers=headers, proxies=proxy, timeout=5)
        if response.status_code == 200:
            return response.text
        elif response.status_code == 429:
            print('IP 被封,需要更换代理')
            return None
        else:
            print(f'其他错误:{response.status_code}')
            return None
    except Exception as e:
        print(f'请求异常:{e}')
        return None

def crawl():
    all_data = []
    for page in range(1, 11):
        # 随机选择一个代理
        proxy = random.choice(proxy_pool)
        print(f'正在爬取第 {page} 页,使用代理 {proxy}')
        html = fetch_page(page, proxy)
        if html:
            # 解析 HTML,提取数据(略)
            all_data.append(html)
        else:
            # 代理失效,从池中移除
            proxy_pool.remove(proxy)
            # 可以选择重新尝试当前页,但需要处理无限循环,这里简单跳过
            continue
        # 随机延时
        time.sleep(random.uniform(1, 3))
    return all_data

if __name__ == '__main__':
    result = crawl()
    print(f'爬取完成,共 {len(result)} 页')

6.4 扩展要求

  • 将代理池维护为可动态更新的,从免费源获取新代理。
  • 使用 Redis 存储代理池,实现分布式共享。

🎯 第7章 实战二:识别简单验证码并自动登录

7.1 目标

  • 找一个有数字/字母验证码的网站(如某学校教务系统、旧版论坛)
  • 使用 OCR 或打码平台识别验证码,实现自动登录
  • 如果找不到合适的网站,可以使用开源的验证码测试平台,如自己搭建或使用 https://captcha.com/demos/features/captcha-demo.aspx

7.2 步骤分析

  1. 访问登录页面,获取验证码图片 URL。
  2. 下载验证码图片,保存为临时文件。
  3. 调用 OCR 或打码平台识别。
  4. 构造登录 POST 请求,携带用户名、密码、验证码。
  5. 验证登录是否成功(如检查跳转后的页面是否有用户名)。

7.3 示例:使用 OCR 登录测试站

假设测试站登录 URL 为 http://test.com/login,验证码图片 URL 为 http://test.com/captcha

python 复制代码
import requests
import pytesseract
from PIL import Image
import io

# 1. 获取验证码
session = requests.Session()
captcha_resp = session.get('http://test.com/captcha')
captcha_img = Image.open(io.BytesIO(captcha_resp.content))

# 2. 识别验证码
code = pytesseract.image_to_string(captcha_img, config='--psm 8').strip()

# 3. 构造登录数据
login_data = {
    'username': 'your_username',
    'password': 'your_password',
    'captcha': code
}
response = session.post('http://test.com/login', data=login_data)

# 4. 验证登录
if '欢迎' in response.text:
    print('登录成功!')
else:
    print('登录失败,可能验证码错误')

7.4 打码平台版本

python 复制代码
import requests
from chaojiying import Chaojiying  # 假设上面定义的类

session = requests.Session()
captcha_resp = session.get('http://test.com/captcha')
chaojiying = Chaojiying('username', 'password', 'soft_id')
result = chaojiying.post_pic(captcha_resp.content, 1004)  # 1004 为验证码类型
code = result['pic_str']

login_data = {
    'username': 'your_username',
    'password': 'your_password',
    'captcha': code
}
response = session.post('http://test.com/login', data=login_data)
# ... 验证结果

7.5 处理验证码错误

如果识别错误,登录会失败。可以循环尝试,直到成功或达到最大次数。

python 复制代码
max_attempts = 3
for i in range(max_attempts):
    # 重新获取验证码(每次需要新的)
    captcha_resp = session.get('http://test.com/captcha')
    # 识别...
    # 提交登录
    if '欢迎' in response.text:
        print('登录成功')
        break
    else:
        print(f'第{i+1}次尝试失败,重试...')
else:
    print('多次尝试失败,可能账号密码错误或验证码太难')

📚 学习资源推荐


⚠️ 法律与道德提醒

  • 本阶段涉及的技术可能被用于非法用途,请务必遵守法律法规和网站协议。
  • 尊重网站的 robots.txt,合理控制请求频率,不要对目标服务器造成压力。
  • 不要爬取个人隐私数据、版权内容。
  • 验证码识别技术应仅用于学习和已授权的测试环境。

🎯 下一步

完成本阶段后,你已经掌握了对抗常见反爬虫的技术。接下来可以进入阶段五:数据存储与清洗,学习将爬取的数据存入数据库,并进行清洗、去重、定时更新。或者进入阶段六:框架与分布式,学习使用 Scrapy 提高开发效率。

如果在实战中遇到问题,欢迎随时交流!

相关推荐
CodeLinghu1 小时前
我写了一个OpenClaw一健部署工具,引发了3w人围观
人工智能·python·语言模型·llm
搬砖者(视觉算法工程师)1 小时前
通俗易懂的 Transformer 入门文章(第一部分):功能概述
人工智能·python
CappuccinoRose1 小时前
MATLAB学习文档 - 汇总篇
学习·算法·matlab
AC赳赳老秦2 小时前
DeepSeek助力国产化AI落地:政务/企业场景下的国产算力适配避坑指南
大数据·人工智能·python·prompt·政务·ai-native·deepseek
bluceli2 小时前
CSS Scroll Snap:打造丝滑滚动体验的利器
前端·css
不灭锦鲤2 小时前
网络安全学习第47天
学习·web安全
电商API_180079052472 小时前
1688 商品详情 API 深度对接:字段说明、异常处理与性能优化
大数据·服务器·爬虫·数据挖掘·数据分析
梵得儿SHI2 小时前
Vue3 生态工具实战进阶:API 请求封装 + 样式解决方案全攻略(Axios/Sass/CSS Modules)
前端·css·vue3·sass·api请求·样式解决方案·组合式api管理
zhouping@3 小时前
BUUCTFweb
学习·web安全·php