news 2026/10/16 7:36:03

Python+Selenium实战:Excel数据自动录入网页业务系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python+Selenium实战:Excel数据自动录入网页业务系统

1. 项目拆解:自动录入到底在解决什么问题

先交代一下背景。这个项目来自某企业内部的一个典型业务痛点:运营人员每天要处理大量Excel表格数据,比如产品信息、订单记录、备案资料,需要一条条手工复制粘贴到网页业务管理系统里做登记。数据量一上来,问题就非常明显——一个人一天录入两三百条已经是极限,而且字段一多就容易串行、漏填,等发现错误再回头排查,基本是一场灾难。

我当时接到的需求就是:把一张几千行的Excel数据表自动录入到网页端的内部业务管理系统里,要求录入结果可追溯、可查重、可断点续跑。技术栈很明确,Python + Selenium + MySQL。这个组合在公司内部技术团队里非常通用,Python负责逻辑编排,Selenium负责模拟真实用户操作浏览器,MySQL负责录入过程和结果的持久化。

要理解这个项目的价值,首先要理解一点:很多网页系统并不提供批量导入功能,甚至连API都没有开放。你拿到的只是一个人工操作的网页表单。在这种情况下,Selenium这类浏览器自动化工具几乎是唯一性价比最高的方案。有人可能会问,为什么不用requests直接模拟接口提交?原因很现实——多数这类系统要么有动态token校验,要么有加密参数,要么会校验浏览器指纹,requests去模拟接口,调试成本极高,而且页面一升级可能就全部失效。相比之下,Selenium是实打实操控浏览器,走的是“正门”,系统再怎么改登录逻辑、怎么加前端校验,只要表单还能人工填写,自动化就能跑。

整个项目并不复杂,但涉及的知识点覆盖了Python基础、Selenium页面交互、MySQL建表与回写、数据清洗、异常恢复等多个方面,非常适合作为自动化脚本开发的练手项目,也能直接迁移到其他相似场景,比如批量报名、数据登记、巡检上报等。

1.1 业务场景还原与痛点梳理

模拟项目X的运营团队每天会收到一份Excel清单,里面包含需要录入的字段:业务编号、名称、分类、数量、日期、备注等。员工的操作路径是:打开浏览器,登录系统,点击“新增记录”,逐项复制粘贴,选分类、填日期、写备注,点击保存,然后处理下一条。全过程每条大约需要一到两分钟,难点不在于单条速度,而在于长时间重复操作带来的疲劳和错误率。

两个最头疼的问题:一是Excel里数据的格式和网页表单要求不完全一致,比如Excel里的日期是“20240515”,网页端要求“2024-05-15”,分类字段Excel里是中文名称,网页端是下拉框选项;二是录入过程中没有人会逐条确认成功与失败,有些记录保存失败也不容易被发现,等月底统计时才发现数据对不上。

所以我搭建这个自动录入工具时,核心思路不只是“把数据塞进表单里”,而是把它做成一个完整的小系统:数据读取、字段清洗转换、模拟录入、结果回写、日志记录、断点续跑。MySQL在这里不是可有可无的附属品,它是整个流程的“记分牌+账本”,每次录入的结果都落库,后续查账和统计就有据可依。

1.2 技术选型:为什么要用Selenium而不是其他方案

先回答一个很多人会问的问题:既然要自动化,为什么不直接用Python的requests库去POST表单数据?答案是这种系统的前端通常有一些机制刻意防止非浏览器端提交,比如页面里有随机的隐藏字段、authentication token、时间戳校验等。你通过requests手工构造请求,第一次可能成功了,第二次就因为某个参数没有正确生成而失败。排错成本很高,而且代码非常脆弱,页面一改就要跟着改。

打个比方,这就好比你想进一栋楼,requests是研究门禁卡芯片然后去复制一张卡,Selenium是直接请楼里的人帮你把门拉开。显然后者更省力,也更不容易被门禁系统识别为异常。

MySQL在这个项目中承担的任务同样清晰:存储Excel导入的原始数据、记录每一条的录入状态、保存每次提交后的系统返回信息。没有数据库,程序如果跑到一半崩溃了,你根本不知道已经录了哪些,只能从头再来或者靠日志一行行翻,效率极低。有了状态字段,程序重启后可以直接恢复到上次断点,只处理status=0的记录。

