OCR 识别图形验证码

从基础预处理到PaddleOCR/Selenium 4 自动化全流程(2024版)


1. 验证码技术概述

随着网络安全意识的提升,各类网站的反爬虫体系迭代越来越快——验证码(CAPTCHA) 作为第一道「人机鉴别门」,技术形态也早已跳出了纯字符组合的范畴:

  1. 初代基础款:纯数字/纯字母/大小写混排,无明显干扰
  2. 轻量增强款:加入倾斜、旋转、干扰点线、随机背景
  3. 中文本地化款:使用常用汉字/生僻汉字,或加汉字组语义干扰
  4. 行为复杂款:如12306的「点选同类物体」「点击文字补全诗句」
  5. AI 辅助交互款:滑动拼图(含缺口检测)、轨迹识别验证

本次教程聚焦于前三种纯静态图形验证码的通用识别方案,复杂交互验证码将单独撰文分享。


2. 图形验证码识别技术基础

2.1 OCR 技术选型参考

OCR(光学字符识别)的核心是把图像中的「视觉文字信息」转成「可编辑文本数据」。2024年做轻量级爬虫,没必要从零搭模型,这几个方案按易用性→准确率→成本排序即可:

方案类型代表工具/API适用场景2024年现状
开源传统引擎Tesseract(5.x+)纯基础、干扰极少的验证码适合练手,准确率下限波动
开源深度学习库PaddleOCR(2.x+)轻量增强/中文本地化的验证码国内用户首选,准确率高
商业API百度/腾讯/阿里云OCR轻量/中等难度验证码批量处理准确率最高,有免费额度

2.2 轻量级开发环境准备

Python 3.9+ 是目前爬虫+OCR的稳定组合,依赖库按方案拆分安装:

通用依赖(必装)

pip install selenium pillow numpy opencv-python

方案1:Tesseract 练手依赖

# 1. 安装 Python 封装库
pip install pytesseract

# 2. 安装引擎本体(系统不同命令不同)
# Windows(需先安装 Chocolatey 包管理器)
choco install tesseract
# Mac(需先安装 Homebrew)
brew install tesseract
# Linux(Debian/Ubuntu 系列)
sudo apt install tesseract-ocr

方案2:PaddleOCR 实战推荐依赖

# CPU 版(适合个人电脑跑)
pip install paddleocr paddlepaddle

# GPU 版(适合批量处理,需匹配 CUDA 版本)
# 参考 PaddlePaddle 官网:https://www.paddlepaddle.org.cn/install/quick

3. 验证码识别全流程实战

3.1 热身:直接用 Tesseract 识别「无干扰基础款」

基础款验证码甚至不需要预处理,但识别率会受字体倾斜/颜色影响:

import pytesseract
from PIL import Image

def tesseract_basic(image_path: str) -> str:
    """直接用 Tesseract 识别纯基础款验证码"""
    # 打开图像
    img = Image.open(image_path)
    # 配置 Tesseract(限定字母数字组合、单块识别)
    config = r"--psm 7 --oem 3 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    # 识别并去除首尾空白
    return pytesseract.image_to_string(img, config=config).strip()

# 测试
if __name__ == "__main__":
    print(tesseract_basic("simple_captcha.png"))

3.2 关键步骤:OpenCV 验证码预处理(适配轻量增强款)

轻量增强款的核心干扰是「背景噪点/线」「颜色混乱」——预处理的目标就是把文字和背景彻底分离,只保留「清晰的黑白文字块」:

import cv2
import numpy as np

def opencv_preprocess(image_path: str) -> np.ndarray:
    """轻量增强款验证码通用预处理"""
    # 1. 读取图像
    img = cv2.imread(image_path)
    # 2. 灰度化(减少计算量,突出明暗对比)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 3. 自适应阈值二值化(比固定阈值更适配背景渐变)
    thresh = cv2.adaptiveThreshold(
        gray, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,  # 高斯加权局部阈值
        cv2.THRESH_BINARY_INV,  # 黑白反转,让文字变成白色(255)
        11,  # 邻域块大小(必须是奇数,11是通用经验值)
        2    # 阈值偏移量(通用经验值)
    )
    # 4. 中值滤波去噪(适合去除椒盐噪点/干扰线断点)
    denoised = cv2.medianBlur(thresh, 3)
    # 5. 形态学闭操作(填补文字小缺口,可选但建议加)
    kernel = np.ones((2, 2), np.uint8)
    processed = cv2.morphologyEx(denoised, cv2.MORPH_CLOSE, kernel)
    
    # 调试时可以保存/显示预处理结果
    # cv2.imwrite("processed_captcha.png", processed)
    # cv2.imshow("Processed", processed)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()
    
    return processed

3.3 2024 实战首选:PaddleOCR 识别带预处理的验证码

