Python 实现 RPA + AI 自动化:大模型 OCR + 网页操作完整源码实战

从环境搭建到生产部署,附可直接运行的完整代码。本文适合有 Python 基础的开发者、自动化工程师、以及对 RPA + AI 融合技术感兴趣的技术人员。


一、前言:为什么需要 RPA + AI 融合方案

做网页自动化这几年,踩过的坑比写的代码还多。去年帮公司做一个财务对账系统,每天登录内部系统、识别发票图片、汇总 Excel,本以为 Selenium + OCR 就能搞定,结果交付时客户问"能不能打包成双击就能用的软件"。后来调研了一圈国产工具,发现 蓝印 rpa 这类产品把网页自动化、AI 识别、打包分发整合得挺完整,走的是本地离线路线。不过本文先不聊工具,我们从 Python 源码层面把 RPA + AI 的完整链路跑通。理解了底层原理,再用工具提效,才是正确的技术路径。


二、技术架构与核心思路

2.1 整体架构

复制代码
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  网页操作层  │ ──→ │  AI 识别层  │ ──→ │  数据处理层  │
│  Selenium   │     │  OCR + LLM  │     │  Excel/DB   │
└─────────────┘     └─────────────┘     └─────────────┘

2.2 技术选型

模块 技术方案 说明
网页自动化 Selenium + WebDriver 模拟浏览器操作,支持动态页面
OCR 识别 PaddleOCR + 大模型 API 本地引擎 + 云端 AI 双保险
图像预处理 OpenCV 灰度化、二值化、去噪
数据导出 pandas + openpyxl 结构化数据输出
异常处理 显式等待 + 重试机制 提升稳定性

三、环境搭建

3.1 安装依赖

复制代码
# 创建虚拟环境
python -m venv rpa_ai_env
source rpa_ai_env/bin/activate  # Linux/Mac
# rpa_ai_env\Scripts\activate  # Windows

# 核心依赖
pip install selenium webdriver-manager
pip install paddleocr paddlepaddle
pip install opencv-python pillow
pip install pandas openpyxl requests

# 大模型 API(可选,用于复杂场景识别)
pip install openai  # 或其他 SDK

3.2 浏览器驱动配置

复制代码
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# 自动下载匹配版本的 ChromeDriver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

四、核心模块源码

4.1 网页自动化模块

复制代码
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
from selenium.webdriver.chrome.options import Options
import time
import random

class WebAutomation:
    """网页自动化操作类"""
    
    def __init__(self, headless=False):
        self.options = Options()
        if headless:
            self.options.add_argument("--headless")
        self.options.add_argument("--disable-blink-features=AutomationControlled")
        self.options.add_argument("--start-maximized")
        
        self.driver = webdriver.Chrome(options=self.options)
        self.wait = WebDriverWait(self.driver, 10)
    
    def navigate(self, url):
        """访问目标网页"""
        self.driver.get(url)
        # 随机延迟,模拟真人操作
        time.sleep(random.uniform(1, 3))
    
    def find_element(self, by, value):
        """显式等待查找元素"""
        return self.wait.until(EC.presence_of_element_located((by, value)))
    
    def input_text(self, by, value, text):
        """输入文本"""
        element = self.find_element(by, value)
        element.clear()
        # 模拟逐字输入
        for char in text:
            element.send_keys(char)
            time.sleep(random.uniform(0.05, 0.15))
    
    def click(self, by, value):
        """点击元素"""
        element = self.wait.until(EC.element_to_be_clickable((by, value)))
        element.click()
        time.sleep(random.uniform(0.5, 1.5))
    
    def get_screenshot(self, filepath):
        """截取当前页面"""
        self.driver.save_screenshot(filepath)
        return filepath
    
    def scroll_to_element(self, by, value):
        """滚动到指定元素"""
        element = self.find_element(by, value)
        self.driver.execute_script("arguments[0].scrollIntoView();", element)
        time.sleep(0.5)
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()

4.2 OCR 识别模块(本地 + 大模型双方案)

复制代码
import cv2
import numpy as np
from paddleocr import PaddleOCR
from PIL import Image
import requests
import base64

