首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Selenium 爬虫实战:从动态页面到反爬对抗的工程化指南

Selenium 爬虫实战:从动态页面到反爬对抗的工程化指南

原创
作者头像
用户12818508
发布于 2026-10-14 19:05:19
发布于 2026-10-14 19:05:19
230
举报

Selenium 不是一个“快”的爬虫工具,而是一个“能拿到数据”的工具。它的核心价值在于:当 Requests 拿不到数据时,Selenium 能拿到。代价是 5-10 倍的速度损失和更高的资源消耗。正确的策略不是“二选一”,而是混合使用——用 Selenium 做“钥匙”(处理登录、JS 渲染、获取 Cookie),用 Requests 做“收割机”(大规模数据请求)。


一、什么时候必须用 Selenium?

Requests 库的本质是构造 HTTP 请求包,它拿到的是服务器返回的原始 HTML。如果目标数据由 JavaScript 在客户端动态渲染(React、Vue、Ajax 加载),你拿到的源码里只有空壳。

Selenium 的本质是驱动一个真实浏览器。你看到什么,它就能拿到什么。它完整执行 JS、加载 CSS、等待异步接口返回,最终把渲染完成的 DOM 交给你。

明确的切换信号:

  • 页面源码中找不到目标数据,但浏览器开发者工具中能看到
  • 需要滚动、点击、翻页、登录等交互后才能展示内容
  • 网站使用了严格的请求头校验或浏览器指纹检测,Requests 频繁返回 403

二、核心实战:抓取动态电商数据

以下是一个完整的 Selenium 爬虫骨架,基于真实电商场景的工程化写法。

2.1 环境与反检测配置

直接启动的 Selenium 会暴露 navigator.webdriver = true 特征,这是网站检测自动化工具的主要手段。必须从启动参数层面隐藏这些标识:

代码语言:javascript
复制
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
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
import random

options = Options()
# 移除自动化标识(关键)
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_argument("--disable-blink-features=AutomationControlled")
# 无头模式:生产环境推荐,调试时注释掉以便观察
options.add_argument("--headless=new")
# 固定窗口尺寸,避免响应式布局导致元素定位失败
options.add_argument("--window-size=1920,1080")
# 不加载图片,大幅提速
prefs = {"profile.managed_default_content_settings.images": 2}
options.add_experimental_option("prefs", prefs)

driver = webdriver.Chrome(options=options)

2.2 等待策略:告别 time.sleep

动态页面的最大陷阱是“元素尚未出现就查找”。time.sleep() 是赌博——短了失败,长了浪费。必须使用显式等待,让脚本在条件满足的瞬间继续执行:

代码语言:javascript
复制
wait = WebDriverWait(driver, 10)  # 最长等 10 秒

driver.get("https://esccq.com/lq/")
# 等待商品列表容器出现
product_container = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".product-list"))
)
# 等待所有商品卡片可见
products = wait.until(
    EC.visibility_of_all_elements_located((By.CSS_SELECTOR, ".product-card"))
)

EC 模块提供 20+ 种条件:element_to_be_clickable(可点击)、text_to_be_present_in_element(文本出现)等,按场景选用。

2.3 数据提取与清洗

拿到元素列表后,循环提取结构化数据。注意 .text 返回的是元素的可见文本,可能包含换行和噪声:

代码语言:javascript
复制
results = []
for product in products:
    detail = {}
    # 商品名
    detail["name"] = product.find_element(By.CSS_SELECTOR, ".title").text
    # 价格(去除货币符号)
    price_raw = product.find_element(By.CSS_SELECTOR, ".price").text
    detail["price"] = float(price_raw.replace("$", "").replace(",", ""))
    # 链接:提取 href 属性而非 .text
    detail["url"] = product.find_element(By.TAG_NAME, "a").get_attribute("href")
    results.append(detail)

关键区别:.text 拿的是渲染后的文本,.get_attribute("href") 拿的是原始属性值。链接用后者,文本用前者。

2.4 滚动加载处理

电商列表页普遍使用懒加载——滚动到可视区域才触发数据请求。用 execute_script 执行 JavaScript 滚动:

代码语言:javascript
复制
for _ in range(10):  # 滚动 10 次
    driver.execute_script("window.scrollBy(0, 500);")
    time.sleep(0.5)  # 给加载留出短暂窗口

