声明:本文技术方案仅供学习研究使用,请遵守目标网站的服务条款。


一、为什么放弃Selenium?

去年接了个需求:每天帮运营往后台系统录几百条数据。第一反应是用Selenium,结果Chrome自动升级后chromedriver版本对不上,生产环境挂了两次。查日志、下驱动、配路径,一套下来半小时没了。

后来同事推荐了DrissionPage 4.x,试了一周,几个痛点确实解决了:

  • 驱动问题:不需要手动管chromedriver,开箱自动匹配版本

  • 定位写法:支持text=提交这种按文本定位,比XPath直观,页面结构微调时不容易失效

  • 等待逻辑:内置智能等待,不用写一堆WebDriverWait

  • 双模式:复杂页面用浏览器模式,简单接口切requests模式直接抓数据

安装就一行:

pip install DrissionPage

注意:4.x版本底层完全自研,不依赖Selenium,跨iframe查找也不需要手动切换,后面会讲到。


二、实战:B端后台表单自动填报

最常见的流程:打开后台 → 登录 → 进表单页 → 填字段 → 提交 → 保存结果。这个流程里几乎涵盖了所有表单交互类型。

我封装了一个FormAutoFiller类,设计原则是每个方法只干一件事,返回布尔值,外层好做流程控制。fill_text_input里的simulate_human参数会在输入时加随机延迟,模拟真实打字节奏,有些后台有风控,这个能规避一部分。

from DrissionPage import Chromium
import time, random
from pathlib import Path

class FormAutoFiller:
    def __init__(self):
        self.browser = Chromium()
        self.tab = self.browser.latest_tab
        self.tab.set.timeouts(base=10)

    def fill_text_input(self, selector, value, simulate_human=True):
        try:
            ele = self.tab.ele(selector)
            ele.clear()
            if simulate_human:
                for char in str(value):
                    ele.input(char)
                    time.sleep(random.uniform(0.05, 0.15))
            else:
                ele.input(str(value))
            return True
        except Exception as e:
            print(f"填写失败 {selector}: {e}")
            return False

    def select_dropdown(self, selector, option_text):
        try:
            self.tab.ele(selector).click()
            time.sleep(0.5)
            self.tab.ele(f'text={option_text}').click()
            return True
        except Exception as e:
            print(f"下拉选择失败 {selector}: {e}")
            return False

    def select_radio(self, selector):
        try:
            self.tab.ele(selector).click()
            return True
        except Exception as e:
            print(f"单选失败 {selector}: {e}")
            return False

    def select_checkbox(self, selector, checked=True):
        try:
            checkbox = self.tab.ele(selector)
            if checked != checkbox.prop('checked'):
                checkbox.click()
            return True
        except Exception as e:
            print(f"复选框失败 {selector}: {e}")
            return False

    def fill_textarea(self, selector, value):
        try:
            ele = self.tab.ele(selector)
            ele.clear()
            ele.input(value)
            return True
        except Exception as e:
            print(f"文本域失败 {selector}: {e}")
            return False

    def upload_file(self, selector, file_path):
        if not Path(file_path).exists():
            print(f"文件不存在: {file_path}")
            return False
        try:
            self.tab.ele(selector).input(file_path)
            return True
        except Exception as e:
            print(f"上传失败 {selector}: {e}")
            return False

    def click_button(self, selector, human_like=True):
        try:
            btn = self.tab.ele(selector)
            if human_like:
                btn.click.at(offset_x=random.randint(-3, 3), 
                            offset_y=random.randint(-3, 3))
            else:
                btn.click()
            return True
        except Exception as e:
            print(f"点击失败 {selector}: {e}")
            return False

    def take_screenshot(self, filename):
        try:
            self.tab.get_screenshot(path=filename, full_page=True)
            return True
        except Exception as e:
            print(f"截图失败: {e}")
            return False

    def close(self):
        self.browser.quit()

使用流程:创建实例 → 打开页面 → 依次调用填写方法 → 提交 → 截图留痕 → 关闭。每个方法都加了异常捕获,外层可以根据返回值决定是否继续执行。


三、元素定位:哪种写法最稳?

DrissionPage的ele()支持多种选择器,以下是我项目里验证过的高稳定性写法:

方式 示例 适用场景
CSS选择器 tab.ele('#username') 有固定id,性能最好
文本定位 tab.ele('text=提交') 按钮、链接,抗结构变化
属性定位 tab.ele('@name=username') 没有id但有稳定属性
组合定位 form.ele('input[type="text"]') 先定位父元素再缩小范围

一个踩坑经验:React/Vue项目里class名经常是动态的(比如css-1a2b3c),但nameplaceholder这些业务属性通常不会变。所以优先用text=@属性名=,比XPath维护成本低。

批量处理时可以用tab.eles('.list-item')返回列表,配合循环处理。


四、动态加载与iframe:4.x版本省了不少事

4.1 等待元素

AJAX动态加载的页面,DrissionPage内置了智能等待,但复杂场景还是要手动兜底:

# 等待元素出现
tab.wait.ele_displayed('#target', timeout=10)
# 等待元素可点击
tab.wait.ele_loaded('#btn', timeout=10)

4.2 iframe处理(4.x改进很大)

以前用Selenium处理嵌套iframe,切来切去代码很啰嗦。DrissionPage 4.x支持跨iframe直接查找:

# 直接定位iframe内的元素,不需要手动switch
tab.ele('t:iframe')

