基于Selenium与PaddleOCR的图片小说自动化采集与识别方案 1. 项目概述从“看”到“读”的自动化小说采集最近在折腾一个挺有意思的自动化项目核心目标是把那些嵌在图片里的小说章节自动抓取并转换成可编辑的文本。这听起来像是把“看图说话”反过来变成“看字说话”。市面上很多小说网站尤其是某些为了防盗版或增加用户粘性的平台会把小说内容以图片形式呈现一页一页的让你没法直接复制粘贴。手动截图再识别效率太低。用爬虫直接抓面对图片只能干瞪眼。这个项目的核心思路就是结合Selenium自动化测试和OCR文字识别这两项技术模拟真人操作浏览器一页一页地“翻看”这些图片小说然后把看到的每一张图片“读”出来转换成文字最后整理成连贯的TXT文档。它解决的痛点非常明确自动化地、批量地获取那些以图片形式存在、无法通过常规HTML解析获取的文本内容。无论是技术爱好者想研究自动化流程还是小说爱好者想方便地离线阅读这个项目都提供了一个清晰的实现路径。整个流程可以拆解为三个核心环节导航与截图、图像处理与识别、文本整理与输出。听起来简单但每个环节都有不少细节和坑等着我们去填。接下来我就结合自己的实操经验把这套方案的思路、工具选型、具体实现步骤以及踩过的那些坑详细拆解一遍。2. 核心工具选型与思路拆解工欲善其事必先利其器。在这个项目里工具链的选择直接决定了实现的难易度和最终效果。我们的目标是搭建一个稳定、高效且易于维护的自动化流程。2.1 为什么是Selenium不仅仅是“自动化测试”提到Selenium很多人第一反应是Web自动化测试。没错它最初确实是为此而生。但它的核心能力——完全模拟用户在浏览器中的真实操作点击、输入、滚动、等待页面加载使其成为了处理复杂Web交互场景的利器远超测试范畴。对于我们的目标图片小说页面来说Selenium的优势无可替代处理动态加载很多小说网站采用瀑布流或点击“下一页”按钮动态加载新图片。Selenium可以精准定位并点击这些按钮模拟翻页。应对反爬机制简单的requests库请求可能被屏蔽。Selenium驱动真实浏览器行为与真人无异能有效绕过一些基于请求头或简单JS验证的反爬。精准元素定位我们需要找到小说图片所在的img标签或者承载图片的容器div。Selenium提供了丰富的定位方式ID、Class、XPath、CSS Selector可以稳定地找到目标。页面渲染与截图只有浏览器完全渲染了页面我们才能截取到包含完整小说图片的页面区域。Selenium可以轻松获取整个页面或特定元素的截图。工具选型我选择Python Selenium的组合。Python生态丰富语法简洁Selenium的Python绑定成熟稳定。浏览器驱动方面ChromeDriver是最主流、兼容性最好的选择配合无头模式Headless可以在后台静默运行不弹出浏览器窗口节省资源。2.2 OCR引擎的抉择Tesseract vs. PaddleOCR光学字符识别OCR是本项目的“大脑”负责把图片变成文字。这里有两个主流选择老牌的Tesseract和 后起之秀PaddleOCR。Tesseract由谷歌维护的开源OCR引擎历史悠久社区庞大。它的优点是安装相对简单对于清晰的印刷体英文识别率不错。但缺点也很明显对中文的支持需要单独下载语言包且对于复杂背景、低分辨率、艺术字体或排版特殊的图片如小说图片可能有的水印、边框识别准确率会大幅下降需要大量的预处理和后期调参。PaddleOCR百度飞桨推出的开源OCR工具包。它的最大优势在于针对中文场景做了深度优化基于深度学习模型识别准确率尤其是对中文印刷体、甚至一些手写体的识别远超Tesseract。它内置了多种预训练模型开箱即用效果好并且提供了丰富的Python API易于集成。我的选择与理由经过实际对比测试在这个“获取图片小说”的场景下我毫不犹豫地推荐PaddleOCR。原因如下准确率至上小说文本是连续性的一个字的识别错误可能导致整句语义不通。PaddleOCR的识别准确率显著更高大大减少了后期校对的工作量。开发效率高PaddleOCR的Python接口简单明了几行代码就能完成初始化、识别和结果获取省去了Tesseract繁琐的预处理和参数调优过程。对图片质量容错性强直接从网页截取的图片可能因为压缩、缩放而质量不高。PaddleOCR的深度学习模型对此有更好的鲁棒性。当然如果你的环境限制必须使用Tesseract也并非不可行但需要投入更多精力在图像预处理二值化、降噪、边框切除上后续我会提到一些通用技巧。2.3 整体架构设计明确了核心工具整个项目的架构就清晰了驱动层Selenium ChromeDriver负责浏览器自动化。采集层定位小说图片元素执行翻页操作并截取图片保存到本地。识别层PaddleOCR引擎读取采集到的图片进行文字识别。处理层对识别出的原始文本进行清洗去除识别错误符号、合并断行、分段根据章节标题并整理格式。输出层将处理好的文本按章节保存为.txt或.md文件。这个流程形成了一个自动化闭环启动脚本 - 浏览器访问 - 循环截图 - 翻页 - 截图...- 批量OCR识别 - 文本后处理 - 生成最终文件。3. 环境搭建与核心代码实现理论说得再多不如一行代码。下面我们进入实战环节一步步搭建环境并实现核心功能。3.1 基础环境配置首先确保你的Python环境建议3.7以上已经就绪。安装Selenium及相关驱动pip install selenium接下来需要下载与你的Chrome浏览器版本匹配的ChromeDriver。去官网或国内镜像站下载后将其所在目录添加到系统PATH环境变量或者直接在代码中指定驱动路径。我更推荐后者便于项目管理。安装PaddleOCRPaddleOCR的安装稍微复杂一点因为它依赖PaddlePaddle深度学习框架。# 首先安装PaddlePaddle CPU版本对于小说识别CPU版本通常够用速度也尚可 pip install paddlepaddle # 然后安装PaddleOCR pip install paddleocr2.0.1如果希望使用GPU加速识别速度更快需要安装CUDA版本的PaddlePaddle具体请参考PaddlePaddle官方文档。对于纯文本小说识别CPU版本足以应对。3.2 Selenium自动化采集模块实现这个模块的任务是自动访问小说页面并抓取每一页的图片。初始化浏览器驱动from selenium import webdriver from selenium.webdriver.chrome.options import Options 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 os def init_driver(chrome_driver_path, headlessTrue): 初始化Chrome浏览器驱动 :param chrome_driver_path: ChromeDriver可执行文件路径 :param headless: 是否使用无头模式不显示浏览器界面 :return: WebDriver对象 options Options() if headless: options.add_argument(--headless) # 无头模式 options.add_argument(--disable-gpu) # 禁用GPU某些环境下需要 options.add_argument(--no-sandbox) # 解决Linux下的一些权限问题 options.add_argument(--disable-dev-shm-usage) # 解决共享内存问题 # 可以添加User-Agent伪装进一步模拟真人 options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36) driver webdriver.Chrome(executable_pathchrome_driver_path, optionsoptions) driver.maximize_window() # 最大化窗口确保页面元素正常加载 return driver核心采集函数这个函数负责导航到起始页然后循环执行“截图-保存-翻页”的操作。def capture_novel_images(driver, start_url, output_dir, max_pages100): 抓取小说图片 :param driver: WebDriver对象 :param start_url: 小说起始页URL :param output_dir: 图片保存目录 :param max_pages: 最大抓取页数防止意外无限循环 if not os.path.exists(output_dir): os.makedirs(output_dir) driver.get(start_url) time.sleep(3) # 初始等待页面加载可根据网络情况调整 page_num 1 while page_num max_pages: try: # 1. 定位小说图片元素 - 这是最关键的一步需要手动分析目标网页 # 示例假设图片在一个id为‘novel-img’的img标签里 # 你需要使用浏览器的开发者工具F12查看实际页面的HTML结构 img_element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, novel-img)) ) # 也可能是CSS选择器 driver.find_element(By.CSS_SELECTOR, .content img) # 2. 截图并保存 screenshot_path os.path.join(output_dir, fpage_{page_num:03d}.png) img_element.screenshot(screenshot_path) # 直接对元素截图比全屏截图更精准 print(f已保存第 {page_num} 页: {screenshot_path}) # 3. 寻找并点击“下一页”按钮 # 同样需要分析页面找到‘下一页’按钮的定位器 next_button driver.find_element(By.XPATH, //a[contains(text(),下一页)]) # 或者通过ID/Class定位 # next_button driver.find_element(By.ID, next-page-btn) # 检查是否已是最后一页按钮可能变灰或消失 if not next_button.is_enabled() or not next_button.is_displayed(): print(已到达最后一页采集结束。) break next_button.click() page_num 1 # 4. 等待新页面加载完成 # 方式一固定等待简单但不稳定 # time.sleep(2) # 方式二显式等待直到新页面的图片元素出现推荐 WebDriverWait(driver, 10).until( EC.staleness_of(img_element) # 等待旧元素从DOM中消失 ) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, novel-img)) ) except Exception as e: print(f在第 {page_num} 页处理时发生错误: {e}) # 可以尝试其他翻页逻辑或保存当前状态后退出 break print(f图片采集完成共采集 {page_num-1} 页。)实操心得一元素定位是成败关键上面代码中的By.ID, novel-img和By.XPATH, //a[contains(text(),下一页)]只是示例。你必须亲自打开目标网站按F12打开开发者工具使用元素选择器仔细分析目标图片和翻页按钮的真实HTML结构。它们可能是img标签也可能是作为background-image的div。翻页按钮可能是一个a链接也可能是一个button甚至是通过JavaScript触发的span。定位不准整个自动化流程就无法进行。多准备几种定位策略ID、Class、XPath、CSS Selector并做好异常处理。3.3 PaddleOCR识别模块实现图片抓取完成后我们使用PaddleOCR进行批量识别。from paddleocr import PaddleOCR import cv2 import os def ocr_images(image_dir, output_text_path): 对指定目录下的所有图片进行OCR识别并合并文本 :param image_dir: 存放小说图片的目录 :param output_text_path: 合并后的文本输出路径 # 初始化PaddleOCR使用中英文识别模型启用字符分类提高准确率 # use_angle_clsTrue 可以自动判断文字方向对于扫描件很有用 # langch 指定中文识别 ocr PaddleOCR(use_angle_clsTrue, langch, use_gpuFalse) # use_gpu根据环境设置 all_text [] image_files sorted([f for f in os.listdir(image_dir) if f.lower().endswith((.png, .jpg, .jpeg))]) for img_name in image_files: img_path os.path.join(image_dir, img_name) print(f正在识别: {img_name}) # 使用PaddleOCR进行识别 result ocr.ocr(img_path, clsTrue) # 解析识别结果 page_text if result is not None: for line in result: # line 是一个列表每个元素是 (坐标框, (文本置信度)) # 我们只关心文本内容 text_info line[1] if text_info: # 确保有识别结果 text_content text_info[0] confidence text_info[1] # 可以设置一个置信度阈值过滤低置信度结果 if confidence 0.5: # 例如只保留置信度大于50%的识别结果 page_text text_content \n else: page_text [低置信度文本] \n else: page_text [识别失败] \n else: page_text [本页无识别内容或识别错误]\n all_text.append(f--- 第{img_name}页 ---\n{page_text}\n) # 将所有页的文本合并并写入文件 with open(output_text_path, w, encodingutf-8) as f: f.writelines(all_text) print(fOCR识别完成文本已保存至: {output_text_path})实操心得二PaddleOCR结果处理PaddleOCR返回的结果是一个嵌套列表结构为[[[坐标], (文本, 置信度)], ...]。它已经按行进行了初步分组这对于小说排版是友好的。上述代码简单地将每行文本拼接。在实际应用中你可能需要更精细的处理比如根据坐标信息判断段落Y坐标的跳变或者过滤掉页码、网站水印等非正文内容。confidence置信度是一个很好的参考指标可以用于初步的质量过滤。3.4 文本后处理与优化直接从OCR出来的文本通常是粗糙的包含不必要的换行、空格和可能的识别错误。一个简单的后处理脚本能极大提升可读性。import re def post_process_text(input_text_path, output_text_path): 对OCR生成的原始文本进行清洗和格式化 :param input_text_path: OCR原始文本路径 :param output_text_path: 处理后文本路径 with open(input_text_path, r, encodingutf-8) as f: raw_text f.read() # 1. 去除每行首尾的空格和制表符 lines [line.strip() for line in raw_text.splitlines()] # 2. 合并因OCR断行造成的短行例如一个句子被错误地识别成两行 # 策略如果一行非空且不以句号、问号、感叹号等结束且下一行也不以段落起始标志如章节名开头则合并 merged_lines [] i 0 while i len(lines): current_line lines[i] if i len(lines) - 1: merged_lines.append(current_line) break next_line lines[i 1] # 简单的合并规则当前行短于一定长度且下一行不是新段落/章节的开始 # 更复杂的规则可以基于标点符号和正则表达式 if (len(current_line) 30 and not re.match(r^第[零一二三四五六七八九十百千\d]章, next_line) and not re.match(r^[【(].*[】)]$, current_line)): # 过滤可能的小标题 merged_lines.append(current_line next_line) i 2 # 跳过下一行 else: merged_lines.append(current_line) i 1 # 3. 去除空行和仅包含特殊字符的行 cleaned_lines [] for line in merged_lines: # 移除行内的多余空格保留一个空格 line re.sub(r\s, , line) if line and not re.match(r^[\.\-\s\*]*$, line): # 过滤掉纯符号行 cleaned_lines.append(line) # 4. 根据特定规则重新分段例如遇到“第一章”、“第一节”等另起一段 formatted_text for line in cleaned_lines: if re.match(r^第[零一二三四五六七八九十百千\d][章节回], line): formatted_text \n\n line \n else: formatted_text line \n with open(output_text_path, w, encodingutf-8) as f: f.write(formatted_text.strip()) print(f文本后处理完成结果保存至: {output_text_path})这个后处理函数做了几件事去除空白字符、合并错误的断行、过滤垃圾行、并根据章节标题重新分段。规则可以根据你目标小说的具体排版风格进行调整比如有的小说用“**”表示场景转换有的用空行。4. 项目集成与完整流程封装现在我们把各个模块串联起来形成一个完整的、可执行的脚本。import argparse def main(): parser argparse.ArgumentParser(description图片小说自动化抓取与识别工具) parser.add_argument(--url, requiredTrue, help小说起始页URL) parser.add_argument(--output_dir, default./novel_output, help输出目录) parser.add_argument(--max_pages, typeint, default50, help最大抓取页数) parser.add_argument(--chrome_driver, requiredTrue, helpChromeDriver路径) parser.add_argument(--headless, actionstore_true, help使用无头模式) args parser.parse_args() # 创建输出子目录 img_dir os.path.join(args.output_dir, images) text_dir os.path.join(args.output_dir, text) raw_text_path os.path.join(text_dir, raw_text.txt) final_text_path os.path.join(text_dir, final_novel.txt) os.makedirs(img_dir, exist_okTrue) os.makedirs(text_dir, exist_okTrue) # 步骤1: 启动浏览器并采集图片 print(步骤1: 开始采集图片...) driver init_driver(args.chrome_driver, headlessargs.headless) try: capture_novel_images(driver, args.url, img_dir, max_pagesargs.max_pages) finally: driver.quit() # 确保浏览器被关闭 # 步骤2: OCR识别图片 print(\n步骤2: 开始OCR识别...) ocr_images(img_dir, raw_text_path) # 步骤3: 文本后处理 print(\n步骤3: 进行文本后处理...) post_process_text(raw_text_path, final_text_path) print(f\n全部流程完成最终小说文本位于: {final_text_path}) if __name__ __main__: main()使用方式很简单在命令行中运行python novel_crawler.py --url https://example.com/novel/page/1 --chrome_driver ./chromedriver --headless --max_pages 100这个脚本定义了完整的流水线参数解析 - 创建目录 - 采集图片 - OCR识别 - 文本清洗 - 输出结果。你可以根据需要增加更多功能比如断点续传、识别进度条、更复杂的翻页逻辑判断等。5. 常见问题与实战避坑指南在实际操作中你几乎一定会遇到下面这些问题。我把它们和解决方案整理出来希望能帮你节省大量调试时间。5.1 Selenium相关的问题与技巧问题1元素定位失败抛出NoSuchElementException或TimeoutException。原因页面结构动态变化、元素加载慢、使用了错误的定位器、页面内有iframe框架。解决方案使用显式等待WebDriverWait这是最重要的技巧。不要用固定的time.sleep而是等待某个特定条件成立如元素可见、可点击。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待最多10秒直到ID为‘myElement’的元素出现 element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, myElement)) ) # 或者等待元素可点击 element WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //button[typesubmit])) )尝试多种定位策略如果一个定位器失效尝试用其他属性。XPath虽然强大但可能脆弱优先使用稳定的ID或唯一的Class。检查iframe如果目标元素在iframe里必须先切换到对应的iframe框架内才能操作。driver.switch_to.frame(iframe_name_or_id) # ... 操作iframe内的元素 ... driver.switch_to.default_content() # 操作完切回主文档使用相对XPath或CSS选择器避免使用绝对路径如/html/body/div[3]/div[2]/img它们极易因页面微小改动而失效。问题2截图不完整或截到空白。原因截图时机不对元素未完全渲染或者截取的不是目标元素。解决方案截图前等待在对元素执行screenshot方法前确保它已完全加载。可以结合显式等待EC.visibility_of_element_located。滚动到元素如果元素不在可视区域内先滚动到它所在位置。driver.execute_script(arguments[0].scrollIntoView(true);, img_element) time.sleep(0.5) # 等待滚动完成 img_element.screenshot(path)验证截图可以简单检查截图文件的尺寸或像素如果异常如1x1像素则重试或记录错误。问题3被网站检测到自动化脚本。原因Selenium驱动浏览器会留下一些特征如window.navigator.webdriver属性为true。解决方案添加实验性选项在初始化Options时添加参数。options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False)执行CDP命令Chrome DevTools Protocol更彻底地隐藏自动化特征。driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })模拟人类行为在操作间加入随机延迟移动鼠标轨迹等。但注意不要过度影响效率。5.2 OCR识别相关的问题与技巧问题1识别准确率不高特别是标点符号和生僻字。原因图片质量差模糊、低对比度、有干扰、字体特殊、OCR模型对某些字符训练不足。解决方案图像预处理如果使用PaddleOCR大部分情况下不需要如果必须处理可以在识别前用OpenCV进行预处理。import cv2 img cv2.imread(img_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转灰度 # 二值化增强对比度 _, binary cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV) # 降噪 denoised cv2.medianBlur(binary, 3) cv2.imwrite(processed.png, denoised) # 保存处理后的图片供OCR识别使用PaddleOCR的更优模型PaddleOCR提供了多种模型ch_ppocr_server_v2.0通常比默认的ch_ppocr_mobile_v2.0识别率更高但体积更大、速度稍慢。初始化时可以指定ocr PaddleOCR(det_model_dir..., rec_model_dir..., cls_model_dir...)。后处理字典校正建立一个常见错误映射字典对识别结果进行替换。例如把“己”和“已”、“土”和“士”等易混字根据上下文进行校正。问题2识别出的文本顺序错乱。原因图片排版复杂如分栏、图文混排OCR引擎未能正确识别阅读顺序。解决方案利用PaddleOCR的坐标信息PaddleOCR返回的每个文本框都有四个顶点的坐标。你可以根据这些坐标的Y轴纵坐标进行主要排序行序再根据X轴横坐标进行次要排序行内字序。PaddleOCR的结果默认已经按行大致排序但对于复杂版面可能不够。手动指定识别区域ROI如果小说图片的正文区域是固定的可以在截图后先用图像处理库如OpenCV或PIL裁剪出只包含正文的区域再进行识别排除页眉、页脚、侧边栏的干扰。问题3处理速度慢尤其是大量图片时。原因CPU识别本身较慢或者网络图片下载耗时。解决方案启用GPU加速如果你的机器有NVIDIA GPU且安装了CUDA将PaddleOCR初始化的use_gpu参数设为True速度可提升数倍至数十倍。批量识别PaddleOCR支持批量图片输入将多张图片路径放入列表一次性传入比循环单张识别效率更高。优化采集流程确保Selenium在无头模式下运行减少资源占用。考虑将采集和识别分离成两个独立进程/线程实现流水线作业。5.3 流程与稳定性问题问题脚本运行中途崩溃如何断点续传解决方案在关键步骤记录状态。例如在保存图片时同时记录一个进度文件如progress.json里面保存已成功抓取的页码列表。每次启动脚本时先读取这个文件跳过已抓取的页码。对于OCR识别可以按页生成独立的文本文件最后再合并这样即使中间出错也只需重识别出错的那几页。问题目标网站改版导致脚本失效。解决方案这是自动化脚本的宿命。没有一劳永逸的方案。可以将定位元素的CSS选择器或XPath表达式提取到配置文件如config.yaml中而不是硬编码在脚本里。当网站改版时只需更新配置文件而无需修改核心代码。同时为脚本添加健全的日志系统记录每一步的操作和错误便于快速定位问题所在。6. 进阶优化与扩展思路当基础功能跑通后你可以考虑以下方向来提升项目的鲁棒性、效率和用户体验。6.1 引入更智能的翻页与结束判断基础的“下一页”按钮点击很脆弱。更健壮的方式可以是多策略翻页同时尝试多种翻页方式点击按钮、滚动到底部自动加载、键盘右键/空格键模拟。智能结束判断除了按钮不可用还可以通过判断图片内容是否重复、是否出现“完结”或“谢谢阅读”等特定文本通过OCR快速识别最后一页来终止循环。6.2 集成更强大的文本处理流程章节自动分割利用正则表达式智能识别“第X章”、“第X回”等模式自动将长文本分割成多个章节文件。命名实体识别NER可以接入简单的NLP工具识别出人名、地名并在后续阅读器中实现高亮或索引功能。文本校对结合语言模型如一些轻量级的本地LLM或规则对OCR结果进行自动校对修正明显的错别字和语法错误。6.3 构建图形化界面或Web服务使用PyQt、Tkinter或Streamlit等库为脚本包装一个简单的图形界面让非技术用户也能方便地使用。或者将其封装成一个Web API服务提供上传图片、返回文本的接口。6.4 应对更复杂的反爬策略如果目标网站加强了反爬你可能需要使用更高级的浏览器自动化工具如Playwright或Puppeteer它们能更好地模拟人类行为并提供更丰富的设备模拟选项。代理IP池在频繁访问时轮换IP地址。验证码识别如果遇到验证码可以集成第三方打码平台API或训练简单的验证码识别模型对于固定类型的验证码。这个项目是一个非常好的练手项目它串联了Web自动化、图像处理、OCR识别和文本处理等多个实用技术点。在实际操作中最耗费时间的往往不是编码而是针对特定目标网站的调试和适配。每一个网站都是一座独特的城堡你需要仔细观察它的城墙HTML结构和卫兵反爬机制才能找到那条自动化的通道。