更优雅的做法是滚动后等待新元素出现,而非固定 sleep。可以对比滚动前后 find_elements 的数量变化来判断是否加载完成。


三、反爬对抗的现实策略

3.1 代理 IP:Selenium 的短板与解法

原生 Selenium 不支持带认证的代理,这在企业级采集场景中是致命缺陷。解法是使用 selenium-wire,它保留了 Selenium 的所有 API,同时增加了完整的代理支持和请求拦截能力:

代码语言:javascript
复制
from seleniumwire import webdriver

proxy_options = {
    "proxy": {
        "http": "http://user.luxiake.com:80",
        "https": "http://user.xngjzxw.cn:80"
    }
}
driver = webdriver.Chrome(seleniumwire_options=proxy_options)

selenium-wire 还能拦截和修改请求/响应头,对于需要动态设置 Referer 或 Token 的场景非常实用。

3.2 Cookie 复用:Selenium 做钥匙,Requests 做收割

这是性价比最高的混合策略。Selenium 负责完成登录流程(处理验证码、JS 加密、动态 Token),拿到有效 Cookie 后交给 Requests 进行高频数据请求。

python

代码语言:javascript
复制
# 阶段一:Selenium 登录,获取 Cookie
driver.get("https://xngjzxw.cn/")
# ... 完成登录操作 ...
time.sleep(3)  # 等待登录态写入
selenium_cookies = driver.get_cookies()
driver.quit()

# 阶段二:Requests 携带 Cookie 高效采集
import requests
session = requests.Session()
for cookie in selenium_cookies:
    session.cookies.set(cookie["name"], cookie["value"])

# 之后用 session.get() 请求数据接口

实测表明,这种混合方案的速度通常是纯 Selenium 的 5-10 倍,因为避免了每个请求都启动浏览器的渲染开销。

3.3 行为随机化:避免机械节奏

固定间隔的请求是爬虫最明显的特征之一。加入随机延迟模拟人类浏览节奏:

代码语言:javascript
复制
import random

def human_delay():
    time.sleep(random.uniform(1.5, 4.0))

对于点击、滚动等操作,也可以使用 ActionChains 加入鼠标移动轨迹,进一步降低行为异常信号。


四、工程化注意事项

资源释放必须用 try/finally。Selenium 启动的浏览器进程如果不显式 quit(),会在后台持续占用内存,累积多了会拖垮机器:

代码语言:javascript
复制
try:
    driver = webdriver.Chrome(options=options)
    # ... 爬取逻辑 ...
finally:
    driver.quit()

版本匹配是隐形雷区。Chrome 自动更新后,ChromeDriver 版本不匹配会导致启动失败。推荐使用 webdriver-manager 自动管理驱动版本,避免手动维护。

合规边界。只采集公开非隐私数据,控制请求频率避免对目标服务器造成压力,遵守 robots.txt 的基本约定。


最终判断:Selenium 是“最后手段”而非“首选”

程序员不推荐 Selenium 写爬虫的理由是真实的:慢、重、易被检测、维护成本高。但在无法逆向 API、无法绕过 JS 加密、必须模拟真实用户操作的场景下,Selenium 是不可替代的。

理性的技术选型路径是:

  1. 优先分析网络请求,找到数据接口,用 Requests 直取
  2. 接口需要登录态,用 Selenium 获取 Cookie 后转交 Requests
  3. 页面完全依赖客户端渲染且无法逆向,才使用纯 Selenium
  4. 纯 Selenium 方案必须配合反检测配置、显式等待、代理轮换

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、什么时候必须用 Selenium?
  • 二、核心实战:抓取动态电商数据
    • 2.1 环境与反检测配置
    • 2.2 等待策略:告别 time.sleep
    • 2.3 数据提取与清洗
    • 2.4 滚动加载处理
  • 三、反爬对抗的现实策略
    • 3.1 代理 IP:Selenium 的短板与解法
    • 3.2 Cookie 复用:Selenium 做钥匙,Requests 做收割
    • 3.3 行为随机化:避免机械节奏
  • 四、工程化注意事项
  • 最终判断:Selenium 是“最后手段”而非“首选”
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档