1.3 系统整体设计与模块划分

整个自动录入工具可以分为四个模块:数据准备层、页面操作层、数据持久层、调度执行层。

  • 数据准备层:读取Excel,清理空行、格式化字段、生成业务主键
  • 页面操作层:封装浏览器启动、元素定位、输入框填充、下拉选择、日期处理、提交按钮点击
  • 数据持久层:通过pymysql连接MySQL,提供写入、更新、查询状态等基础函数
  • 调度执行层:控制整体循环流程,调用上述模块,处理异常重试与日志输出

这样分层的目的很明确,就是为了“解耦”。页面操作层如果换了一个系统,只需要重写定位逻辑,数据准备层、数据持久层几乎不用动;反过来,如果Excel格式变了,也只需要改数据准备层的解析逻辑,不影响页面操作。项目不大,但这种分层思路在日后扩展时非常有用。

2. 环境准备与工具选型

工欲善其事,必先利其器。这个项目对运行环境没有很高的要求,一台普通的办公电脑就能跑起来。但有几个细节值得提前确认,不然在折腾依赖的环节会浪费不少时间。

2.1 Python与依赖包的安装

Python版本建议使用3.8以上,推荐3.10或者3.11稳定版。项目依赖的核心库有四个:selenium负责浏览器自动化,pymysql负责MySQL交互,pandas和openpyxl配合完成Excel读取。安装命令一行搞定:

pip install selenium pymysql pandas openpyxl

装完之后可以验证一下版本,避免因为版本不匹配导致后面出现莫名其妙的问题:

python -c "import selenium; print(selenium.__version__)"

这里要特别提醒一点,pandas读取Excel时底层依赖openpyxl,所以两个库要一起装,不要只装pandas。另外,如果你的电脑同时装了多个Python版本,一定要确认pip安装到了你要用的那个解释器下面,建议直接用虚拟环境,避免环境污染。

2.2 浏览器驱动的版本匹配问题

Selenium本身不是浏览器,它是通过驱动(如chromedriver)来操控浏览器的。很多人第一次跑Selenium程序报错“session not created”或者“chromedriver executable needs to be in PATH”,就是因为浏览器和驱动版本对不上。

常用的组合是Chrome浏览器 + chromedriver,当然也可以选Edge和msedgedriver。Chrome的版本号可以在浏览器地址栏输入chrome://version查看,然后去知名镜像站下载对应版本的chromedriver。版本选择原则是“主版本号必须一致,小版本尽量接近”,比如Chrome 120就用chromedriver 120.x.x.x。

下载后可以把驱动放到和脚本同一个目录,然后在代码里指定路径:

from selenium import webdriver driver = webdriver.Chrome(executable_path=r"D:\chromedriver\chromedriver.exe")

不过新版Selenium(4.6以上)支持了自动管理驱动,只需要把Chrome浏览器升级到较新版本,代码里直接写webdriver.Chrome()即可,Selenium Manager会自动下载匹配的驱动。对于公司内网环境不方便访问外网的场景,手动指定路径反而更可靠。

2.3 MySQL库表设计:状态字段决定断点续跑

MySQL这边不需要复杂的表结构,但几个关键字段一定要考虑到位。我设计的是一张业务数据表和一张操作日志表。

业务数据表保存从Excel里读出来的原始数据和录入状态:

