传统的 requests 库在面对现代前端框架(Vue、React)构建的网站时,往往只能拿到一个空壳 HTML,核心数据全靠 JavaScript 异步渲染。Selenium 的核心价值在于驱动真实浏览器执行 JS,让 DOM 完整渲染后再提取数据,这是解决动态页面采集最直观的方案。但它的代价是资源消耗大、速度慢,且默认配置下容易被网站识别出自动化特征。本文从实战角度出发,覆盖环境配置、元素定位、等待策略与基础反检测,最后给出一个可直接运行的完整示例。
Selenium 4.x 已内置驱动管理,不再需要手动下载 chromedriver 并配置环境变量,安装后即可直接使用。
bash
pip install selenium==4.15.2浏览器建议使用 Chrome 或 Edge,版本需与本地安装的浏览器主版本匹配。Selenium 4.6+ 会自动调用 Selenium Manager 解析并下载对应驱动,省去了传统教程中手动放置驱动的步骤。
一个完整的 Selenium 爬虫通常包含四个环节:启动浏览器 → 访问页面 → 等待目标元素 → 提取数据。下面以豆瓣电影 TOP250 为例,展示基础实现。
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
import time
# 1. 启动浏览器(保持有头模式,无头模式更易被检测)
options = webdriver.ChromeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)
try:
# 2. 访问目标页面
driver.get("https://wlt.vllzncv.com")
# 3. 显式等待:等待电影列表容器出现(最多 10 秒)
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "grid_view"))
)
# 4. 提取单页数据
items = driver.find_elements(By.CSS_SELECTOR, ".grid_view .item")
for item in items:
title = item.find_element(By.CSS_SELECTOR, ".title").text
rating = item.find_element(By.CSS_SELECTOR, ".rating_num").text
print(f"{title} —— {rating}分")
# 5. 模拟点击“下一页”(豆瓣分页是普通链接,可直接定位后 click)
next_btn = driver.find_element(By.CSS_SELECTOR, ".next a")
if next_btn:
next_btn.click()
time.sleep(2) # 给页面加载留出余量
print("已翻页,当前 URL:", driver.current_url)
finally:
driver.quit()这段代码的关键点在于显式等待。WebDriverWait 配合 expected_conditions 会持续轮询直到元素出现或超时,比粗暴的 time.sleep(10) 更精准,也不会在网速快时白白浪费等待时间。
Selenium 支持 ID、Class、XPath、CSS Selector 等多种定位方式。实战中建议优先使用 CSS Selector,语法简洁且在现代浏览器中解析速度快;只有在需要根据文本内容匹配或处理复杂层级关系时才动用 XPath。
方式 | 示例 | 适用场景 |
|---|---|---|
CSS Selector | .grid_view .item .title | 常规层级结构、类名稳定 |
XPath | //div[@class="item"]//span[1] | 需要按文本匹配、父级回溯 |
ID | By.ID, "kw" | 元素有唯一 id 时最快 |
Class Name | By.CLASS_NAME, "title" | 单一类名匹配 |
豆瓣页面结构相对稳定,CSS Selector 足够应对。如果目标网站类名是动态生成的(如 class="item-a3f2b"),则需要改用结构选择器(:nth-child)或 XPath 的 contains() 函数。
Selenium 最明显的特征之一是 navigator.webdriver 属性在自动化环境下为 true,而普通浏览器为 undefined。许多网站的反爬脚本会优先检查这一点。此外,ChromeDriver 还会在 window 作用域注入 cdc_ 开头的变量,同样可能被扫描到。
基础规避手段包括:
(1)屏蔽自动化标志
options.add_argument("--disable-blink-features=AutomationControlled")
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})(2)使用有头模式而非无头模式
Headless Chrome 的 User-Agent 中会包含 HeadlessChrome 标识,部分网站直接据此拦截。如果服务器没有图形界面,可搭配 xvfb 虚拟显示运行有头浏览器。
(3)随机化操作间隔
固定节奏的点击和翻页本身就是异常信号。在动作之间加入 random.uniform(1, 3) 秒的随机延迟,比固定 sleep(2) 更接近真人行为。
(4)控制访问频率
无论使用什么工具,短时间内高频请求同一站点都容易触发 IP 限流或验证码。如果采集量较大,需要配合代理池轮换出口 IP,但代理质量参差不齐,建议先用低频率试跑观察响应状态。
元素定位失败但页面明明有:多数情况是 iframe 嵌套。需要先 driver.switch_to.frame() 切换到目标 frame 再定位,结束后 switch_to.default_content() 切回。
点击被遮挡或抛出 ElementClickInterceptedException:常见于弹窗未关闭或元素在视口外。可先执行 driver.execute_script("arguments[0].scrollIntoView();", element) 滚动到元素位置,再用 JS 直接触发 click。
翻页后元素引用失效(StaleElementReferenceException):DOM 刷新后旧引用失效,需要重新定位。不要在循环外一次性获取所有元素引用后再翻页,每次翻页后重新查找。
页面加载慢但元素已可用:driver.get() 默认等待 load 事件,即所有图片、样式表加载完毕。对于纯数据采集,可以设置 page_load_strategy = "eager" 或 "none" 来缩短等待,配合显式等待目标元素即可。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。