共计 2943 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
从 ChatGPT 网页端抽取数据时,开发者常遇到三个主要难点:

- 动态渲染:对话内容通过 JavaScript 动态加载,传统 HTTP 请求无法获取完整 DOM 树
- 速率限制:频繁请求会触发 429 错误,甚至导致临时封禁
- 会话保持:需要维持登录状态才能访问历史对话记录
这些特性使得常规的 Requests+BeautifulSoup 组合难以奏效,必须采用能够执行 JavaScript 的浏览器自动化工具。
技术选型对比
目前主流方案主要有三种技术路线:
- Requests+BeautifulSoup
- 优点:轻量级、速度快
- 局限:无法处理动态内容
-
适用场景:静态页面抓取
-
Selenium
- 优点:完整浏览器环境、支持所有 JS 渲染
- 缺点:资源消耗大
-
适用场景:需要交互操作的复杂页面
-
Playwright
- 优点:多浏览器支持、自动等待机制
- 缺点:较新生态
- 适用场景:需要跨浏览器测试的项目
对于 ChatGPT 这种 SPA 应用,Selenium 目前仍是平衡功能和稳定性的最佳选择。
核心实现方案
1. Selenium 环境配置
需要先安装浏览器驱动(以 Chrome 为例):
- 安装 Selenium 包:
pip install selenium - 下载对应版本的 ChromeDriver
- 配置无头模式减少资源占用
2. 登录状态保持
关键技巧:
- 使用
get_cookies()保存登录凭据 - 通过
add_cookie()恢复会话 - 设置合理的 session 过期处理逻辑
3. 动态加载处理
ChatGPT 采用无限滚动加载,需要模拟滚动操作:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(2) # 等待新内容加载
4. 内容解析最佳实践
推荐使用 XPath 定位对话气泡:
# 定位 AI 回复
ais = driver.find_elements(By.XPATH, '//div[contains(@class,"ai-message")]')
# 定位用户提问
users = driver.find_elements(By.XPATH, '//div[contains(@class,"user-message")]')
完整代码示例
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import json
# 初始化浏览器
def init_browser():
options = webdriver.ChromeOptions()
options.add_argument("--headless") # 无头模式
driver = webdriver.Chrome(options=options)
return driver
# 登录并保存 cookies
def login(driver, email, password):
driver.get("https://chat.openai.com/auth/login")
time.sleep(3)
# 执行登录操作(示例伪代码)driver.find_element(By.ID, "username").send_keys(email)
driver.find_element(By.ID, "password").send_keys(password)
driver.find_element(By.XPATH, "//button[contains(text(),' 登录 ')]").click()
time.sleep(5) # 等待登录完成
return driver.get_cookies()
# 加载完整对话历史
def load_full_conversation(driver, url):
driver.get(url)
last_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(2)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
return driver.page_source
if __name__ == "__main__":
driver = init_browser()
cookies = login(driver, "your_email", "your_password")
# 保存 cookies 供下次使用
with open("cookies.json", "w") as f:
json.dump(cookies, f)
html = load_full_conversation(driver, "https://chat.openai.com/c/conv-id")
driver.quit()
风险控制策略
1. 请求频率控制
推荐使用指数退避算法:
import random
def exponential_backoff(retries):
base_delay = 5 # 基础延迟秒数
max_delay = 60 # 最大延迟
delay = min(base_delay * (2 ** retries) + random.uniform(0, 1), max_delay)
time.sleep(delay)
2. 反检测措施
- 轮换 User-Agent
- 随机化操作间隔
- 禁用自动化特征:
options.add_argument("--disable-blink-features=AutomationControlled")
3. 验证码处理预案
建议设置验证码触发阈值报警,当出现以下情况时暂停采集:
- 连续 3 次请求返回 403
- 页面出现验证码元素
- 响应时间异常延长
避坑指南
1. 避免 429 错误
关键 header 配置:
headers = {
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://chat.openai.com/",
"DNT": "1"
}
2. 指纹检测规避
无头模式下的额外配置:
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
3. 数据去重设计
建议使用对话 ID+ 时间戳作为唯一键:
import hashlib
def generate_msg_id(msg):
return hashlib.md5(f"{msg['timestamp']}-{msg['content'][:50]}".encode()).hexdigest()
开放性问题
当需要大规模采集时,如何设计分布式爬取架构?考虑以下方面:
- 任务调度:如何分配 URL 给不同节点
- 状态同步:确保所有节点共享最新的反爬策略
- 数据一致性:避免重复采集和冲突
- 监控系统:实时掌握各节点状态
期待大家在评论区分享自己的分布式爬虫设计思路!
正文完
发表至: 未分类
近三天内
