做爬虫的最怕遇到验证码了,费尽了心思好不容易搞定了IP轮询,还模拟好了浏览器指纹,自认为整个流程已然是没有任何破绽,堪称天衣无缝了,突然弹出来的图形验证码,直接让数据流戛然而止。
遇见图形验证码别慌,当代理 IP池碰到初级验证码——如四位数字字母混合、简单扭曲、背景噪点这类图片时,这套“组合拳”打法,能让你90%的初级验证码自动过。

第一步:别急着识别,先试试“绕过”和“屏蔽”
最高级的应对,是从源头减少麻烦。动识别模型之前,不妨先琢磨两个问题:
一个问题是,这个验证码能不能触发得晚一点、少一点?很多网站的验证码不是首次访问就出现,而是单位时间内访问频次过高才会触发。一个稳定、优质的动态代理IP池——比如独享IP池或设置得当的隧道代理,本身就是第一道防线。它能有效分散请求,模拟真实用户的“慢速”访问节奏,从根上减少验证码的触发几率。要是用粗暴的高频请求,哪怕换再多IP,也是在“求着”对方弹出验证码。
另一个问题是,我们看到的验证码,浏览器也看到了吗?打开浏览器开发者工具(F12),切换到Network(网络)标签,仔细查看触发验证码的请求。有时候,验证码的答案(Token)会直接藏在某个接口的响应里,或者生成逻辑完全由前端JavaScript计算完成。这种情况下,根本不用识别图形,只要正确重现前端逻辑就行。这类“验证码”本质是“逻辑题”,破解成本比图像识别低多了。
第二步:预处理是成功的一半——把“脏图”变“干净”
从网页上直接下载的验证码图片,大多不能直接扔给识别库。需要一套图像处理“流水线”把它标准化。
关键点在于,预处理没有标准答案,一张处理得当的图片,能够让后续的识别准确率提高数倍。
import cv2
import numpy as np
def preprocess_captcha(image_data):
# 1. 灰度化:降维,去掉颜色干扰
gray = cv2.cvtColor(image_data, cv2.COLOR_BGR2GRAY)
# 2. 二值化:将图像转换为纯粹的黑白,便于分离字符和背景
# 自适应阈值能更好地处理光照不均的图片
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2)
# 3. 降噪:去除孤立的小黑点(椒盐噪声)
kernel = np.ones((1, 1), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 4. 去干扰线:如果验证码有贯穿的横线或竖线,可取消注释使用
# kernel_line = np.ones((2, 2), np.uint8)
# cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel_line)
return cleaned
# 假设从网页获取了图片(实际使用时需补充下载逻辑)
# raw_image = cv2.imread("captcha.jpg") # 本地读取示例,也可对接网络下载
# processed_image = preprocess_captcha(raw_image)第三步:选择合适的“武器”——识别引擎的选型
图片处理干净后,就该选识别工具了。根据验证码复杂度和项目需求,有三种主流选择:
方案A:OCR通用引擎(快,但适用范围窄)
代表工具:pytesseract(Tesseract的Python封装)、百度/腾讯通用OCR API。
适用场景:字体标准、无扭曲、无粘连、背景极干净的验证码,比如部分企业内部系统或老旧网站的验证码。
优点:部署简单,识别速度快。
缺点:对变形、粘连的验证码几乎无效,准确率可能低于30%。
使用提示:一定要先做好预处理,还可以尝试通过pytesseract的config参数调整页面分割模式,比如--psm 7表示单行文本,有时能带来意外提升。
方案B:专用识别库(性价比之王)
代表工具:ddddocr,应对初、中级验证码的首选。
核心优势:内置训练好的模型,对数字、字母(大小写)、简单中文验证码识别效果出色,而且库体积轻量,无需GPU。本质是基于大量验证码数据预训练的CNN(卷积神经网络)模型,开箱即用。
优点:准确率高(针对目标类型可达90%+),速度和资源占用平衡得好,上手成本极低。
方案C:定制化深度学习模型(终极方案,但成本高)
代表架构:CNN(如ResNet)、CRNN等,需自行收集数据、标注、训练。
适用场景:字体独特、扭曲严重、干扰极强,且样式长期稳定的验证码,值得投入成本定制。
核心流程:
1. 数据收集:利用代理IP池低频率、长时间收集数千到数万张目标验证码图片;
2. 数据标注:可借助打码平台接口或半自动化工具生成初版标注,再人工校验;
3. 模型训练:用TensorFlow或PyTorch搭建训练模型,初期可直接用CNN做分类;
4. 部署集成:将训练好的模型封装成API,或直接集成到爬虫项目中。
第四步:构建稳健的自动化处理流程
除了识别外,还需要搭建一个闭环的自动化流程。
import requests
import ddddocr
import cv2
from your_ip_pool_manager import get_proxy, mark_proxy_temp_disabled # 需对接自身IP池管理模块
# 初始化OCR和预处理函数
ocr = ddddocr.DdddOcr(show_ad=False)
def preprocess_captcha(image_data):
gray = cv2.cvtColor(image_data, cv2.COLOR_BGR2GRAY)
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2)
kernel = np.ones((1, 1), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
return cleaned
def solve_and_request(url, captcha_url_parser, retry=3):
for attempt in range(retry):
# 1. 获取新鲜代理IP
proxy = get_proxy(type="http")
if not proxy:
print("未获取到可用代理,重试...")
continue
# 2. 发起请求,获取含验证码的页面(或触发验证码)
session = requests.Session()
session.proxies = {"http": proxy, "https": proxy}
try:
initial_resp = session.get(url, timeout=10)
initial_resp.raise_for_status() # 抛出HTTP错误
except requests.exceptions.RequestException as e:
print(f"请求失败:{e},标记IP暂不可用")
mark_proxy_temp_disabled(proxy)
continue
# 3. 解析页面,提取验证码图片URL(需根据目标网站定制解析逻辑)
image_url = captcha_url_parser(initial_resp.text)
if not image_url:
print("未触发验证码,请求成功")
return initial_resp # 未触发验证码,直接返回结果
# 4. 下载并识别验证码
try:
img_resp = session.get(image_url, timeout=10)
img_resp.raise_for_status()
# 转换为OpenCV可处理的格式
img_array = np.asarray(bytearray(img_resp.content), dtype=np.uint8)
raw_image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
processed_image = preprocess_captcha(raw_image)
captcha_code = ocr.classification(processed_image)
except Exception as e:
print(f"验证码处理失败:{e}")
mark_proxy_temp_disabled(proxy)
continue
# 5. 构造表单数据,提交验证码
post_data = {
"username": "your_user",
"password": "your_pass",
"captcha": captcha_code
}
try:
final_resp = session.post(url, data=post_data, timeout=10)
final_resp.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"提交验证码失败:{e}")
mark_proxy_temp_disabled(proxy)
continue
# 6. 验证请求是否成功
if "登录成功" in final_resp.text or final_resp.status_code == 200:
print(f"第{attempt+1}次尝试成功,使用代理:{proxy},识别码:{captcha_code}")
return final_resp
else:
print(f"第{attempt+1}次尝试失败,识别码可能错误,更换IP重试...")
mark_proxy_temp_disabled(proxy)
continue
print(f"重试{retry}次均失败,建议人工干预或升级识别方案")
return None需要注意的是:
1.代理IP跟验证码协同,一旦识别失败,就得更换新的IP以及新的验证码图片再去尝试,要是用同一个IP反复提交错误答案,只会加快IP被封禁的速度。
2.进行结果验证,必须得有判断提交成功与否的逻辑,像检查返回页的内容,或者检查状态码之类。
3.当连续失败达到N次之后,需要把相关任务转到“待处理队列”,触发人工处理,避免业务停滞。
应对初级图形验证码时,最重要的是“综合调理”:要先仔细观察验证码的特征,再探寻网络请求绕过的可能性,检查自身流程;同时要保证IP池处于稳定健康的状态、访问节奏尽可能地贴近真实人类的行为模式,最后用“预处理+ddddocr”直击要害。