CREATE TABLE `biz_record` ( `id` int NOT NULL AUTO_INCREMENT, `biz_no` varchar(64) NOT NULL COMMENT '业务编号', `biz_name` varchar(255) NOT NULL COMMENT '名称', `category` varchar(64) DEFAULT NULL COMMENT '分类', `quantity` int DEFAULT NULL COMMENT '数量', `entry_date` date DEFAULT NULL COMMENT '日期', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `record_status` tinyint DEFAULT '0' COMMENT '0待录入 1成功 2失败 3跳过', `submit_message` varchar(500) DEFAULT NULL COMMENT '系统返回结果', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里有两个关键点。一是业务编号建议加唯一索引,防止同一份数据被重复导入;二是record_status字段是整个工具的“心脏”,程序每次启动前只拉取status=0的记录,跑完一条立刻更新状态,这样无论程序因为什么原因中断,下次启动都能从上次中断的地方继续,不需要人工去记跑到哪一条了。

日志表则用于记录每次操作的关键信息:

CREATE TABLE `operate_log` ( `id` int NOT NULL AUTO_INCREMENT, `biz_id` int NOT NULL, `operate_type` varchar(32) DEFAULT 'submit', `result` varchar(1000) DEFAULT NULL, `log_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

日志表的作用体现在排错阶段。程序跑完一批数据后,不要只看成功数,还要能从日志表里回溯某一条记录失败时的上下文信息。

3. Selenium自动化核心代码实现

环境准备好、表结构建好,就进入最核心的环节——写自动化代码。这一部分是整个项目的重头戏,我拆成四块来讲:数据读取与清洗、浏览器初始化、页面元素操作封装、主流程控制。

3.1 Excel数据读取:格式问题都在这里踩坑

Excel数据读取本身不复杂,但格式问题特别多。我的建议是第一步就把数据清洗干净,而不是等到填表时再处理。举几个常见的坑:

第一个坑是“产品编号”这类字段很容易变成科学计数法或者小数。Excel里单元格如果是常规格式,长数字会被显示成类似8.19002E+17这样的形式。pandas读取时如果没有指定类型,拿到手的就是数字。解决办法是读取时强制指定dtype为字符串:

import pandas as pd df = pd.read_excel("data_2025.xlsx", dtype={"biz_no": str, "phone": str})

第二个坑是空行和Null值。Excel表格里看起来空白的行,pandas会读成NaN,如果不过滤,后面在页面上输入“None”或者空字符串都会出问题。建议统一做一次dropna:

df = df.dropna(subset=["biz_no", "biz_name"]) df = df.fillna("")

第三个坑是日期格式。Excel里可能是“20240515”“2024/05/15”“2024-05-15”等各种写法,而网页端表单大概率接受的是标准格式。清洗阶段就统一成datetime对象,再按需格式化:

from datetime import datetime def parse_date(value): if not value: return "" value = str(value).strip().replace("/", "-") try: return datetime.strptime(value, "%Y%m%d").strftime("%Y-%m-%d") except ValueError: try: return datetime.strptime(value, "%Y-%m-%d").strftime("%Y-%m-%d") except ValueError: return ""

清洗完成后,把数据写入MySQL,作为本次任务的数据池:

import pymysql conn = pymysql.connect(host="localhost", user="root", password="yourpassword", database="auto_entry", charset="utf8mb4") cursor = conn.cursor() for row in df.to_dict("records"): sql = "INSERT INTO biz_record (biz_no, biz_name, category, quantity, entry_date, remark) VALUES (%s,%s,%s,%s,%s,%s)" cursor.execute(sql, (row["biz_no"], row["biz_name"], row["category"], row["quantity"], row["entry_date"], row["remark"])) conn.commit()

注意:如果准备断点续跑,这里要先按业务编号查重,存在相同编号就直接跳过,避免重复插入:

check_sql = "SELECT COUNT(*) FROM biz_record WHERE biz_no=%s" cursor.execute(check_sql, (row["biz_no"],)) if cursor.fetchone()[0] > 0: continue

3.2 浏览器初始化配置

Selenium操作浏览器的第一步是创建driver对象。除了最基本的启动,还需要做一些配置,比如最大化窗口、屏蔽不必要的日志、设置页面加载超时等。我常用的初始化模板如下:

from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() options.add_argument("--start-maximized") options.add_experimental_option("excludeSwitches", ["enable-automation"]) # 去掉自动化提示 options.add_experimental_option("useAutomationExtension", False) driver = webdriver.Chrome(options=options) driver.set_page_load_timeout(30) driver.implicitly_wait(10)

这里有几个细节值得解释一下。第一,excludeSwitches的enable-automation参数可以去掉Chrome页面上的“Chrome正受到自动软件的控制”提示条,虽然不影响功能,但视觉上更干净,在某些页面中避免触发前端检测。第二,implicitly_wait设置的是全局隐式等待,意思是Selenium在查找元素时,如果没找到,会轮询等待最多10秒再报错。这个时间不用太长,3到10秒之间即可,太长了会拖慢整体速度。

另外,如果你的系统里需要登录才能进录入页面,我不建议把账号密码写死在代码里。更稳妥的做法是单独写一个登录函数,把账号密码从配置文件中读取,登录成功后等待一段时间再继续操作。登录逻辑如果涉及验证码,那就得配合人工处理,这部分后面单独讲。

3.3 元素定位与输入封装:这套函数可以复用

Selenium自动化的核心难点不在启动浏览器,而在于稳定地找到网页上的元素并把数据填进去。不同网页的元素属性千差万别,有的元素ID是动态生成的,有的控件不是简单的input框,而是自定义组件。我建议不要每个字段都单独写一段定位代码,而是封装一个通用的操作类。

先定义定位器,优先使用稳定的属性:

from selenium.webdriver.common.by import By # 示例:新记录表单页面 LOCATORS = { "biz_no": (By.ID, "bizNo"), "biz_name": (By.ID, "bizName"), "category_select": (By.ID, "category"), "quantity": (By.ID, "quantity"), "entry_date": (By.ID, "entryDate"), "remark": (By.ID, "remark"), "submit_btn": (By.CSS_SELECTOR, "button.submit-btn"), "success_msg": (By.CSS_SELECTOR, ".alert-success"), }

然后封装安全的输入方法,这里用显式等待加异常捕获:

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def wait_and_send_keys(driver, locator, text): elem = WebDriverWait(driver, 10).until(EC.visibility_of_element_located(locator)) elem.clear() elem.send_keys(text)

为什么不用time.sleep固定等几秒?因为页面加载速度受网络影响波动很大,固定sleep要么等多了浪费时间,要么等少了元素还没渲染出来。WebDriverWait是等到元素可见了才继续,效率高且稳定。这是Selenium新手最容易忽略的一个点。

3.4 下拉框选择:Select对象的正确用法

网页表单里最常见的控件除了输入框就是下拉框。如果是原生HTML的select标签,Selenium提供了Select类来处理:

from selenium.webdriver.support.ui import Select def select_by_text(driver, locator, text): elem = WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator)) Select(elem).select_by_visible_text(text)

这里要说一个很常见的坑:如果text是动态变化的,比如分类选项是“电子产品-手机”这种带层级的前缀,直接按可见文本选可能因为多了一个空格而失败。建议提前把Excel里的分类值和网页下拉选项对应关系维护成一个字典,读取数据时先做映射,映射不到的数据标记为跳过,并写入日志,而不是在页面上反复尝试导致重复提交。

另一个坑是部分网页下拉框是经过JS改造的自定义组件,并不是原生的select标签。点击后会弹出一层div列表,这时候需要直接去点击对应的li或者div。这种情况没有统一解法,核心思路就是F12打开开发者工具,观察展开后的DOM结构,然后用XPath或CSS选择器定位选项元素。

3.5 日期控件的处理技巧

日期字段通常有两种形式:可直接输入的文本框,和只能点击选择的日历组件。

如果是可输入的文本框,最简单的办法是先清空再填入格式化好的日期字符串,这也是我在清洗数据阶段把日期统一成“YYYY-MM-DD”格式的原因。但要注意部分日期控件有readonly属性,直接send_keys不生效。解决办法是先通过JS移除readonly属性,再输入:

def set_date(driver, locator, date_str): elem = WebDriverWait(driver, 10).until(EC.visibility_of_element_located(locator)) driver.execute_script("arguments[0].removeAttribute('readonly');", elem) elem.clear() elem.send_keys(date_str)

如果是日历点击型组件,则要先点击日期框弹出日历面板,再找到对应日期元素点击。日期数字通常在一个表格里,需要手动构造XPath:

# 日历面板中,按文本内容定位日单元格 day_xpath = f"//div[contains(@class,'calendar-panel')]//td[contains(@class,'day') and text()='{day}']" driver.find_element(By.XPATH, day_xpath).click()

处理日期控件不要硬刚,灵活使用JS执行和XPath定位会省很多事。

3.6 主流程控制:循环、状态回写、断点续跑

单个表单的填充逻辑封装好之后,主流程就是一个循环控制的活。整体逻辑如下:

def process_pending_records(driver): pending = fetch_pending_records() for rec in pending: try: driver.get("http://internal-system.example.com/entry") wait_and_send_keys(driver, LOCATORS["biz_no"], rec["biz_no"]) wait_and_send_keys(driver, LOCATORS["biz_name"], rec["biz_name"]) select_by_text(driver, LOCATORS["category_select"], rec["category"]) wait_and_send_keys(driver, LOCATORS["quantity"], str(rec["quantity"])) set_date(driver, LOCATORS["entry_date"], rec["entry_date"]) wait_and_send_keys(driver, LOCATORS["remark"], rec["remark"]) driver.find_element(*LOCATORS["submit_btn"]).click() success = check_success(driver) if success: update_record_status(rec["id"], status=1, message="success") else: update_record_status(rec["id"], status=2, message=extract_error_msg(driver)) except Exception as e: update_record_status(rec["id"], status=2, message=str(e)) continue

其中fetch_pending_records只查record_status=0的数据,update_record_status每跑完一条就更新状态。这样整个流程具备断点续跑能力,就算跑到一半登录态过期、程序崩了,重启后直接拉取剩余待处理记录继续跑就行。

check_success这个函数建议用显式等待提交后页面上的成功提示元素,而不是盲目sleep两秒再截图。常见实现:

def check_success(driver): try: WebDriverWait(driver, 10).until( EC.visibility_of_element_located(LOCATORS["success_msg"]) ) return True except Exception: return False

这里再补充一个经验:对于数据量较大的批量录入,不要同时把所有数据灌进内存一次性循环。更好的方式是分批次从数据库取,比如每次取50条,跑完再取下一批,避免内存持续膨胀,也方便中途暂停。

4. 实际踩坑与排查技巧实录

自动化脚本看起来简单,实际跑起来幺蛾子特别多。我整理了一些高频问题和排查思路,都是实战中真实踩过的坑。

4.1 元素找不到:frame与iframe是罪魁祸首

很多后台管理系统的录入表单是嵌套在一个iframe里的。Selenium默认只能访问主文档中的元素,如果你直接通过ID查找iframe内部的元素,十有八九会报NoSuchElementException。遇到这种情况,先切到iframe再操作:

frame = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TAG_NAME, "iframe"))) driver.switch_to.frame(frame) # 操作iframe内部元素... # 操作完成后切回主文档 driver.switch_to.default_content()

判断页面有没有iframe,最简单的方式是浏览器F12打开控制台,输入document.querySelectorAll('iframe'),如果有返回值,那多半就是这个原因。

4.2 动态ID与属性变化:优先用XPath相对定位

有的表单每次打开页面时元素的ID会出现随机后缀,比如input_abc123、input_def456,这种就不能用固定ID定位。排查时优先看元素的name属性、class属性是否稳定,再看它的父级和兄弟节点是否有稳定的标识。

XPath相对定位在这种情况下会很顺手,比如:

# 找到label文本为“业务编号”的输入框 xpath = "//label[contains(text(),'业务编号')]/following-sibling::input" driver.find_element(By.XPATH, xpath)

这种写法虽然啰嗦,但抗页面改版能力反而强。Selenium定位元素就像找人,如果你知道对方具体房间号(固定ID),就敲门;如果房间号总变,但你知道门口挂着“姓名:张三”的门牌,那就用XPath按门牌去找。

4.3 点击按钮被拦截:滚动与JS点击

有时候元素明明定位到了,但在调用click()时报错“element click intercepted”,这是因为按钮不在可视区域内,或者被某个悬浮层盖住了。解决办法是先滚动到元素位置,再点击:

element = driver.find_element(*LOCATORS["submit_btn"]) driver.execute_script("arguments[0].scrollIntoView();", element) element.click()

如果滚动后还是被遮罩拦截,直接用JS强制点击:

driver.execute_script("arguments[0].click();", element)

不过JS点击虽然能生效,但不会触发部分框架绑定的事件,某些页面上按钮的点击事件是绑定在鼠标按下、鼠标抬起上的,JS click可能无法触发完整逻辑。所以如果JS点击后页面没有反应,还是要回到“手动先操作一遍,观察控制台有无报错”的老路上去排查。

4.4 验证码与人工介入机制

这个项目里最棘手的问题就是验证码。有些系统登录时需要验证码,有些系统提交频率过高时也会弹出验证码。完全绕过验证码不在这个项目的讨论范围内,我的经验是给程序设计一个人工介入的“暂停-继续”机制。

具体做法是在代码里增加一个检测点,如果检测到验证码图片出现,就停止自动化流程,用程序弹窗或者命令行提示操作人员手动输入验证码:

def check_captcha(driver): try: captcha_img = driver.find_element(By.XPATH, "//img[contains(@class,'captcha')]") if captcha_img.is_displayed(): code = input("请手动查看验证码并输入:") driver.find_element(By.ID, "captchaInput").send_keys(code) return True except Exception: return False

这里要注意的是,验证码输入框填完验证码后,不要立刻接着点提交,最好等待一两秒让人工确认页面上是否显示校验通过,避免因为验证码输入错误导致整条记录被判定失败。

4.5 数据库连接超时与重连机制

pymysql默认的connection有个问题:如果程序长时间空闲或者跑了很久,MySQL服务端会主动断开空闲连接。下次执行execute时就会报“MySQL server has gone away”。解决办法是在主循环里增加重连机制:

def execute_with_reconnect(conn, cursor, sql, params): try: cursor.execute(sql, params) except pymysql.err.OperationalError: conn = pymysql.connect(host="localhost", user="root", password="yourpassword", database="auto_entry", charset="utf8mb4") cursor = conn.cursor() cursor.execute(sql, params)

或者更简单,每次更新状态时单独建立一次连接,执行完关闭。这种方式虽然浪费一点连接开销,但对于录入量几百到几千条的场景影响可以忽略,换来的是稳定性。

4.6 重复提交防护与幂等设计

最后也是最重要的一点:自动化脚本跑批量录入时,最怕的就是重复提交。原本想提交100条,因为网络抖动,某一条提交成功了但程序没收到成功提示,于是重试时又提交了一次,数据库里就多了一条重复数据。

解决思路有两种。第一种是前端判断,提交前先按业务编号在系统里查询是否已存在,存在就跳过;第二种是后端兜底,如果系统支持提交唯一标识字段,就把业务编号作为外壳字段提交上去,数据库层面做唯一约束。第一种方案见效快,但多了一次查询操作,影响整体速度;第二种方案更稳妥,但不是所有系统都支持。

我在这类项目里更推荐第一种方案配合状态机控制:每条记录只有在status=0时才允许提交,提交中先在本地把状态改成4(处理中),等系统返回明确结果后再更新为成功或失败。这样即使程序崩溃重启,也不会对一条记录发起重复提交。

4.7 高频排查问题速查表

问题现象可能原因排查方法
NoSuchElementException元素不在当前frame里、ID动态变化、页面未加载完成先检查iframe,打印当前页面HTML片段,使用WebDriverWait
ElementClickInterceptedException遮罩层覆盖、元素不在可视区scrollIntoView后点击,必要时JS点击
SessionNotCreatedExceptionchromedriver版本与浏览器版本不一致检查版本号,下载对应驱动
MySQL server has gone away空闲连接被服务器断开增加重连机制,或每次执行独立连接
成功提示出现了但数据没入库前端校验拦截、JS事件未触发手动操作一遍对比前端控制台日志
下拉框选项无法点击自定义下拉组件、需要展开列表F12查看DOM,改为点击对应列表项
程序跑一半登录态过期Session或Cookie失效增加登录态检测,过期后重新登录并继续

5. 项目经验总结与扩展方向

项目做完之后,几个同事也在他们自己的业务场景里复用了这套代码,换汤不换药,改改定位器和字段映射就跑起来了。我在这个过程中最大的体会是,自动化脚本写起来不难,难的是一开始就把“状态管理”和“异常恢复”想清楚。

很多初学者写Selenium脚本时,脑子里只有一个简单的“循环填表”模型:数据读完一遍,填完就结束。但真实业务环境下,脚本可能要跑几个小时,中途会遇到各种意外——网络卡顿、系统弹窗、元素偶发定位失败、数据库连接断开,甚至电脑休眠。如果没有状态字段和断点续跑机制,任何一次中途失败都意味着前面几小时的成果要全部重来。所以在我给这类项目搭框架时,第一步永远是设计好数据库表,把“待录入/处理中/成功/失败”这个状态机立起来,后续所有逻辑都围绕状态机转。

第二个体会是要给程序留“人控开关”。我在代码里设置了几个关键节点的人工确认环节,比如登录完成后等待用户按回车继续,遇到验证码时提示用户输入,批量提交过程中每处理50条数据暂停一次并输出进度。这些看似“不智能”的设计,在实机运行中反而大幅降低了无人值守的风险。自动化和人工不是二选一,而是要用机器做重复劳动,用人做判断和兜底。

这个项目后续的扩展空间也很大。比如可以加一个简单的Web界面,把MySQL里的记录状态可视化展示;也可以接入定时任务,让脚本每天自动从指定目录拉取新增Excel并执行录入,形成一条全自动的数据管道。再进一步,还可以加入邮件通知,批量任务结束后把成功数和失败明细发给相关人,进一步完善自动化闭环。

如果你正准备做一个类似的网页自动录入项目,我的建议是:先花半天时间用F12手动把目标页面的DOM结构摸透,不要急着写代码;再把数据清洗和状态管理设计好;最后才动手写Selenium操作逻辑。按照这个顺序来,项目进度反而会快很多,踩的坑也会少很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:35:55

Win10家庭版WiFi图标消失?启用组策略快速修复

1. 从一次真实的断网事故说起那天下午正开着视频会议,屏幕右下角的WiFi图标突然消失了。不是断连,是图标整个不见了——任务栏那个熟悉的扇形信号标志凭空蒸发,点开网络设置也找不到任何无线网络列表。第一反应是重启,没用。第二反…

作者头像 李华
网站建设 2026/10/16 7:35:51

双目立体匹配中的动态规划:从扫描线DP到SGM

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 7:35:40

LSTM与XGBoost特征融合:多变量时序预测完整实战

1. 为什么我最终选择了LSTM-XGBoost组合模型做多变量时序预测先交代一下背景。之前我接过几个时序预测类的需求,有做风功率预测的,有做交通流量预测的,还有做设备剩余寿命预估的。它们有一个共同特点:数据维度多、噪声大、趋势和周…

作者头像 李华
网站建设 2026/10/16 7:35:30

STM32F4驱动EC20 TCP透传模式实战:AT指令、DMA接收与断线重连

简介:这份资源面向嵌入式开发初学者与物联网通信方向的工程师,聚焦EC20无线通信模块在STM32F4系列微控制器上的TCP透传模式实现,帮助读者打通从AT命令配置到SOCKET数据收发的完整链路。压缩包共174个文件,以C源码与头文件为主体&a…

作者头像 李华
网站建设 2026/10/16 7:35:13

RDMA 从入门到实战:零拷贝、内核旁路与 CPU 卸载的完整指南

1. RDMA 到底是什么,为什么值得花时间搞懂第一次接触 RDMA 是在一个分布式存储项目里,当时两台机器之间做数据同步,万兆网卡跑满了 CPU 还是居高不下,延迟怎么调都下不来。后来一位做高性能计算的朋友跟我说了一句:“你…

作者头像 李华
网站建设 2026/10/16 7:35:11

DeepSeek Harness实战指南:AI工程化落地的轻量级部署框架

1. 项目概述:这不是一个“安装包”,而是一套AI工程化落地的完整方法论你点开这个标题,第一反应可能是:“又一个吹牛的AI教程?”——我完全理解。过去两年里,我亲手拆解过不下87个标榜“全网最细”“比付费强…

作者头像 李华