Python爬虫进阶:突破反爬机制(UA伪装+代理池+验证码识别)
在爬虫开发中,“反爬”是绕不开的门槛——当请求频率过高、请求特征异常时,目标网站会通过封禁IP、弹出验证码、返回无效数据等方式拦截爬虫。本文将聚焦UA伪装、代理池搭建、验证码识别三大核心反爬场景,结合实战代码讲解具体解决方案,帮助你高效、合规地获取数据。
一、基础反爬:UA伪装——让爬虫“伪装”成真实浏览器
1.1 为什么需要UA伪装?
UA(User-Agent)是HTTP请求头中的关键字段,用于标识请求来源(如浏览器型号、操作系统)。网站通过识别UA,可轻松区分“爬虫程序”与“真实用户”——若UA为 python-requests/2.25.1 (requests库默认值),大概率会被直接拦截。
1.2 实战:动态生成并使用UA
1.2.1 方案选择:使用fake-useragent库
fake-useragent 可自动生成不同浏览器、不同系统的真实UA,无需手动维护UA列表。
安装依赖:
python
pip install fake-useragent
1.2.2 核心代码实现
python
import requests
from fake_useragent import UserAgent
# 1. 初始化UA生成器(use_cache_server=False避免缓存失效问题)
ua = UserAgent(use_cache_server=False)
# 2. 动态获取一个Chrome浏览器的UA
headers = {
"User-Agent": ua.chrome, # 也可指定ua.firefox/ua.safari
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" # 补充其他请求头,模拟真实浏览器
}
# 3. 发送请求(以测试网站httpbin.org为例,可查看请求头是否生效)
url = "http://httpbin.org/get"
response = requests.get(url, headers=headers)
# 4. 验证结果:查看返回的UA是否为伪装后的内容
print("请求头中的UA:", headers["User-Agent"])
print("网站接收的UA:", response.json()["headers"]["User-Agent"])
1.2.3 避坑点
- 若 fake-useragent 报错“Maximum retries exceeded”,需添加 use_cache_server=False 禁用缓存服务器;
- 不要频繁使用同一UA,建议每次请求前重新生成,降低被识别风险。
二、进阶反爬:代理池搭建——解决IP封禁问题
2.1 IP被封禁的场景与危害
当爬虫用同一IP高频次请求网站时(如每秒10次以上),网站会将该IP加入黑名单,导致后续请求返回403/503错误,甚至无法访问网站。此时,代理IP是唯一解决方案——通过切换不同IP发送请求,规避IP封禁。
2.2 实战:搭建简易代理池(免费代理+有效性检测)
2.2.1 核心思路
1. 从免费代理网站(如“西刺代理”“快代理”)爬取代理IP;
2. 对爬取的代理进行有效性检测(测试能否正常访问目标网站);
3. 维护一个“可用代理列表”,供爬虫随机调用。
2.2.2 代理池核心代码
python
import requests
from fake_useragent import UserAgent
import time
# 1. 爬取免费代理(以快代理为例,仅作演示,免费代理稳定性较低)
def get_free_proxies():
proxies_list = []
url = "https://www.kuaidaili.com/free/inha/1/"
headers = {"User-Agent": UserAgent(use_cache_server=False).chrome}
response = requests.get(url, headers=headers)
# 解析页面(此处用正则提取IP和端口,实际可结合lxml/xpath)
import re
pattern = r'<td data-title="IP">(\d+\.\d+\.\d+\.\d+)</td>.*?<td data-title="PORT">(\d+)</td>'
proxies = re.findall(pattern, response.text, re.S)
# 整理为requests可识别的格式({"http":"http://IP:端口", "https":"https://IP:端口"})
for ip, port in proxies:
proxies_list.append({
"http": f"http://{ip}:{port}",
"https": f"https://{ip}:{port}"
})
return proxies_list
# 2. 检测代理有效性(测试能否访问目标网站,此处以百度为例)
def check_proxy(proxy, target_url="https://www.baidu.com"):
try:
# 超时时间设为5秒,避免无效代理阻塞程序
response = requests.get(target_url, proxies=proxy, timeout=5)
# 若状态码为200,说明代理可用
return response.status_code == 200
except:
return False
# 3. 构建可用代理池
def build_proxy_pool():
free_proxies = get_free_proxies()
valid_proxies = []
for proxy in free_proxies:
if check_proxy(proxy):
valid_proxies.append(proxy)
print(f"新增可用代理:{proxy}")
time.sleep(1) # 避免检测频率过高被代理网站拦截
print(f"\n可用代理池构建完成,共{len(valid_proxies)}个可用代理")
return valid_proxies
# 4. 爬虫调用代理池(随机选择一个可用代理)
if __name__ == "__main__":
proxy_pool = build_proxy_pool()
if not proxy_pool:
print("无可用代理,程序退出")
exit()
# 随机选择一个代理
import random
target_url = "https://目标网站.com" # 替换为你的目标网站
selected_proxy = random.choice(proxy_pool)
try:
response = requests.get(
target_url,
proxies=selected_proxy,
headers={"User-Agent": UserAgent(use_cache_server=False).chrome},
timeout=10
)
print(f"请求成功,状态码:{response.status_code}")
except Exception as e:
print(f"请求失败,原因:{str(e)}")
2.2.3 优化建议
- 免费代理稳定性差,生产环境建议使用付费代理API(如阿布云、芝麻代理),可直接获取高可用代理;
- 给代理池添加“定时更新”逻辑(如每10分钟重新爬取+检测),保证代理有效性;
- 记录代理使用次数,若某代理连续失败3次,从池中移除。
三、高阶反爬:验证码识别——突破人机验证门槛
3.1 常见验证码类型与应对方案
网站常用的验证码分为“简单图形验证码”(如数字+字母组合)和“复杂验证码”(如滑块验证码、点选验证码),不同类型对应不同解决方案:
验证码类型 解决方案 适用场景
数字/字母图形验证码 Tesseract-OCR(免费)、第三方API(如百度AI) 验证码无扭曲、无干扰线
滑块验证码 模拟鼠标拖动(如selenium+ActionChains) 需验证“滑动轨迹是否自然”
点选验证码 图像识别+坐标定位(如百度AI图像识别) 需点击指定文字/图形
3.2 实战1:用Tesseract-OCR识别简单图形验证码
3.2.1 环境准备
1. 安装Tesseract-OCR引擎(官网下载,需配置系统环境变量);
2. 安装Python依赖:
python
pip install pytesseract pillow # pillow用于图像处理
3.2.2 核心代码(含图像预处理)
图形验证码常存在“干扰线、噪点”,需先进行灰度处理、二值化(黑白化),提升识别准确率:
python
from PIL import Image
import pytesseract
import requests
from io import BytesIO
# 1. 下载验证码图片(替换为目标网站的验证码URL)
captcha_url = "https://目标网站.com/captcha.jpg"
response = requests.get(captcha_url)
image = Image.open(BytesIO(response.content))
# 2. 图像预处理:灰度处理 → 二值化(去除干扰)
def preprocess_image(image):
# 灰度处理:将彩色图转为黑白图
gray_image = image.convert("L")
# 二值化:设定阈值(127),低于阈值设为0(黑),高于设为255(白)
threshold = 127
binary_image = gray_image.point(lambda x: 0 if x < threshold else 255, "1")
return binary_image
# 3. 识别验证码
processed_image = preprocess_image(image)
# 调用Tesseract识别(lang="eng"表示识别英文,若有数字可省略)
captcha_text = pytesseract.image_to_string(processed_image, lang="eng")
# 去除识别结果中的空格、换行符
captcha_text =
captcha_text.strip().replace(" ", "").replace("\n", "")
print(f"识别出的验证码:{captcha_text}")
3.2.3 避坑点
- 若识别准确率低,可调整二值化阈值(如100-150之间测试);
- 若验证码有中文,需下载Tesseract中文语言包(放入Tesseract的 tessdata 目录),并将 lang 改为 "chi_sim" 。
3.3 实战2:用Selenium突破滑块验证码
以“某网站滑块验证码”为例,核心是模拟“人类滑动轨迹”(匀速加速+轻微抖动),避免被识别为机器:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
import random
# 1. 初始化浏览器(需下载对应浏览器的驱动,如ChromeDriver)
driver = webdriver.Chrome()
driver.get("https://目标网站.com/login") # 替换为带滑块验证码的页面
time.sleep(2) # 等待页面加载
# 2. 定位滑块元素(需根据目标网站的HTML结构调整xpath)
slider = driver.find_element("xpath", '//div[@class="slider-btn"]') # 滑块按钮
slider_track = driver.find_element("xpath", '//div[@class="slider-track"]') # 滑块轨道
# 3. 模拟人类滑动轨迹:先加速后减速,带轻微抖动
def simulate_slide(slider, track_width):
# 计算滑动距离(轨道宽度 - 滑块宽度,需根据实际情况调整)
slide_distance = track_width - 50 # 50为滑块宽度,需实测
# 生成滑动轨迹(分3段:加速→匀速→减速)
track = []
current_distance = 0
# 第一段:加速(滑动距离的60%)
while current_distance < slide_distance * 0.6:
speed = random.randint(5, 10) # 加速阶段速度递增
track.append(speed)
current_distance += speed
# 第二段:匀速(滑动距离的30%)
while current_distance < slide_distance * 0.9:
speed = random.randint(3, 5) # 匀速阶段速度稳定
track.append(speed)
current_distance += speed
# 第三段:减速(剩余10%距离)
while current_distance < slide_distance:
speed = random.randint(1, 3) # 减速阶段速度递减
track.append(speed)
current_distance += speed
# 执行滑动操作
ActionChains(driver).click_and_hold(slider).perform() # 按住滑块
for x in track:
# 横向滑动x像素,纵向随机抖动±1像素(模拟人类操作)
ActionChains(driver).move_by_offset(xoffset=x, yoffset=random.randint(-1, 1)).perform()
time.sleep(random.uniform(0.01, 0.03)) # 每步停顿,模拟真实速度
time.sleep(random.uniform(0.1, 0.2)) # 滑动结束后停顿
ActionChains(driver).release().perform() # 释放滑块
# 4. 执行滑动
track_width = slider_track.size["width"] # 获取轨道宽度
simulate_slide(slider, track_width)
time.sleep(3) # 等待验证结果
# 5. 验证是否成功(根据页面提示调整判断逻辑)
if "验证成功" in driver.page_source:
print("滑块验证码突破成功!")
else:
print("滑块验证码突破失败,需重新尝试")
driver.quit() # 关闭浏览器
四、爬虫合规性与注意事项
1. 遵守robots协议:爬取前查看目标网站的 robots.txt (如 https://www.xxx.com/robots.txt ),不爬取禁止访问的目录;
2. 控制请求频率:即使使用代理,也需添加 time.sleep(random.uniform(1, 3)) ,避免给网站服务器造成压力;
3. 避免过度爬取:仅获取必要数据,不下载无关资源(如图片、视频),尊重网站版权;
4. 应对反爬升级:若网站启用“指纹识别”(如Canvas指纹、WebGL指纹),需使用 selenium+undetected-chromedriver 隐藏浏览器指纹。
本文从“UA伪装”“代理池搭建”“验证码识别”三个维度,讲解了Python爬虫突破反爬的核心方案,每个场景均提供可直接运行的实战代码。反爬与反反爬是动态博弈的过程,关键在于模拟真实用户行为+持续关注网站反爬策略变化。