资讯动态

Python Selenium网页自动化实战:元素定位、等待与数据导出

发布时间:2026/10/3 10:14:13 来源:尧图企业网站定制
在浏览器里反复登录、填表、翻页、点按钮这种事做一次两次还行次数多了真的很消磨人。我最早是搞运维的经常要在后台管理系统里核对上百条配置记录后来把活交给了 Python Selenium 网页自动化才算真正解放出来。这篇文章不聊那种“花哨但用不上”的奇技淫巧只把我实际用 Python Selenium 做网页自动化时踩过的坑、验证过的套路以及随手能用的代码整理成一份总结。适合下面这几类朋友快速建立自己的自动化能力被重复性网页操作折磨的运营和数据分析同学、做回归测试的测试工程师以及刚入门 Python 想尝试真实项目的开发者。1. 先搞清楚 Selenium 到底替你做了什么事1.1 它本质上是“让浏览器替你干活”而不是简单的爬虫工具当你在浏览器里手动填一个搜索框再点一下搜索按钮然后等着结果表格出现这一连串动作在背后会产生大量页面渲染和数据请求。Python Selenium 网页自动化的思路就是把这套动作从“人手操作”变成“程序指挥”由代码打开一个真实浏览器按顺序完成定位、点击、输入、读取然后把结果拿回来使用。Selenium 这个生态通常看三件套IDE 是录制回放工具适合快速做原型验证WebDriver 是我们日常写代码的核心负责把 Python 指令翻译成浏览器能执行的原生调用Grid 则用来做分布式并发可以在多台机器、多个浏览器上同时跑一套用例。日常使用中 90% 的工作量都集中在 WebDriver 上也就是用 Python 写 selenium 脚本驱动 Chrome 或者 Edge。它和直接请求网页接口完全不是一个路子。requests 这样的库拿到的其实是服务端返回的原始 HTML如果页面靠 JavaScript 动态渲染里面的关键数据可能根本不在初始 HTML 里。而 Selenium 会启动真实浏览器把 JS 全部执行完你再通过 DOM 去读取渲染后的结果。如果用生活化的例子类比requests 是站在柜台前递一张纸条给柜员看到什么只能靠对方给的单据Selenium 就像你本人坐在柜台前柜员看到的你就是一个普通用户该有的交互都有。我做过最典型的例子是每天上班从内部系统导前一天的报表。手工操作要登录、选日期、点查询、等结果、再导出少说三到五分钟。换成 Selenium 跑以后整个过程稳定控制在十几秒内。这种场景用 requests 很难复刻因为登录链路里有前端加密、动态 token 和复杂交互而 Selenium 模拟的是同一个操作过程浏览器内部自动把这些细节都处理掉了。1.2 确定你的场景吃不吃这套方案不是所有网页操作都值得上 Selenium。如果你要抓大量数据接口又稳定、字段可枚举那直接调用 API 其实更合适速度和资源消耗都优于浏览器方案。我见过不少朋友把 Selenium 当成万能锤任何需求都往上套结果把简单的接口搬运活做得又慢又重。我的判断标准通常有三条同时满足才建议用 Selenium目标页面是动态渲染直接请求接口拿不到最终内容操作链路比较长包含登录、按钮、下拉框、翻页这类交互页面会变但你希望用直观的方式来维护自动化逻辑。维度Selenium 方案requests 接口方案是否执行 JS完整执行拿渲染后 DOM一般不执行拿原始 HTML登录态浏览器真实 Cookie自动维护需要手动处理 Session/Cookie运行速度慢启动浏览器有开销快毫秒级资源占用高一个浏览器数百 MB 内存极低适用场景回归测试、复杂交互、动态表格简单接口、批量分发数据选型的核心就一句话如果你的任务本质上需要“模拟一个真人完整使用网页”用 Selenium 才对路如果只是数据从 A 点到 B 点优先考虑接口方案。2. 环境准备版本和驱动是最大的坑2.1 安装 Python 和 Selenium先把地基打好这里默认你已经装了 Python 3.8 以上的环境。之前很多教程还在提 Python 2现在新项目基本都切到 3.x 了所以安装包的时候一定看清版本不要混着来。装好之后直接在终端里执行python --version pip install selenium如果在内网环境pip 默认源连不上换成国内镜像会快很多pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple装完以后验证一下能不能正常引入包from selenium import webdriver print(webdriver.__version__)只要不报 ModuleNotFoundError环境基本就通了。这里有个高频低级错误容易把包名写错比如装了一个不存在的库代码里却 import selenium排查了半天最后发现是安装源的问题。另外提醒一下如果电脑里同时装了多个 Python 版本要在当前终端确认你使用的是 3.x很多 IDE 里默认解释器选错也会出现“明明装了却 import 不了”的怪现象。2.2 浏览器驱动为什么必须单独装一份 driverSelenium 工作原理里有一环很关键Python 的 API 是一端Chrome 本身不认识这些指令中间需要一位“翻译”就是 ChromeDriver。浏览器、驱动、Selenium 三者版本必须匹配很多新手卡在启动报错十有八九就是驱动版本和浏览器版本对不上。现在的推荐做法是直接用 webdriver-manager 这个库让代码自动下载并匹配合适的驱动省去自己查版本、下载、丢 PATH 的麻烦pip install webdriver-manager初始化代码可以这么写from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager options webdriver.ChromeOptions() driver webdriver.Chrome(serviceService(ChromeDriverManager().install()), optionsoptions)我平时调试时习惯先保留浏览器窗口等功能稳定后再加上无头模式。下面几个 options 参数都是高频用的参数作用--headlessnew无头模式不显示窗口适合后台跑任务--disable-gpu关掉 GPU 渲染解决部分环境下的花屏问题--no-sandbox在容器里运行时有需要本地机器不要乱加--window-size1440,900固定窗口大小很多页面在窄屏下布局会变环境准备阶段最值得做的验证就两个一是能打开一个页面并读取 title二是能对某个输入框做一次 send_keys。这两个能跑通说明最核心的链路已经建立起来了。3. 核心操作拆解定位、等待、交互一次讲透3.1 元素定位用稳定的属性找出目标在网页 HTML 里找元素靠的是 WebDriver 的定位 API。这里有个很重要的工作习惯页面经常改版选择器写得太死后期维护成本会很高。我平时的定位优先级是id name css_selector xpath。为什么 id 优先因为 id 在页面里唯一且绝大多数框架生成的 id 相对稳定。但要注意有些后台管理系统的 id 是动态拼接的每次刷新都会变这时候就要退一步找 name或者找稳定 class 的组合。CSS 选择器通常简短直观XPath 功能最强大但匹配路径写长了很容易碎一旦页面层级调整就会失效。看几个例子from selenium.webdriver.common.by import By # 按 id 定位输入框并输入 driver.find_element(By.ID, username).send_keys(test) # 按 CSS 选择器定位按钮并点击 driver.find_element(By.CSS_SELECTOR, button.login-btn).click() # 按 XPath 定位并取文本 title driver.find_element(By.XPATH, //div[classcard]//h3[contains(text(),报表)]).text特别提醒一句XPath 的 contains 文本匹配要克制使用。如果页面上有多个“报表”字样匹配出来的可能不是你想要的那个节点。更稳妥的写法是结合层级关系和唯一属性比如//div[data-report-id3]//h3宁可多写一层也别让匹配结果不唯一。3.2 等待策略为什么不要用 sleep 硬等刚接触自动化的朋友最容易犯的毛病就是在点击后加 time.sleep(2) 再继续操作。页面快的时候白白等页面慢的时候又等不够一旦网络出现抖动脚本就崩。正确做法是用 WebDriverWait 做显式等待让脚本等到某个条件满足后再继续。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, timeout10) wait.until(EC.presence_of_element_located((By.ID, result))) wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, button.next))).click()这里有几个高频使用的 EC 条件presence_of_element_located 表示元素出现在 DOM 中visibility_of_element_located 表示元素可见element_to_be_clickable 表示可点击。页面刚加载、数据请求还在进行时先用 presence 比较稳妥准备点按钮前用 element_to_be_clickable 更好。隐式等待和显式等待也可以组合起来用。隐式等待是driver.implicitly_wait(5)相当于给每次查找元素都加一个最长等待时间显式等待是针对某个操作的定向等待条件。我的习惯是隐式等待统一设 5 到 10 秒做兜底显式等待处理关键操作路径但不要把两者叠太久否则一个页面慢下来等待时间会成倍增加整个任务被拖得很长。4. 一个可以直接套用的完整案例自动登录、翻页、导出 Excel4.1 案例需求与流程设计假设我现在要做这样一件事登录公司内部报表系统选择“前一天”的查询条件分页浏览前 3 页明细最后把表格导出成 Excel。这个场景我在好几家公司都见过基本是典型的 Python Selenium 网页自动化工作流。先梳理流程打开登录页 → 填用户名密码 → 点登录 → 等待列表加载 → 循环收集当前页表格行 → 判断是否有下一页 → 点击下一页 → 等待新数据 → 全部翻完后保存 Excel。整个流程里最大的风险点是页面加载完成时机所以每一步都要用显式等待兜底。我不拿真实公司系统做演示只写一套具体的脚本套路。代码里的选择器比如 username、password、button.login-btn、tbody tr是常见框架里的 class 命名习惯你换成自己页面的真实元素就能跑起来。4.2 代码实现从入口到收尾import time import pandas as pd 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.service import Service from webdriver_manager.chrome import ChromeDriverManager options webdriver.ChromeOptions() options.add_argument(--window-size1440,900) driver webdriver.Chrome(serviceService(ChromeDriverManager().install()), optionsoptions) rows_lst [] def save_rows(): 把当前页表格里的行数据收集起来 rows driver.find_elements(By.CSS_SELECTOR, tbody tr) for row in rows: cells row.find_elements(By.TAG_NAME, td) if cells: rows_lst.append([cell.text.strip() for cell in cells]) try: driver.get(这里替换成目标登录地址) wait WebDriverWait(driver, 15) # 登录 wait.until(EC.presence_of_element_located((By.ID, username))).send_keys(test_user) driver.find_element(By.ID, password).send_keys(你的密码) driver.find_element(By.CSS_SELECTOR, button.login-btn).click() # 等待列表数据渲染出来 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, tbody tr))) # 翻页收集 for page in range(3): save_rows() next_btn driver.find_element(By.CSS_SELECTOR, button.next) if disabled in next_btn.get_attribute(class): break driver.execute_script(arguments[0].click();, next_btn) time.sleep(1) wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, tbody tr))) if rows_lst: df pd.DataFrame(rows_lst) df.to_excel(data.xlsx, indexFalse, headerFalse) print(共收集到, len(rows_lst), 行数据) finally: driver.quit()这段代码里有几个细节值得解释。首先是 webdriver_manager 自动匹配驱动版本省去手动维护驱动。其次是固定窗口大小避免窄屏下表格列被折叠或者按钮被隐藏。第三翻页用 execute_script 来点击而不是直接.click()是因为有些框架的按钮上面会盖一层遮罩或者提示浮层原生点击会被挡住JS 点击能绕开这层干扰。最后用 pandas 存 Excel这里需要提前装 openpyxl 这个引擎pip install openpyxl不然 to_excel 会直接报错。保存路径默认和脚本同目录如果你想放到指定位置改成绝对路径就行。4.3 让它更贴近生产环境的三件事第一把登录逻辑和翻页逻辑都封装成函数入口函数只负责编排流程。这样页面一旦变动只需要改选择器所在的函数不用在主线代码里到处找。第二给异常分支加上截图。常见做法是在 except 里调用driver.save_screenshot(error.png)再配合 error log隔天排查问题会轻松很多。第三如果需要定时跑任务可以用系统自带的任务计划程序或者 crontab 调用 Python 脚本。注意一点跑完任务之后一定确认driver.quit()被调用。我在公司服务器上遇到过脚本异常退出、浏览器进程没关干净几天后内存被占满的情况排查起来非常痛苦。5. 常见问题与排查技巧5.1 元素不存在怎么办第一步不是改代码遇到 NoSuchElementException 时很多人的第一反应是换个选择器再试结果来回试半天。我踩过几次坑之后现在养成了一个习惯先把当前页面现场打印出来再看问题。print(driver.page_source[:2000]) driver.save_screenshot(debug.png)这一步能解决大部分“看不见摸不着”的问题。常见的元素找不到原因我整理过一张速查表现象大概率原因处理方式元素总是超时不存在页面还没加载完换成显式等待 presence选择器能搜到但脚本报错元素在 iframe 或 frame 里先driver.switch_to.frame(标识)元素在源码里但点击无效被遮罩层盖住用 JS click 或者先关闭弹层页面上有多个同名元素选择器命中不唯一改用 find_elements 按下标或文本过滤点击后页面闪了一下又回原样外层弹窗拦截了交互先处理弹窗再操作目标元素我遇到过最隐蔽的一种情况是表格第一屏已经渲染出来了但后面的行还没真正加载到 DOM 里这时急着统计数据就会少。排查思路还是要回到调试输出先打印 page_source确认元素在不在再考虑是不是等待不够或 iframe 的问题。5.2 页面滚动和左右滑动别让数据“躲在屏幕外面”网页自动化里滚动很常见尤其是页面在底部做懒加载或者右侧出现横向滚动条看不到后半部分表格。Selenium 处理滚动主要靠 execute_script 配合 scroll 系列方法# 纵向滑到底触发懒加载 driver.execute_script(window.scrollTo(0, document.body.scrollHeight)) # 让某个元素滚到视野中间 driver.execute_script(arguments[0].scrollIntoView({block:center}), elem) # 横向滚动容器比如运营后台常见的横向宽表 container driver.find_element(By.CSS_SELECTOR, .table-scroll) driver.execute_script(arguments[0].scrollLeft arguments[0].scrollWidth, container)这三种滚动方式分别对应不同场景列表页无限加载、页面上元素需要被点击、横向宽表被容器包裹。横向滑动看起来偏门但处理过报表后台的人都知道表格列数一多右侧一定有个横向滚动容器直接用 scrollIntoView 不一定有效因为滚动发生在内层容器而不是窗口上所以必须先找到那个容器再操作 scrollLeft。5.3 操作太频繁触发了验证码正确思路是收敛行为不少刚接触 Python Selenium 的人写完脚本就一小时跑一次全量很快会发现页面上开始出现滑块、验证码或者直接提示访问受限。这其实是在告诉你目前的访问频率或行为模式不自然并不是脚本在技术上“失败”了。我的处理思路是先在合规范围内收敛自动化行为。能少跑就少跑能隔开就隔开无头模式和高频操作是风控最敏感的触发点非必要不开启关键步骤可以设计人工介入比如检测到验证码后脚本就自动停住通知同事人工确认一次。不要尝试去绕过验证码或签名机制那些做法不仅违反平台规则而且状态很容易失效维护成本极高。还应该多说一句动手之前先确认目标系统的使用条款确定自动化是被允许的。内部系统和自己的测试站点问题不大对外部平台要格外谨慎这个意识要贯穿项目始终。6. 从“能跑”变成“稳定跑”的几个经验细节6.1 调试阶段先别开无头模式无头模式听起来很省资源但它让你在运行时什么都看不见。我第一次在公司服务器上跑无头登录失败后脚本一直在循环点击一个不存在的按钮最后靠日志才定位到问题。正确做法是先在本地窗口模式跑通再上无头。而且上无头以后也要把窗口大小传进 options否则页面可能被当成窄屏甚至移动端布局来渲染选择器全变样脚本自然就挂了。6.2 登录态不是只能每次重新输入如果跑自动化时每次都真实登录既慢又容易触发限制。对于允许保持会话的站点可以把 Cookie 序列化保存下来下次启动后直接带进去。import json # 第一次运行时登录并保存 cookies driver.get_cookies() with open(cookies.json, w, encodingutf-8) as f: json.dump(cookies, f, ensure_asciiFalse) # 下次启动时恢复 driver.get(https://目标域名首页) # 先访问一次让浏览器有对应域名上下文 for cookie in json.load(open(cookies.json, encodingutf-8)): driver.add_cookie(cookie) driver.refresh()这个技巧很实用但要注意 cookie 的 domain 字段必须和当前访问的域名匹配如果域名对不上浏览器会直接丢弃。另外如果系统用短时效 token 做会话比如一两个小时就失效那就不能只靠恢复 cookie 解决问题还是得走真实登录流程。6.3 异常处理和日志能让你少熬很多夜写过自动化的人都有这种体会白天跑得好好的脚本夜深人静定时执行时总能莫名其妙挂掉。为了能快速定位问题我会在每个关键步骤留下日志并且在异常分支把当前页面信息保存下来。import logging import traceback logging.basicConfig(levellogging.INFO, format%(asctime)s %(message)s) try: driver.get(url) logging.info(已打开页面: %s, driver.title) except Exception: logging.error(traceback.format_exc()) driver.save_screenshot(error.png) raise finally: driver.quit()长时间运行的任务日志习惯的价值会越来越明显。脚本挂在哪个环节、页面当时长什么样下一个接手的同事只要翻一下日志和截图就能看出大概问题不需要从头到尾猜。这套思路不仅适用于 Python Selenium 网页自动化放到任何定时任务、批处理脚本里都一样受用。如果你手里也有一堆这种重复性网页操作可以从文中的最小案例开始先搭一个能跑的版本再去优化细节。等跑通以后你会发现最难的不是写代码而是把业务逻辑彻底理清楚什么时候登录、什么时候翻页、什么时候该停。把流程图理顺了Python Selenium 这套工具就是个很趁手的帮手。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