class OCRProcessor:
    """OCR 处理器:支持本地引擎和大模型 API"""
    
    def __init__(self, use_paddle=True, llm_api_key=None):
        self.use_paddle = use_paddle
        self.llm_api_key = llm_api_key
        
        if use_paddle:
            # 初始化 PaddleOCR,支持中英文
            self.ocr = PaddleOCR(
                use_angle_cls=True,
                lang='ch',
                show_log=False
            )
    
    def preprocess_image(self, image_path):
        """图像预处理:提升 OCR 准确率"""
        img = cv2.imread(image_path)
        if img is None:
            raise ValueError(f"无法读取图片: {image_path}")
        
        # 灰度化
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # 自适应二值化
        binary = cv2.adaptiveThreshold(
            gray, 255, 
            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY, 11, 2
        )
        
        # 去噪
        denoised = cv2.fastNlMeansDenoising(binary, None, 10, 7, 21)
        
        processed_path = image_path.replace('.', '_processed.')
        cv2.imwrite(processed_path, denoised)
        return processed_path
    
    def recognize_local(self, image_path):
        """本地 PaddleOCR 识别"""
        processed = self.preprocess_image(image_path)
        result = self.ocr.ocr(processed, cls=True)
        
        texts = []
        if result and result[0]:
            for line in result[0]:
                text, confidence = line[1]
                texts.append({
                    'text': text,
                    'confidence': confidence,
                    'bbox': line[0]
                })
        return texts
    
    def recognize_with_llm(self, image_path):
        """大模型 OCR 识别(以通用 API 为例)"""
        with open(image_path, 'rb') as f:
            image_base64 = base64.b64encode(f.read()).decode()
        
        # 调用大模型 API 进行图像识别
        # 这里以通用接口为例,实际使用时替换为具体平台
        headers = {
            'Authorization': f'Bearer {self.llm_api_key}',
            'Content-Type': 'application/json'
        }
        
        payload = {
            'model': 'gpt-4-vision',
            'messages': [{
                'role': 'user',
                'content': [
                    {'type': 'text', 'text': '请识别图片中的所有文字,按行输出'},
                    {'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_base64}'}}
                ]
            }]
        }
        
        # 实际请求代码(示例)
        # response = requests.post('https://api.example.com/v1/chat/completions', 
        #                          headers=headers, json=payload)
        # return response.json()
        
        # 模拟返回
        return [{'text': '大模型识别结果示例', 'confidence': 0.95}]
    
    def recognize(self, image_path, use_llm=False):
        """统一识别入口"""
        if use_llm and self.llm_api_key:
            return self.recognize_with_llm(image_path)
        return self.recognize_local(image_path)

4.3 数据提取与导出模块

复制代码
import pandas as pd
import re
from datetime import datetime

