自动化 项目实战

Python Selenium 自动化实战:从脚本到exe交付

2026-07-02 · 约6分钟阅读 · Python · Selenium · PyInstaller · ChromeDriver

把一个Selenium脚本变成客户双击就能运行的exe,中间的坑比想象的要多。这篇文章记录了我从零搭建MSD自动化系统到交付exe的全过程,尤其是那些踩出来的坑。

项目背景

MSD采购自动化系统需要处理以下流程:弹窗附件下载(CD0625.py处理)、封门照片自动上传、入库计划解析、免堆期存储状态追踪。整个流程涉及多页面跳转、动态加载、弹窗处理——典型的"页面跳转多、元素加载慢"的复杂自动化场景。

技术选型

  • Python 3.13:主语言
  • Selenium 4.x:浏览器自动化框架
  • ChromeDriver 149.0.7827.53:匹配Chrome v149
  • PyInstaller:打包为独立exe

坑一:ChromeDriver版本对齐

这是第一个也是最容易犯的错:Chrome浏览器的版本必须与ChromeDriver版本精确匹配。

Chrome v149对应ChromeDriver 149.0.7827.53。如果版本不一致,Selenium初始化时就会报错。而且Chrome会自动更新,所以需要关闭自动更新或固定版本。

解决方案:下载对应版本的ChromeDriver,放在项目目录中,通过代码指定路径:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

service = Service('chromedriver.exe')
driver = webdriver.Chrome(service=service)

坑二:DOM StaleElementReferenceException

这是最头疼的问题。当页面发生跳转、刷新或DOM更新时,之前获取的元素引用会"过期"。你明明用xpath定位到了元素,但click()的时候却报StaleElementReferenceException。

这个错误不总出现——取决于页面的渲染速度和网络延迟。有时跑10次才出现1次,极难复现。

解决方案:

  1. 操作前重新获取元素:不要缓存WebElement对象,每次需要操作时重新find_element
  2. 加等待策略:使用WebDriverWait配合EC.element_to_be_clickable,确保元素完全就绪
  3. 加重试机制:捕获StaleElementReferenceException后重新定位重试
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

def safe_click(driver, xpath, max_retries=3):
    for i in range(max_retries):
        try:
            element = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, xpath))
            )
            element.click()
            return
        except StaleElementReferenceException:
            if i == max_retries - 1:
                raise

坑三:PyInstaller打包时缺少hidden-import

脚本在本地Python环境跑得好好的,打包成exe之后就报"ModuleNotFoundError: No module named 'selenium.webdriver'"。

这是因为PyInstaller的分析引擎没有自动检测到Selenium的部分动态导入。需要手动指定hidden-import。

pyinstaller --onefile ^
  --hidden-import=selenium.webdriver.common.by ^
  --hidden-import=selenium.webdriver.support.ui ^
  --hidden-import=selenium.webdriver.support.expected_conditions ^
  --add-binary "chromedriver.exe;." ^
  main.py

关键参数说明:

  • --onefile:打包为单个exe文件
  • --hidden-import:手动声明PyInstaller没检测到的模块
  • --add-binary:把chromedriver.exe也打包进去

坑四:exe运行时chromedriver路径问题

打包后的exe运行时,chromedriver.exe会被解压到临时目录,但路径变了。直接写死路径会找不到。

解决方案:使用sys._MEIPASS获取PyInstaller的临时解压目录:

import sys
import os

def get_driver_path():
    if getattr(sys, 'frozen', False):
        base_dir = sys._MEIPASS
    else:
        base_dir = os.path.dirname(__file__)
    return os.path.join(base_dir, 'chromedriver.exe')

交付checklist

做完以上这些,你的脚本就可以交付了。交付前确认:

  1. exe在全新Windows系统上能直接运行(无需安装Python)
  2. Chrome版本与ChromeDriver匹配
  3. 所有异常都被捕获,有友好的错误提示
  4. 脚本执行完成后自动关闭浏览器(释放资源)
  5. 提供使用说明文档

总结

Selenium自动化从脚本到交付exe,核心挑战不是"写逻辑",而是处理边界情况:版本对齐、元素过期、打包配置。把这三个坑填平了,你就能交付一个稳定、用户友好的自动化工具。