Python+Selenium 网页自动化:从环境搭建到稳定实战
发布时间:2026/10/2 11:35:17来源:尧图网络
1. 先搞清楚Selenium到底能解决什么问题做网页自动化Python 配上 Selenium 是我个人觉得最稳妥的组合之一。简单说Selenium 是一套模拟真实浏览器操作的框架它能驱动 Chrome、Firefox、Edge 这些浏览器像真人一样打开页面、点击按钮、填写表单、滚动屏幕、读取数据。对于重复性的网页操作比如每天固定时间到某个系统里拉取报表、批量注册测试账号、巡检线上页面是否正常用 Selenium 能省下大量人工时间。这套方案适合谁如果你正在做 Python 爬虫、自动化测试、或者想把手头重复的网页操作脚本化这篇文章就是围绕这套链路展开的。我会按实际动手的顺序来写先讲清楚选型思路再讲环境怎么搭、核心操作怎么写然后重点说等待机制这个最容易翻车的地方最后把常见的坑列成清单方便你直接对照排查。读完之后你至少能独立写出一套可复用的 Selenium 自动化脚本而不是停留在跑通 Demo的阶段。需要先说明一点Selenium 是公开的正规自动化测试工具用来做自己负责的网站测试、数据采集、内部系统自动化都完全没问题。但如果你打算拿它去绕过某个网站的访问限制请一定先确认对方的服务条款合规使用永远是前提。2. 选型思路为什么是 Selenium 而不是别的方案2.1 对比 Playwright、Pyppeteer 该怎么选很多新手一上来就问Selenium 是不是过时了。其实这个判断要看场景。Playwright 和 Pyppeteer 在性能、API 设计上确实更现代但 Selenium 有一个别人替代不了的优势生态成熟。WebDriver 协议是老牌标准绝大多数的自动化测试平台、云测服务、CI 流程都对 Selenium 有原生支持。如果你要对接企业内部的测试平台或者团队里其他人都在用 Java/Selenium你这边用 PythonSelenium 能无缝协作。性能方面Selenium 4 引入了相对定位器Relative Locator和全新的元素等待策略说实话已经没以前那么笨重了。我自己在维护一个每日巡检脚本管理着 30 多个页面Selenium 4 跑下来比旧版稳定得多。所以我的建议是做功能测试、和企业平台集成选 Selenium做轻量数据采集、追求渲染速度可以考虑 Playwright。但如果你刚开始学Selenium 依然是入门性价比最高的选择资料多、问题容易搜到。2.2 Selenium 4 对比旧版的几个关键变化早期版本大家最头疼的就是等待问题所以才会流行各种自定义的 WebDriverWait 封装。Selenium 4 把很多功能内置了比如相对定位器可以用above()、below()、to_left_of()这类方法基于某个参考元素去找附近元素定位组合复杂的 UI 时特别有用。新窗口管理driver.switch_to.new_window()直接开新 Tab不用再靠 JavaScript 模拟。更规范的 Shadow DOM 支持穿透自定义组件的内部结构方便多了。如果你在旧教程里看到find_element_by_id()这种写法那已经废弃了。现在统一用driver.find_element(By.ID, xxx)这点要提前适应否则照着老代码抄会直接报错。3. 环境搭建Python、Selenium 和 Driver 三板斧3.1 安装与版本对应关系先确保本机装好了 Python 3.8 以上的版本。官网下载安装包之后安装时记得勾选 Add Python to PATH这一步省掉后面大量找不到 python 命令的麻烦。装好后打开命令行验证python --version pip --version然后装 Seleniumpip install selenium这里要专门说下 Driver 的问题。Selenium 本质是通过 WebDriver 协议和浏览器通信所以光装库还不够得下载一个和浏览器版本精确对应的驱动。比如你用的是 Chrome 120.0.6099.130那 chromedriver 也必须匹配这个版本号差一个版本都可能启动失败。我在 windows 上习惯用webdriver-manager这个库来自动管理驱动版本几条命令就能搞定pip install webdriver-managerfrom selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager driver webdriver.Chrome(serviceService(ChromeDriverManager().install()))这个库会自动检测当前 Chrome 版本、下载对应驱动并缓存到本地。实测下来省了非常多的手工维护成本。Linux 服务器上同理只要提前装好 Chromium 或用 Chrome都能自动匹配。3.2 显式配置浏览器选项启动浏览器时我建议一开始就把常用配置写好否则后面调试时来回改很耽误事。一个比较完整的启动模板长这样from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_argument(--start-maximized) # 窗口最大化 options.add_experimental_option(detach, True) # 脚本结束不自动关浏览器 options.add_experimental_option(excludeSwitches, [enable-automation]) # 去掉自动化提示条 driver webdriver.Chrome(optionsoptions) driver.get(https://example.com)启动成功后打开目标页面用driver.page_source或直接driver.save_screenshot(home.png)验证一下页面确实加载出来了。跑通这一步你的环境就算立住了。4. 元素定位与核心交互实操4.1 八种定位方式怎么选Selenium 提供了 8 种元素定位方式但实际项目中绝大多数都只需要掌握 4 种ID、CSS 选择器、XPath、链接文本。定位策略的选择直接影响脚本的稳定性我一般按这个优先级来有id优先用By.ID这是最稳定的方式因为 ID 在页面里通常是唯一的。没有id就考虑By.CSS_SELECTOR性能好、语法简洁。当元素没有明显 class 或 id但能通过页面结构描述出唯一路径时用By.XPATH。如果是定位超链接文字By.LINK_TEXT和By.PARTIAL_LINK_TEXT很直观。一个非常常见的错误是新手用绝对 XPath比如//html/body/div[1]/div[2]/form/input。这种写法只要前端加一层 div 就崩了。我个人的习惯是尽量用相对路径配合属性和文本组合定位# 推荐通过 input 的 name 属性和父级表单锁定 username driver.find_element(By.XPATH, //form[idloginForm]//input[nameusername]) # 推荐文本定位按钮 submit_btn driver.find_element(By.XPATH, //button[contains(text(),登录)])4.2 表单填写、点击与滚动排除定位之外第二类核心操作是交互。填输入框、点按钮、滚动页面这三件事覆盖了绝大多数场景from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys # 输入文本 search_box driver.find_element(By.ID, kw) search_box.send_keys(Python Selenium) search_box.send_keys(Keys.ENTER) # 点击 driver.find_element(By.ID, submit).click() # 滚动到页面底部 driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) # 将一个元素滚动到可视区域再操作 target driver.find_element(By.ID, result) driver.execute_script(arguments[0].scrollIntoView(true);, target)关于selenium 网页左右滑动这种常见需求本质就是横向滚动容器。页面里经常有横向滚动的轮播图或数据表格此时不能用window.scrollTo得对具体的容器元素执行滚动# 容器里横向滑动找到滚动的 div修改其 scrollLeft carousel driver.find_element(By.CLASS_NAME, carousel-viewport) driver.execute_script(arguments[0].scrollLeft 500;, carousel)如果要做横向滑动后判断某个元素是否可见可以用is_element_clickable或者element.location_once_scrolled_into_view来辅助判断。处理这类横向滚动容器时脚本执行 JS 是最省事的Selenium 自带的ActionChains拖拽反而容易因为边界条件失控。还有个我踩过的坑click()有时会因为元素被遮挡而报ElementClickInterceptedException。这不是定位写错了而是页面前端有弹层或固定导航栏盖住了目标。解决方式是先让元素滚动到可视区域再直接用 JS 点击兜底driver.execute_script(arguments[0].click();, element)JS 点击绕过了一层模拟点击检查在自动化测试自己的网站时作为兜底方案非常管用。5. 等待机制自动化脚本的隐形命门5.1 为什么强制等待是最差解很多脚本刚开始写的时候都能跑过几天就莫名其妙报NoSuchElementException。原因几乎都是页面还没加载完代码就去找元素了。最简单的解决办法是time.sleep(3)但它有三个问题一是固定死等页面 1 秒加载完也照样白等 2 秒二是网络慢时 3 秒又不够三是脚本整体时间被拖得很长。所以强制等待只能用来临时调试不能当主力方案。正确做法是用 Selenium 的显式等待。它会在指定时间内反复检测某个条件直到条件满足或超时。理解成等人等到出现为止而不是数完 3 秒再说from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 10) login_button wait.until(EC.element_to_be_clickable((By.ID, loginBtn))) login_button.click()ec.element_to_be_clickable是个高频使用的条件它同时检查元素是否存在、是否可见、是否可点击比presence_of_element_located更严格也更符合实际点击场景。5.2 显式等待和隐式等待别混用除了显式等待Selenium 还提供implicitly_wait()它表示全局轮询时间——每找一个元素最长等待 N 秒。这两个机制可以共存但我强烈建议不要混用因为它们底层计时逻辑叠加后会让某些失败用例的等待时间变成两个等待时间相乘排查起来非常痛苦。我个人的习惯是只用显式等待配合等待条件的封装。经验之谈公共函数里把等待逻辑抽成一层比如下面这种def wait_and_click(driver, locator, timeout10): element WebDriverWait(driver, timeout).until( EC.element_to_be_clickable(locator) ) element.click() return element后面所有脚本都用这同一个函数遇到弹窗、校验、异常按钮统一在这里补充处理逻辑维护面就一个文件。写了三年自动化我最大的体会就是自动化脚本的稳定性不是靠定位技巧堆出来的是靠统一的等待策略撑起来的。6. 实际项目中的完整流程与踩坑记录6.1 一套完整的登录加数据采集脚本把前面这些东西串起来给你看一个我在实际项目中常用的骨架。任务是每天登录一个内部系统、按条件查询、抓取表格数据并写入 Excelimport 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 options webdriver.ChromeOptions() # 后台运行配置适合部署到服务器定时执行 options.add_argument(--headless) options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) driver webdriver.Chrome(optionsoptions) try: driver.get(https://your-internal-system.com/login) wait WebDriverWait(driver, 10) # 登录 wait.until(EC.presence_of_element_located((By.ID, username))).send_keys(your_account) driver.find_element(By.ID, password).send_keys(your_password) driver.find_element(By.ID, loginBtn).click() # 等待登录完成进入列表页 wait.until(EC.url_contains(dashboard)) # 滚动加载全部数据 last_height driver.execute_script(return document.body.scrollHeight) while True: driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(1) new_height driver.execute_script(return document.body.scrollHeight) if new_height last_height: break last_height new_height # 提取表格数据 rows driver.find_elements(By.XPATH, //table[iddataTable]/tbody/tr) data [] for row in rows: cells row.find_elements(By.TAG_NAME, td) data.append([cell.text for cell in cells]) df pd.DataFrame(data) df.to_excel(daily_report.xlsx, indexFalse) print(数据写入完成共, len(data), 行) finally: driver.quit()这里有几个细节要说明time.sleep(1)在滚动循环里是必要的它不是在等元素出现而是给页面一个渲染缓冲无限滚动到底的判断用的是页面高度不再变化这个方案对大多数滚动加载都有效。finally里的driver.quit()必须有否则浏览器进程残留在后台堆积跑几次服务器内存就被吃光了。6.2 常见问题速查表我把自己踩过并且高频出现在各种提问里的问题整理成了一张表方便你直接对照报错信息常见原因解决方向ModuleNotFoundError: No module named selenium没装库或用错 Python 环境pip install selenium并检查当前解释器路径SessionNotCreatedExceptionDriver 与浏览器版本不匹配用 webdriver-manager 自动匹配NoSuchElementException元素未加载或定位路径错误加显式等待改相对定位ElementClickInterceptedException元素被其他层遮挡scrollIntoView JS 点击兜底TimeoutException等待条件未满足先手动在浏览器里验证元素是否存在脚本正常但数据不全页面是懒加载或有分页模拟滚动到底或循环点击下一页无头模式下拿不到数据某些站点对 headless 有差异先用有头模式写出稳定脚本再切无头验证6.3 关于自动化被站点特征识别这件事这里要单独说一句。现在很多站点的前端会检查当前浏览器是否由自动化工具驱动比如判断navigator.webdriver属性、检查浏览器启动参数等Selenium 默认启动时会暴露这些特征。对于正常的测试和生产环境我一般通过调整启动参数来降低误报概率例如关闭自动化提示条就是其中一步。但更重要的是如果你的目标网站明确禁止自动化访问或者有登录协议限制那就不要强行绕过。技术能力是用来解决正当问题的不是用来对抗服务条款的。合规采集、在自己负责的系统上做自动化测试才是这套工具的正确打开方式。我处理过的多数真实需求其实只要模拟正常的浏览器指纹行为和合理的访问频率根本不需要做任何对抗性操作。7. 进阶技巧与维护心得7.1 处理 iframe、多窗口和上传下载网页自动化绕不开几个特殊场景。第一个是 iframe元素明明在页面上但就是定位不到大概率是因为它在 iframe 里。处理方式很简单先切进 iframe操作完再切回主文档frame driver.find_element(By.XPATH, //iframe[idcontentFrame]) driver.switch_to.frame(frame) # 操作 iframe 内部元素 driver.find_element(By.ID, innerBtn).click() driver.switch_to.default_content()第二个是多窗口。点击某个链接开了新 Tab但 Selenium 仍然停留在旧窗口。正确的流程是记录当前窗口句柄、点开新窗口、切换过去base_window driver.current_window_handle driver.find_element(By.LINK_TEXT, 打开新窗口).click() for handle in driver.window_handles: if handle ! base_window: driver.switch_to.window(handle) break第三个是上传文件。标准的上传控件直接给send_keys传本地文件路径就行不需要模拟点击文件选择框driver.find_element(By.XPATH, //input[typefile]).send_keys(C:\\data\\test.xlsx)7.2 让脚本真正能长期跑的几条经验脚本写完能跑一天不算本事能稳定跑三个月才是。在这个过程中我最受益的几个经验日志先行在关键步骤加print或写入日志文件报错时能直接看到卡在哪一步。失败要留证据except里加上driver.save_screenshot()和当前页面 HTML排查问题比看堆栈快得多。定时任务注意会话用 cron 或计划任务跑脚本时避免把浏览器配置得太特殊无头模式加超时重试是标配。页面改版后第一件事是改定位器前端一改版最稳定的 ID 都可能失效。平时就把定位器抽到独立的配置文件里改版时集中更新不用翻整个脚本。我个人在实际操作中最常用到的一个小技巧是先打开浏览器开发者工具在 Console 里测试一下 XPath 或 CSS 选择器是否能唯一选中目标元素。用document.querySelector验证 CSS用$x()验证 XPath确认无误后再写进 Selenium 脚本。这一步能省掉大量的跑一遍脚本发现定位失败的时间。这套工具链的扩展空间也很大。我后续就把这块自动化的数据接入到了内部的定时任务系统里每天凌晨自动跑完报表再配合 pandas 做简单清洗后通过企业微信机器人推送结果。整个过程从人工操作十几分钟压缩到全自动一到两分钟。你要做的就是先把环境搭起来、把前几节的骨架跑通然后针对自己的页面一点一点把逻辑补完整。自动化脚本的价值往往就是从解决第一个重复劳动开始积累起来的。
网站建设高端定制企业官网