Selenium 不是一个“快”的爬虫工具,而是一个“能拿到数据”的工具。它的核心价值在于:当 Requests 拿不到数据时,Selenium 能拿到。代价是 5-10 倍的速度损失和更高的资源消耗。正确的策略不是“二选一”,而是混合使用——用 Selenium 做“钥匙”(处理登录、JS 渲染、获取 Cookie),用 Requests 做“收割机”(大规模数据请求)。
Requests 库的本质是构造 HTTP 请求包,它拿到的是服务器返回的原始 HTML。如果目标数据由 JavaScript 在客户端动态渲染(React、Vue、Ajax 加载),你拿到的源码里只有空壳。
Selenium 的本质是驱动一个真实浏览器。你看到什么,它就能拿到什么。它完整执行 JS、加载 CSS、等待异步接口返回,最终把渲染完成的 DOM 交给你。
明确的切换信号:
以下是一个完整的 Selenium 爬虫骨架,基于真实电商场景的工程化写法。
直接启动的 Selenium 会暴露 navigator.webdriver = true 特征,这是网站检测自动化工具的主要手段。必须从启动参数层面隐藏这些标识:
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)动态页面的最大陷阱是“元素尚未出现就查找”。time.sleep() 是赌博——短了失败,长了浪费。必须使用显式等待,让脚本在条件满足的瞬间继续执行:
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(文本出现)等,按场景选用。
拿到元素列表后,循环提取结构化数据。注意 .text 返回的是元素的可见文本,可能包含换行和噪声:
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") 拿的是原始属性值。链接用后者,文本用前者。
电商列表页普遍使用懒加载——滚动到可视区域才触发数据请求。用 execute_script 执行 JavaScript 滚动:
for _ in range(10): # 滚动 10 次
driver.execute_script("window.scrollBy(0, 500);")
time.sleep(0.5) # 给加载留出短暂窗口更优雅的做法是滚动后等待新元素出现,而非固定 sleep。可以对比滚动前后 find_elements 的数量变化来判断是否加载完成。
原生 Selenium 不支持带认证的代理,这在企业级采集场景中是致命缺陷。解法是使用 selenium-wire,它保留了 Selenium 的所有 API,同时增加了完整的代理支持和请求拦截能力:
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 的场景非常实用。
这是性价比最高的混合策略。Selenium 负责完成登录流程(处理验证码、JS 加密、动态 Token),拿到有效 Cookie 后交给 Requests 进行高频数据请求。
python
# 阶段一: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 倍,因为避免了每个请求都启动浏览器的渲染开销。
固定间隔的请求是爬虫最明显的特征之一。加入随机延迟模拟人类浏览节奏:
import random
def human_delay():
time.sleep(random.uniform(1.5, 4.0))对于点击、滚动等操作,也可以使用 ActionChains 加入鼠标移动轨迹,进一步降低行为异常信号。
资源释放必须用 try/finally。Selenium 启动的浏览器进程如果不显式 quit(),会在后台持续占用内存,累积多了会拖垮机器:
try:
driver = webdriver.Chrome(options=options)
# ... 爬取逻辑 ...
finally:
driver.quit()版本匹配是隐形雷区。Chrome 自动更新后,ChromeDriver 版本不匹配会导致启动失败。推荐使用 webdriver-manager 自动管理驱动版本,避免手动维护。
合规边界。只采集公开非隐私数据,控制请求频率避免对目标服务器造成压力,遵守 robots.txt 的基本约定。
程序员不推荐 Selenium 写爬虫的理由是真实的:慢、重、易被检测、维护成本高。但在无法逆向 API、无法绕过 JS 加密、必须模拟真实用户操作的场景下,Selenium 是不可替代的。
理性的技术选型路径是:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。