class DataExtractor:
    """数据提取与结构化"""
    
    @staticmethod
    def extract_invoice_info(ocr_results):
        """从 OCR 结果中提取发票信息"""
        full_text = ' '.join([r['text'] for r in ocr_results])
        
        # 正则提取关键字段
        patterns = {
            'invoice_no': r'发票号码[::]\s*(\d+)',
            'date': r'开票日期[::]\s*(\d{4}年\d{2}月\d{2}日)',
            'amount': r'(?:金额|价税合计)[::]\s*¥?\s*(\d+\.?\d*)',
            'seller': r'销售方[::]\s*(.+?)(?=\n|$)',
        }
        
        extracted = {}
        for key, pattern in patterns.items():
            match = re.search(pattern, full_text)
            extracted[key] = match.group(1) if match else ''
        
        return extracted
    
    @staticmethod
    def export_to_excel(data_list, output_path):
        """导出到 Excel"""
        df = pd.DataFrame(data_list)
        
        # 添加时间戳列
        df['export_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
            df.to_excel(writer, sheet_name='Data', index=False)
            
            # 调整列宽
            worksheet = writer.sheets['Data']
            for column in worksheet.columns:
                max_length = max(len(str(cell.value)) for cell in column)
                worksheet.column_dimensions[column[0].column_letter].width = max_length + 2
        
        return output_path

4.4 主流程整合

复制代码
import os
from datetime import datetime

class RPAAutomation:
    """RPA + AI 自动化主流程"""
    
    def __init__(self, llm_api_key=None):
        self.web = WebAutomation()
        self.ocr = OCRProcessor(llm_api_key=llm_api_key)
        self.extractor = DataExtractor()
        self.results = []
    
    def process_invoice_page(self, url, username, password):
        """处理发票页面完整流程"""
        try:
            # 1. 登录
            self.web.navigate(url)
            self.web.input_text(By.ID, 'username', username)
            self.web.input_text(By.ID, 'password', password)
            self.web.click(By.ID, 'login-btn')
            
            # 2. 进入发票列表
            self.web.click(By.XPATH, '//a[contains(text(), "发票管理")]')
            time.sleep(2)
            
            # 3. 获取所有发票图片
            invoice_items = self.web.driver.find_elements(
                By.CSS_SELECTOR, '.invoice-item'
            )
            
            for i, item in enumerate(invoice_items):
                # 点击展开详情
                item.click()
                time.sleep(1)
                
                # 截图保存
                screenshot_path = f'temp_invoice_{i}.png'
                self.web.get_screenshot(screenshot_path)
                
                # OCR 识别
                ocr_results = self.ocr.recognize(screenshot_path)
                
                # 提取结构化数据
                info = self.extractor.extract_invoice_info(ocr_results)
                info['source_image'] = screenshot_path
                self.results.append(info)
                
                # 返回列表
                self.web.click(By.CLASS_NAME, 'back-btn')
                time.sleep(1)
            
            # 4. 导出结果
            output = self.extractor.export_to_excel(
                self.results, 
                f'invoice_data_{datetime.now().strftime("%Y%m%d")}.xlsx'
            )
            print(f"处理完成,结果已保存至: {output}")
            
        finally:
            self.web.close()
    
    def run(self, config):
        """批量执行入口"""
        for task in config['tasks']:
            self.process_invoice_page(
                task['url'],
                task['username'],
                task['password']
            )

# 使用示例
if __name__ == '__main__':
    config = {
        'tasks': [{
            'url': 'https://example.com/login',
            'username': 'your_username',
            'password': 'your_password'
        }]
    }
    
    automation = RPAAutomation(llm_api_key='your_api_key')
    automation.run(config)

五、进阶:AI 大模型增强识别

5.1 为什么需要大模型?

传统 OCR 在以下场景表现不佳:

  • 手写体、潦草字体

  • 复杂表格布局

  • 印章遮挡文字

  • 多语言混合

大模型(如 GPT-4V、文心一言、DeepSeek 等)具备更强的语义理解能力,可以结合上下文推断模糊文字。

5.2 接入方案

复制代码
class LLMOCRAdapter:
    """大模型 OCR 适配器"""
    
    def __init__(self, provider='deepseek'):
        self.provider = provider
        self.endpoints = {
            'deepseek': 'https://api.deepseek.com/v1/chat/completions',
            'kimi': 'https://api.moonshot.cn/v1/chat/completions',
            'wenxin': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions',
        }
    
    def recognize(self, image_path, prompt='请识别图片中的所有文字'):
        with open(image_path, 'rb') as f:
            image_data = base64.b64encode(f.read()).decode()
        
        payload = {
            'model': 'deepseek-v4',  # 或其他模型
            'messages': [{
                'role': 'user',
                'content': [
                    {'type': 'text', 'text': prompt},
                    {'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_data}'}}
                ]
            }]
        }
        
        # 实际调用
        # response = requests.post(self.endpoints[self.provider], ...)
        # return response.json()['choices'][0]['message']['content']
        return "大模型识别结果"

六、稳定性优化与异常处理

6.1 元素定位失效问题

复制代码
from selenium.webdriver.common.by import By

class SmartLocator:
    """智能元素定位:多策略容错"""
    
    STRATEGIES = [
        (By.ID, 'id'),
        (By.NAME, 'name'),
        (By.CLASS_NAME, 'class'),
        (By.CSS_SELECTOR, 'css'),
        (By.XPATH, 'xpath'),
    ]
    
    @classmethod
    def find_element(cls, driver, selectors):
        """多策略尝试定位"""
        for by, value in cls.STRATEGIES:
            if value in selectors:
                try:
                    return driver.find_element(by, selectors[value])
                except:
                    continue
        raise Exception("所有定位策略均失败")

6.2 重试机制

复制代码
import functools