这个在处理多层嵌套的后台系统时很实用,省了不少代码。


五、批量数据驱动:Excel自动填报

实际数据往往来自Excel,配合pandas实现批量处理:

import pandas as pd
from DrissionPage import Chromium

def batch_fill_from_excel(excel_path, url):
    df = pd.read_excel(excel_path)
    browser = Chromium()
    tab = browser.latest_tab
    tab.get(url)
    
    for index, row in df.iterrows():
        tab.ele('#name').input(row['姓名'])
        # 注意:手机号在Excel里可能是数字类型,必须转字符串
        tab.ele('#phone').input(str(row['手机号']))
        tab.ele('#email').input(row['邮箱'])
        tab.ele('#submit').click()
        
        # 等待提交成功提示
        tab.wait.ele_displayed('#success-msg', timeout=5)
        tab.ele('#add-new').click()
        time.sleep(1)
    
    browser.quit()

重点提醒str(row['手机号'])这个转换不要漏。Excel里手机号默认是数字,直接input()会报错,这个坑我踩过两次。


六、工程化:从脚本到交付

6.1 打包成可执行文件

脚本自己跑没问题,但交给运营同事用,必须打包成EXE。对比过几个方案:

方案 优点 缺点 适用场景
PyInstaller 简单直接,文档多 体积大(50-200MB),部分杀毒软件误报 快速交付、内部使用
Nuitka 编译成C,运行快 配置稍复杂,部分库兼容性需测试 性能敏感场景
cx_Freeze 跨平台支持好 社区相对小,资料少 需要Win/Mac双平台

PyInstaller命令:

pip install pyinstaller
pyinstaller --onefile --windowed form_autofiller.py

体积大是因为打包了Python解释器和依赖库,这是trade-off,没办法。

6.2 加个GUI界面

用tkinter做个简单面板,核心思路:StringVar绑定输入框,"浏览"按钮弹文件选择,"开始"按钮开后台线程跑自动化(避免界面卡死),日志区用scrolledtext实现。

布局分四块:文件选择、网址输入、运行按钮、日志显示。代码量约80行,够用就行,内部工具不要过度设计。


七、常见问题排查

7.1 元素定位不到?

  • 在iframe里?→ 4.x支持直接跨iframe查找:tab.ele('t:iframe')

  • 还没加载出来?→ 加显式等待:tab.wait.ele_displayed('#target', timeout=10)

  • 选择器错了?→ 用开发者工具检查,或用tab.ele()返回值是否为None判断

7.2 被风控拦截?

  • 设置User-Agent模拟真实浏览器:co.set_user_agent('Mozilla/5.0...')

  • 设置代理分散来源:co.set_proxy('http://代理地址:端口')

  • 降低操作频率,前面fill_text_inputsimulate_human参数就是干这个的

7.3 文件上传失败?

新手常见错误:点击上传按钮,结果弹出了系统文件对话框,自动化控制不了操作系统层面的UI。

正确做法:直接给<input type="file">元素传路径:

tab.ele('input[type="file"]').input('/path/to/file.pdf')

错误做法

tab.ele('#upload-btn').click()  # 不要这样做

八、工程化延伸:脚本多了怎么管?

写多了自动化脚本,几个现实问题会冒出来:

脚本分散:今天写填报,明天写采集,过段时间自己都找不到。建议按业务场景建仓库,统一管。

给同事用门槛高:不是每个人都懂Python。tkinter做GUI + PyInstaller打包成EXE是必要的交付方式。

网页改版维护难:选择器失效要一个个改。建议把选择器集中放到config文件里,不要硬编码,改版时改一处就行。

数据安全:脚本里别硬编码账号密码。敏感配置放环境变量或加密配置文件,别直接提交到Git。

对于需要团队协作、权限控制,或者希望把Python脚本快速封装成带界面工具的场景,市面上有一些RPA工程化方案可以调研。像蓝印RPA这类支持Python直接接入的平台,能把脚本打包成带界面的可执行文件,还能设置授权验证,适合中小企业内部工具的分发管理。如果涉及自动化流程结合AI能力(如自动识别图片内容、智能填写表单),也可以关注下支持大模型API对接的方案,目前主流模型如文心一言、豆包、DeepSeek、Kimi等都已比较成熟,可以按需接入。


九、RPA工具选型建议

场景 建议方案 考虑因素
个人/小团队 DrissionPage + Python脚本 灵活、无额外成本
中型团队 内部脚本仓库 + 版本控制 需考虑交接和维护
企业级 支持Python接入的RPA平台 权限、审计、跨部门协作

选型没有绝对好坏,匹配团队规模和需求就行。企业级场景如果涉及跨部门协作和审计要求,可以评估蓝印RPA这类支持Python脚本直接接入的方案,既能保留代码灵活性,又能获得企业级的管理和分发能力。


本文基于实际项目经验,整理了DrissionPage 4.x的网页表单自动填报方案:

  • 驱动管理:开箱即用,告别chromedriver版本对不上的噩梦

  • 元素定位:优先text=@属性名=,比XPath维护成本低

  • 等待机制:内置智能等待,复杂场景手动兜底

  • iframe处理:4.x跨iframe直接查找,不需要手动切换

  • 批量数据:pandas读Excel,注意手机号等数字字段转字符串

  • 工程化交付:tkinter做GUI + PyInstaller打包,敏感配置不放代码里

如果实际使用中遇到问题,或者有其他自动化场景想交流,欢迎评论区讨论。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