PaddleOCR 的预训练模型已经覆盖了中英文/常见干扰的场景,配合预处理准确率能到90%以上:

from paddleocr import PaddleOCR
import cv2

# 全局初始化一次 PaddleOCR(首次运行会自动下载中英文轻量模型,约10MB)
# use_angle_cls=True:识别旋转180°的文字
# lang="en":优先英文识别;lang="ch":中英文混合识别
ocr = PaddleOCR(use_angle_cls=True, lang="en", show_log=False)  # show_log=False 关闭冗余日志

def paddle_ocr_processed(processed_img: np.ndarray) -> str:
    """识别预处理后的验证码"""
    # 调用 OCR 接口,result 格式是 [[[[x1,y1],[x2,y2],[x3,y3],[x4,y4]], ('text', confidence)]]
    result = ocr.ocr(processed_img, cls=True)
    # 只取第一块文字(验证码通常是单块连续的)
    if result and result[0]:
        text, confidence = result[0][0][1]
        # 可选:过滤低置信度结果
        # if confidence < 0.8:
        #     return ""
        return text.strip()
    return ""

# 组合预处理+识别
def full_paddle_ocr(image_path: str) -> str:
    processed = opencv_preprocess(image_path)
    return paddle_ocr_processed(processed)

# 测试
if __name__ == "__main__":
    print(full_paddle_ocr("enhanced_captcha.png"))

4. 自动化登录实战(Selenium 4 + PaddleOCR)

以「scrape.center」的模拟登录验证码网站为例,把整个流程串起来:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import random
from full_paddle_ocr import full_paddle_ocr  # 假设上面的代码存为 full_paddle_ocr.py

def auto_login_scrape_center():
    """scrape.center 模拟登录全流程"""
    # 1. 初始化 Selenium 4 浏览器(推荐用 Service 统一管理驱动)
    options = webdriver.ChromeOptions()
    # 可选:伪装浏览器指纹(防简单的反爬)
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])  # 隐藏自动化标识
    options.add_experimental_option("useAutomationExtension", False)
    
    service = webdriver.ChromeService()
    driver = webdriver.Chrome(service=service, options=options)
    
    try:
        # 2. 访问目标网站
        driver.get("https://captcha7.scrape.center/")
        time.sleep(random.uniform(1, 2))  # 随机延迟,模拟真人操作
        
        # 3. 输入用户名密码
        username_input = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.NAME, "username"))
        )
        username_input.send_keys("admin")
        time.sleep(random.uniform(0.5, 1))
        
        password_input = driver.find_element(By.NAME, "password")
        password_input.send_keys("admin")
        time.sleep(random.uniform(0.5, 1))
        
        # 4. 获取并识别验证码
        captcha_img = driver.find_element(By.CSS_SELECTOR, ".captcha img")
        # 直接用 element.screenshot() 截图,避免整页截图后裁剪
        captcha_img.screenshot("current_captcha.png")
        code = full_paddle_ocr("current_captcha.png")
        print(f"识别到的验证码:{code}")
        
        # 5. 输入验证码并提交
        captcha_input = driver.find_element(By.NAME, "captcha")
        captcha_input.send_keys(code)
        time.sleep(random.uniform(0.5, 1))
        
        submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
        submit_btn.click()
        
        # 6. 验证登录成功
        WebDriverWait(driver, 10).until(
            EC.text_to_be_present_in_element((By.TAG_NAME, "h2"), "登录成功")
        )
        print("🎉 登录成功!")
        
    finally:
        # 7. 关闭浏览器
        time.sleep(2)
        driver.quit()

if __name__ == "__main__":
    auto_login_scrape_center()

5. 轻量级反反爬技巧(2024更新)

识别验证码只是第一步,还要避免被网站的「其他反爬虫规则」封掉:

  1. 随机延迟:所有操作都加 random.uniform(a, b) 延迟,不要毫秒级执行
  2. IP轮换:如果频繁请求,用免费/付费代理池(推荐付费,免费的可用性太低)
    options.add_argument('--proxy-server=http://your_proxy_ip:port')
  3. 验证码错误重试:PaddleOCR 也不是100%准确,加个3-5次的重试逻辑
  4. 减少请求频率:如果是批量爬取,控制在每分钟10-20次以内

6. 总结与展望

本次教程分享了2024年轻量级爬虫识别纯静态图形验证码的全流程最佳实践——配合OpenCV预处理+PaddleOCR预训练模型,大部分网站的轻量增强/中文验证码都能搞定。

如果遇到特别复杂的验证码(比如生僻字密集、扭曲变形严重),可以:

  1. 尝试百度/腾讯的商业API
  2. 收集该网站的验证码数据,训练专用的小样本模型

参考资料