def retry(max_attempts=3, delay=2):
    """重试装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    time.sleep(delay * (attempt + 1))
            return None
        return wrapper
    return decorator

# 使用
@retry(max_attempts=3)
def unstable_operation():
    pass

七、从代码到产品:打包与交付

写完代码只是第一步,真正的挑战是怎么交付给非技术同事使用。

7.1 传统方案的痛点

  • 需要安装 Python 环境

  • 依赖库版本冲突

  • 浏览器驱动版本不匹配

  • 源码暴露,知识产权难保护

7.2 更优的交付思路

这里分享一个我在实际项目中验证过的方案。之前用 蓝印 rpa 做过一个发票自动识别工具,它的打包方式给了我很大启发:支持将自动化流程直接导出为 EXE 可执行文件,而且支持授权机制、在线推送更新、API 触发 + 定时执行、数据本地存储、自定义界面。另外它在 AI 这块也做了不少整合,内置了文心一言、豆包、DeepSeek、Kimi 等大模型接入,支持图片识图和 OCR 功能,AI 费用采用用户自行对接 API 的方式,还新增了 Agent 功能。

这些能力用纯 Python 实现起来非常麻烦,需要 PyInstaller 打包、Inno Setup 做安装程序、自己写更新逻辑、还要处理各种兼容性问题。如果项目周期紧,或者需要频繁交付给客户,用工具整合这些能力是更务实的选择。


八、完整项目结构

复制代码
rpa_ai_project/
├── core/
│   ├── __init__.py
│   ├── web_automation.py      # 网页自动化
│   ├── ocr_processor.py        # OCR 识别
│   └── data_extractor.py     # 数据提取
├── adapters/
│   └── llm_ocr.py            # 大模型适配
├── utils/
│   ├── retry.py               # 重试机制
│   └── logger.py              # 日志
├── config/
│   └── tasks.yaml             # 任务配置
├── main.py                    # 入口
├── requirements.txt
└── README.md

九、总结与建议

9.1 技术路线选择

场景 推荐方案
学习研究、高度定制 Python 源码方案(本文)
快速交付、非技术用户使用 蓝印 rpa 等工具打包 EXE
敏感数据、内网环境 本地离线 RPA 工具
需要 AI 增强识别 大模型 API + RPA 结合

9.2 关键要点

  1. 网页自动化优先用显式等待,硬编码 sleep 是稳定性杀手

  2. OCR 一定要做图像预处理,灰度化 + 二值化能显著提升准确率

  3. 异常处理比功能代码更重要,生产环境没有重试机制等于不可用

  4. 交付时考虑用户体验,能双击运行的 EXE 比源码仓库更实用


十、参考与扩展

  • Selenium 官方文档

  • PaddleOCR GitHub 仓库

  • OpenCV 图像处理教程

如果你需要把这套方案快速落地成可交付的产品,可以关注 rpa 的打包分发能力,它把"代码 → 产品"这个最后一公里解决得挺干净。


版权声明: 本文为技术分享,代码可自由使用和学习。如需转载,请注明出处。

相关推荐
运维小欣1 小时前
AI可观测平台选型指南(2026深度版):从“救火”到“智治”,企业如何选择新一代智能运维底座?
人工智能
Keller-Zhou1 小时前
实体零售货架商品图像识别技术选型:从模型到落地的全链路对比
人工智能
朱大喜1 小时前
数据仓库从零搭建:从分层建模到数据治理的工程化落地
人工智能
闪闪发亮的小星星1 小时前
轨道的不同分类
人工智能·分类·数据挖掘
stephon_1001 小时前
从零设计 Agent 上下文压缩:三级流水线与动态阈值,治好 context too long(附开源实现)
人工智能·python
志栋智能1 小时前
超自动化安全的实施路径:从单点场景到体系化建设
运维·网络·安全·自动化
love530love1 小时前
Anaconda Navigator 升级后图形界面启动失败故障修复实录
人工智能·windows·python·anaconda·navigator
bIo7lyA8v1 小时前
算法稳定性分析的参数敏感性建模研究的技术7
人工智能
爱睡懒觉的焦糖玛奇朵1 小时前
【视觉检测之人员奔跑检测算法开发思路】
人工智能·python·深度学习·算法·yolo·视觉检测