首页 / 资讯中心 / 文章详情

python爬虫selenium模块详解

python爬虫selenium模块详解 ★ FEATURED ARTICLE
前言先厘清一个最容易被混淆的概念Selenium 属于浏览器自动化不是 HTTP 请求工具。你可能听惯了「用 Selenium 做爬虫」但它和requests走的是完全不同的路径。requests是直接向服务器发 HTTP 请求、拿回 HTML 文本再由你自己解析Selenium 则是真的启动一个浏览器进程像人一样打开页面、点击、输入然后你在浏览器已经渲染好的页面上取数据。这个区别决定了很多事。因为浏览器真的把页面渲染出来了那些靠 JavaScript 才能生成的内容懒加载、单页应用在 Selenium 里天然就是可见的而requests拿到的 HTML 里可能一个数据都没有。代价是开浏览器重、慢、吃内存一条路跑几百个页面就不太合适了。选型的原则很简单——页面不需要 JS 渲染就能拿到数据用 HTTP 请求必须等 JS 跑完才有内容才轮到浏览器自动化。还要提醒合规本文示例指向自己构造的本地页面和公开练习页不指向任何真实商业站点。浏览器自动化同样要遵守目标站点的服务条款与 robots 约定不并发压站不尝试绕过登录墙。一、WebDriver 协议是什么Selenium 的核心是一个叫WebDriver 的协议。可以这样理解它你的 Python 代码其实不直接控制浏览器而是通过一个「中间人」——浏览器驱动driver——来转达指令。驱动是一个独立的小程序它把收到的 HTTP 请求翻译成浏览器能懂的操作。链路大致是Python 测试代码│ (通过 WebDriver 协议发 HTTP 命令)▼浏览器驱动 (chromedriver / geckodriver 等)│ (翻译成浏览器内部调用)▼真实浏览器进程 (Chrome / Firefox)因为中间隔着驱动所以有两件事必须匹配。第一驱动版本必须与浏览器版本对应。浏览器更新了、驱动还停在旧版本启动时就会连不上或行为异常。第二驱动必须能从代码里找到。早期要手动下载驱动、配置路径后来 Selenium 内置了Selenium Manager能在多数情况下自动探测并获取匹配的驱动省掉了手工这一步——但它的具体行为以官方文档为准遇到版本不匹配时你仍要知道「驱动与浏览器版本要对上」这条根因。基本用法长这样# 适用于 Python 3.8需先 pip install selenium# 具体参数与浏览器管理行为以 Selenium 官方文档为准from selenium import webdriverdriver webdriver.Chrome()try:driver.get(http://127.0.0.1:8000/local-demo.html) # 本地演示页非真实站点print(driver.title)finally:driver.quit()driver.quit()放在finally里很重要它会把浏览器进程和驱动进程一起关掉。如果脚本中途抛异常而没关浏览器会残留在后台跑几次就堆一堆进程。driver.close()只关当前窗口driver.quit()才结束整个会话两者不要混用。二、定位元素的多种方式拿到页面后第一件事是「找到要操作的元素」。Selenium 提供了多种定位策略统一通过By来指定。# 适用于 Python 3.8from selenium.webdriver.common.by import By# 各种定位方式取第一个匹配要取多个用 find_elementselement driver.find_element(By.ID, submit)element driver.find_element(By.NAME, q)element driver.find_element(By.CLASS_NAME, btn)element driver.find_element(By.TAG_NAME, h1)element driver.find_element(By.LINK_TEXT, 登录)element driver.find_element(By.PARTIAL_LINK_TEXT, 登)element driver.find_element(By.CSS_SELECTOR, div.card a)element driver.find_element(By.XPATH, //button[typesubmit])选用原则能用ID就不要用 CSS能用 CSS 就不要用 XPath。ID 最稳定也最唯一CSS 选择器简洁、浏览器支持好XPath 功能最强能按文本、按父节点反向定位但也最脆——页面结构一动就可能失效而且写法复杂、可读性差。find_element只返回第一个匹配项如果没找到会抛NoSuchElementException不会返回None这是初学时常见的困惑点。三、三种等待方式的权衡重点这是 Selenium 里最值得花时间理解的部分。页面是动态的元素不一定在你「执行到下一条语句」时就存在。等待方式有三种它们不是「哪个更好」的问题而是作用范围、粒度、副作用完全不同。先看对照表方式作用范围粒度主要副作用time.sleep(n)全局无条件死等无每种情况都等满快的情况也白等隐式等待implicitly_wait会话级对所有查找生效粗与显式等待混用会互相干扰显式等待WebDriverWait针对单个条件细写法略长但最可控time.sleep(n)最直接睡够 n 秒再往下走。问题是它不关心页面到底好没好——网络快的时候白等网络慢的时候又不够。把它当主要手段脚本要么慢得离谱要么还是不稳定。隐式等待是对整个会话生效的设置一次之后之后每次find_element如果当下找不到元素会在设定时限内反复重试直到找到或超时。它省事但问题在于颗粒度太粗它只处理「元素存在」这一种等待场景等待点击可交互、等待文本变化这些就无能为力而且它和显式等待混用时行为会变得难以预测等的是两者中更长的那个容易把超时时间叠加放大。显式等待是首选。它针对具体条件等待条件满足就立刻继续不必等满超时。# 适用于 Python 3.8from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECwait WebDriverWait(driver, timeout10) # 最多等 10 秒element wait.until(EC.presence_of_element_located((By.ID, result)))# 等待「可点击」而不是「存在」更贴近真实交互button wait.until(EC.element_to_be_clickable((By.ID, submit)))button.click()WebDriverWait(driver, timeout).until(...)会轮询条件条件返回真值就返回对应结果超时则抛TimeoutExpired。常见的条件有「元素出现」「元素可点击」「元素可见」「文本出现」等具体可用的条件列表以官方文档为准。注意传参格式定位条件是一个(By.XX, 值)的元组少写一层括号是最常见的语法错误。实践建议很明确主用显式等待不用time.sleep硬等慎用甚至不用隐式等待如果一定要设置隐式等待就不要在同一脚本里再混用显式等待的超时逻辑避免两者叠加。四、取数据与渲染后的 DOM浏览器自动化在爬取上的优势就是拿到的是渲染后的 DOM。element.text会返回渲染后该元素的可见文本element.get_attribute(href)拿属性driver.page_source拿当前页面的完整 HTML 源码——注意是当前即 JS 执行之后的样子。这里有一个重要区别要讲清driver.page_source拿到的是「那一刻」的快照。如果页面还在异步加载你在数据还没到时就取拿到的当然是空的。所以取数据前应当先显式等待目标元素出现再取而不是取完发现是空再回头加sleep。同时再强调本文的立场Selenium 解决的是「渲染」问题不是「绕过」问题。遇到需要登录、有验证码、有风控的页面正确做法是走官方授权接口或干脆不做而不是研究怎么让浏览器自动化「假装成人」。这类规避手段本文不涉及。常见坑点❌ 浏览器升级后没更新驱动脚本报连不上会话或版本不匹配。✅ 记住驱动版本必须与浏览器匹配用 Selenium Manager 或按其文档管理版本。❌ 用time.sleep(5)当主要等待手段网络快时白等、慢时依旧失败。✅ 改用显式等待WebDriverWait(...).until(EC...)等待具体条件。❌ 同时设置隐式等待和显式等待超时时间被叠加脚本行为难以预测。✅ 二选一推荐只用显式等待作用范围清晰可控。❌ 把定位条件写成EC.presence_of_element_located(By.ID, x)少一层括号。✅ 条件是元组EC.presence_of_element_located((By.ID, x))。❌ 找不到元素时以为find_element返回None直接判空导致逻辑失效。✅ 它找不到会抛NoSuchElementException要判存在用find_elements看长度。❌ 只用driver.close()后台还留着浏览器和驱动进程越跑越多。✅ 用driver.quit()结束整个会话并放在finally里确保执行。❌ 页面还在异步加载就driver.page_source取到的是空数据还误以为没内容。✅ 先等目标元素出现再取源码page_source是「那一刻」的快照。❌ 明明 HTTP 请求就能拿到的静态页也硬要用 Selenium慢且费资源。✅ 先判断是否需要 JS 渲染不需要就用请求方案需要才上浏览器自动化。总结主题关键结论定位优先By.ID其次 CSSXPath 最强但最脆等待主用显式等待time.sleep硬等最差隐式等待慎与显式混用驱动版本必须与浏览器匹配或用 Selenium Manager 管理会话用driver.quit()收尾并放在finally定位认知浏览器自动化 ≠ HTTP 爬虫只在需要 JS 渲染时才用学 Selenium最该先想清楚的不是 API 怎么调而是「我到底需不需要浏览器」。需要 JS 渲染才有内容它无可替代能直接 HTTP 拿到数据就别用它。把这一层判断建立起来再用显式等待把「等页面就绪」这件事做扎实脚本的稳定性会有质的提升。最后记住合规底线自动化的是你自己的操作不是绕过别人的防护。
阅读完成 · 觉得有帮助?
咨询建站