🕷️ 爬虫工程师学习路径 · 阶段四:反爬虫对抗(完整学习文档)
-
-
- [📌 阶段目标](#📌 阶段目标)
- [🌐 第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 获取代理的途径
- 免费代理网站 :如
https://free-proxy-list.net/,但可用性低,不稳定。 - 付费代理服务:如阿布云、芝麻代理,提供稳定、高匿的代理 IP。
- 自建代理池:从免费源爬取代理,验证后存入池中。
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章 User-Agent 轮换与 Cookie 池
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 有时会请求远程服务器更新数据,如果网络不好可以本地缓存。
2.2 Cookie 池
对于需要登录的网站,可以维护一个 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 调用方便。
常用打码平台:
- 超级鹰(http://www.chaojiying.com/)
- 打码兔
- 图鉴
示例(超级鹰):
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-chromedriver 或 playwright-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 步骤分析
- 访问登录页面,获取验证码图片 URL。
- 下载验证码图片,保存为临时文件。
- 调用 OCR 或打码平台识别。
- 构造登录 POST 请求,携带用户名、密码、验证码。
- 验证登录是否成功(如检查跳转后的页面是否有用户名)。
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('多次尝试失败,可能账号密码错误或验证码太难')
📚 学习资源推荐
- 代理相关 :
- 验证码识别 :
- 指纹浏览器 :
- 反爬虫书籍:《Python 3 反爬虫原理与实战》韦世东
⚠️ 法律与道德提醒
- 本阶段涉及的技术可能被用于非法用途,请务必遵守法律法规和网站协议。
- 尊重网站的
robots.txt,合理控制请求频率,不要对目标服务器造成压力。 - 不要爬取个人隐私数据、版权内容。
- 验证码识别技术应仅用于学习和已授权的测试环境。
🎯 下一步
完成本阶段后,你已经掌握了对抗常见反爬虫的技术。接下来可以进入阶段五:数据存储与清洗,学习将爬取的数据存入数据库,并进行清洗、去重、定时更新。或者进入阶段六:框架与分布式,学习使用 Scrapy 提高开发效率。
如果在实战中遇到问题,欢迎随时交流!