共计 3196 个字符,预计需要花费 8 分钟才能阅读完成。
1. 背景痛点分析
ChatGPT 作为基于 AJAX 的动态网页,其核心内容通过 JavaScript 异步加载,传统 requests 库无法直接获取有效数据。实际爬取时会遇到三个主要难点:

- 动态元素延迟加载 :对话内容需等待 API 响应后才渲染到 DOM,直接获取会出现元素不存在的情况
- Cloudflare 反爬 :连续高频访问会触发 5 秒人机验证,甚至封禁 IP
- 行为检测机制 :固定的操作间隔和线性鼠标轨迹会被识别为机器人行为
2. 技术选型对比
2.1 Selenium 优势
- 成熟的浏览器自动化框架,支持 Chrome/Firefox/Edge 等多平台
- 完善的等待机制(显式 / 隐式等待)
- 丰富的模拟输入 API(键盘 / 鼠标操作)
2.2 Playwright 特点
- 更快的执行速度(直接通过 CDP 协议控制浏览器)
- 内置自动等待功能
- 支持多语言(Python/Java/C#)
最终选择 Selenium 的原因:社区资源丰富,遇到问题更容易找到解决方案
3. 核心实现方案
3.1 动态元素定位策略
推荐使用 CSS 选择器 + 显式等待组合方案:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待消息框出现
message_box = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div[role='presentation']"))
)
3.2 会话保持技巧
-
首次登录后保存 cookies:
import pickle # 登录后保存 pickle.dump(driver.get_cookies(), open("cookies.pkl", "wb")) # 后续会话复用 cookies = pickle.load(open("cookies.pkl", "rb")) for cookie in cookies: driver.add_cookie(cookie) -
定期检测登录状态,遇到失效自动重新认证
3.3 人类行为模拟
from selenium.webdriver.common.action_chains import ActionChains
import random
import time
# 随机延迟(1- 3 秒)time.sleep(random.uniform(1, 3))
# 非线性鼠标移动
actions = ActionChains(driver)
actions.move_to_element_with_offset(element,
random.randint(5,15),
random.randint(5,15))
actions.perform()
4. 完整代码示例
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
# 浏览器配置
chrome_options = Options()
chrome_options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36")
chrome_options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=chrome_options)
try:
# 访问登录页
driver.get("https://chat.openai.com")
# 等待登录完成(手动操作)WebDriverWait(driver, 120).until(EC.url_contains("/chat")
)
# 对话交互示例
for i in range(3):
# 定位输入框
input_box = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "textarea#prompt-textarea"))
)
# 模拟人类输入速度
for char in f"测试问题 {i}":
input_box.send_keys(char)
time.sleep(random.uniform(0.1, 0.3))
# 随机延迟后提交
time.sleep(random.uniform(1, 2))
input_box.submit()
# 等待响应完成
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div[class*='request-:r']"))
)
# 获取响应内容
responses = driver.find_elements(By.CSS_SELECTOR, "div[class*='response-:r']")
print(f"响应内容: {responses[-1].text}")
# 操作间隔
time.sleep(random.uniform(5, 10))
except Exception as e:
print(f"发生错误: {str(e)}")
finally:
driver.quit()
5. 性能与安全优化
5.1 IP 轮换方案
- 使用住宅代理服务(Luminati/StormProxies)
- 每个会话更换 IP:
chrome_options.add_argument(f"--proxy-server=http://{get_random_proxy()}")
5.2 请求频率控制
- 遵循 30/30 规则:每 30 分钟不超过 30 次请求
- 动态调整间隔:
delay = base_delay * (1 + random.random()) # 随机波动
5.3 验证码处理
- 使用 2Captcha 等付费服务
- 自动检测验证码出现:
if "验证码" in driver.page_source: solve_captcha()
6. 常见问题解决方案
6.1 XPath 失效问题
- 避免使用绝对路径,改用相对路径
- 示例改进:
# 不推荐 //*[@id="__next"]/div/div[2]/div[2]/div/div[3]/div/div/div # 推荐 //div[contains(@class, 'response-')]
6.2 内存泄漏预防
- 及时清理无用的 WebElement 引用
- 定期重启浏览器实例(每 2 小时)
6.3 版本兼容性
- 使用 WebDriverManager 自动匹配驱动版本:
from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(ChromeDriverManager().install())
7. 扩展思考
本文方案可迁移到其他动态网站如:
– 微博 /Twitter 等社交媒体
– 电商商品评论抓取
– 金融实时数据监控
对于更复杂的场景,建议尝试:
– Playwright 的自动等待特性
– Puppeteer 的请求拦截功能
– 结合 Scrapy 构建分布式爬虫
实际项目中建议根据具体需求组合多种技术,并严格遵守目标网站的 robots.txt 规定。
正文完
