'# 网络请求爬虫【requests】和自动化爬虫【selenium】
一、背景与问题
在数据采集领域,网络请求爬虫(requests)和自动化爬虫(selenium)是两种主流技术方案。前者通过模拟HTTP请求获取静态网页内容,后者通过模拟浏览器行为处理动态网页内容。这两种技术在实际项目中存在显著差异:
- requests:基于HTTP协议的轻量级工具,适用于静态页面数据采集,但无法处理JavaScript动态渲染内容
- selenium:基于浏览器自动化的工具,能完整模拟用户操作,但资源消耗大且存在反爬机制
本文将深入分析这两种技术的原理、实现方式、适用场景和常见问题,通过代码示例和完整案例展示其实际应用。
二、基本原理
1. requests 的工作原理
requests 库基于 Python 的 urllib3 实现,通过发送 HTTP/HTTPS 请求获取服务器响应。其核心流程如下:
- 构造 HTTP 请求头(User-Agent、Cookie 等)
- 发送 GET/POST 请求
- 处理响应头(Status Code, Content-Type)
- 解析响应内容(HTML, JSON, XML 等)
关键特性:
- 支持会话保持(Session)
- 自动处理重定向
- 内置异常处理机制
2. selenium 的工作原理
selenium 通过 WebDriver 接口与浏览器内核通信,其核心流程包括:
- 启动浏览器实例(Chrome/Firefox 等)
- 通过 WebDriver 发送操作指令(点击、输入等)
- 浏览器执行 JavaScript 操作
- 获取 DOM 内容或截图
关键特性:
- 完全模拟真实用户行为
- 支持动态内容加载
- 可处理复杂交互(如 AJAX 请求)
三、环境准备
# 安装依赖
pip install requests selenium# 安装浏览器驱动(以 Chrome 为例)
# 下载 ChromeDriver: https://chromedriver.storage.googleapis.com/
# 将 chromedriver 放入系统路径四、核心实现
1. requests 的核心代码
import requests
from bs4 import BeautifulSoup
def fetch_page(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 检查 HTTP 错误
return response.text
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
# 解析示例
html = fetch_page("https://example.com")
soup = BeautifulSoup(html, 'html.parser')
print(soup.title.string)关键点解释:
User-Agent避免被识别为爬虫raise_for_status()检查 4xx/5xx 错误timeout参数控制超时时间
2. selenium 的核心代码
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
def login_website():
driver = webdriver.Chrome()
try:
driver.get("https://example.com/login")
# 填充表单
username = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.NAME, "username"))
)
username.send_keys("test_user")
password = driver.find_element(By.NAME, "password")
password.send_keys("test_password")
# 提交表单
driver.find_element(By.XPATH, "//button[@type='submit']").click()
# 等待页面加载
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "content"))
)
print(driver.find_element(By.ID, "content").text)
finally:
driver.quit()关键点解释:
WebDriverWait实现显式等待,避免因页面加载延迟导致的元素定位失败By类提供多种定位策略(ID, XPath, CSS 等)find_element和find_elements用于DOM元素操作
3. 动态内容处理对比
# requests 无法获取动态内容
html = requests.get("https://example.com/dynamic").text
# 结果中可能缺少 JavaScript 渲染的 DOM 内容
# selenium 可完整获取动态内容
driver = webdriver.Chrome()
driver.get("https://example.com/dynamic")
print(driver.page_source)五、完整案例
案例:电商商品信息采集
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def get_product_info():
# 第一步:获取初始页面(requests)
base_url = "https://example-ecommerce.com"
headers = {'User-Agent': 'Mozilla/5.0'}
# 获取商品列表页
html = requests.get(base_url + "/products", headers=headers).text
soup = BeautifulSoup(html, 'html.parser')
product_links = [a['href'] for a in soup.select('.product-link')]
# 第二步:获取商品详情页(selenium)
driver = webdriver.Chrome()
for link in product_links[:5]: # 只爬取前5个商品
try:
driver.get(base_url + link)
# 等待 JavaScript 加载
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "product-title"))
)
title = driver.find_element(By.ID, "product-title").text
price = driver.find_element(By.ID, "product-price").text
description = driver.find_element(By.ID, "product-description").text
print(f"{title} - {price} - {description}")
# 模拟用户操作(如点击 "Add to cart")
driver.find_element(By.ID, "add-to-cart").click()
time.sleep(2) # 避免请求频率过高
except Exception as e:
print(f"处理商品失败: {e}")
driver.quit()关键点说明:
- 结合 requests 和 selenium 的优势:用 requests 获取静态列表,用 selenium 处理动态详情页
- 模拟用户操作(点击、输入)更符合真实场景
- 需要处理浏览器控制权释放(如 sleep、等待)
六、源码解析
requests 的核心流程
# requests 的核心请求流程(简化版)
def request(url, headers):
session = requests.Session()
response = session.get(url, headers=headers)
return response.text关键机制:
Session对象维护 cookies 和 headers- 内部使用 urllib3 实现 HTTP 协议
- 自动处理重定向(默认 max_redirects=10)
selenium 的 WebDriver 通信
# selenium 与浏览器的通信机制(伪代码)
def send_command(command):
driver.execute_script("""
// 通过 WebDriver 协议发送命令
// 通过浏览器内核执行 JavaScript
// 返回结果
""")关键机制:
- 使用 WebDriver 协议(JSON Wire Protocol)通信
- 支持多种浏览器引擎(Chrome, Firefox, Edge 等)
- 内部使用浏览器的 DOM 操作能力
七、进阶使用
1. requests 的高级用法
# 使用会话保持处理 Cookie
session = requests.Session()
session.headers.update({'Authorization': 'Bearer token123'})
response = session.get("https://api.example.com/data")2. selenium 的高级用法
# 使用代理和代理认证
options = webdriver.ChromeOptions()
options.add_argument('--proxy-server=http://10.10.1.10:3128')
options.add_argument('--proxy-user=proxy_user:proxy_pass')
driver = webdriver.Chrome(options=options)3. 结合两种技术的优化方案
# 用 requests 获取页面,用 selenium 处理动态内容
def hybrid_crawler():
html = requests.get("https://example.com").text
soup = BeautifulSoup(html, 'html.parser')
dynamic_links = [a['href'] for a in soup.select('.dynamic-content')]
driver = webdriver.Chrome()
for link in dynamic_links:
driver.get(link)
# 处理动态内容...八、性能与工程实践
1. requests 的性能优化
# 使用连接池和并发
from concurrent.futures import ThreadPoolExecutor
def fetch_page(url):
# 省略具体实现...
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch_page, urls))优化建议:
- 使用
requests.Session()重用连接 - 设置
timeout避免阻塞 - 使用
httpx异步库提升性能
2. selenium 的性能优化
# 使用无头模式和并发
from selenium import webdriver
from concurrent.futures import ThreadPoolExecutor
def get_page(url):
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get(url)
# 处理页面...
driver.quit()
with ThreadPoolExecutor(max_workers=5) as executor:
executor.map(get_page, urls)优化建议:
- 启用无头模式减少资源占用
- 使用
Selenium Wire抓取 HTTP 请求 - 使用
Playwright作为替代方案(更高效的浏览器自动化)
3. 异常处理与资源管理
# 增强异常处理
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"请求异常: {e}")
# 记录日志、重试机制、告警通知九、常见问题与踩坑
1. requests 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 403 Forbidden | 被识别为爬虫 | 设置 User-Agent、使用代理 |
| 503 服务不可用 | 服务器限流 | 使用代理、降低请求频率 |
| 证书错误 | SSL 证书过期 | 设置 verify=False(不推荐) |
2. selenium 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 元素定位失败 | 页面未完全加载 | 使用 WebDriverWait 显式等待 |
| 浏览器崩溃 | 资源占用过高 | 启用无头模式、使用 headless 模式 |
| 验证码识别失败 | 人机验证机制 | 使用第三方验证码识别服务 |
3. 安全风险分析
requests 风险:
- 可能被服务器识别为爬虫
- 未处理 SSL 证书导致中间人攻击
- 频繁请求导致被封IP
selenium 风险:
- 浏览器指纹识别(Browser Fingerprinting)
- 反爬虫机制(如 CAPTCHA)
- 操作日志记录(部分网站会记录自动化行为)
十、最佳实践
1. 使用场景推荐
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 静态网页数据采集 | requests | 轻量、高效 |
| 动态网页数据采集 | selenium | 完全模拟用户行为 |
| 需要模拟复杂交互 | selenium | 支持 JavaScript 操作 |
| 高并发采集 | requests + 异步 | 更好的性能 |
2. 实践建议
- requests:优先使用
Session对象,设置合理的 headers 和 timeout - selenium:启用无头模式,使用
ChromeOptions配置代理和用户代理 - 混合使用:将 requests 用于静态页面获取,selenium 处理动态内容
- 反爬应对:使用代理 IP、设置随机 User-Agent、处理验证码
十一、总结
requests 和 selenium 是数据采集领域的两种重要工具,各有适用场景。requests 适用于静态内容采集,具有轻量高效的特点;selenium 能处理动态内容,但资源消耗较大。实际开发中应根据具体需求选择合适方案:
- 使用 requests:当需要快速获取静态页面数据,且无需处理 JavaScript 渲染
- 使用 selenium:当需要模拟真实用户行为,处理动态加载内容或复杂交互
- 混合使用:结合两者优势,用 requests 获取静态列表,用 selenium 处理动态详情页
在实际项目中,还需注意法律风险和道德规范,遵守网站的 robots.txt 规则,避免对服务器造成过大压力。通过合理选择工具、优化性能、处理异常,可以构建高效稳定的数据采集系统。