共计 2167 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在日常开发中,很多开发者会频繁使用 ChatGPT 进行技术讨论或问题解决,但官方并没有提供便捷的批量导出功能。这导致了一些常见需求无法满足:

- 数据分析:想要统计高频问题或对话趋势
- 合规审计:企业用户需要留存沟通记录
- 知识沉淀:将优质对话整理为内部知识库
- 个人备份:防止重要对话意外丢失
网页端和移动端都只能手动复制单条对话,当对话量较大时,这种方式的效率极低。
技术方案对比
方案 1:基于 Playwright 的网页自动化抓取
优点:
– 不需要 OpenAI API 权限
– 可以获取到完整的对话渲染结果(包括代码高亮等格式)
缺点:
– 需要处理动态加载和反爬机制
– 网页结构变更可能导致脚本失效
– 执行效率相对较低
方案 2:OpenAI 官方 API 调用
优点:
– 数据格式规范统一
– 执行效率高
– 官方支持的稳定接口
缺点:
– 需要有效的 API 密钥
– 有调用频率限制
– 部分历史对话可能无法获取
核心实现
Playwright 方案关键技术点
- 模拟登录:
- 使用浏览器上下文保存登录状态
-
处理可能的验证码挑战
-
滚动加载:
- 监测页面高度变化
-
判断加载完成的标志
-
反爬对策:
- 随机化操作间隔
- 使用真人操作模式
API 方案关键技术点
- 分页获取:
- 使用
after参数实现游标分页 -
处理空响应终止条件
-
速率控制:
- 实现指数退避重试
- 响应头解析配额信息
代码示例
Playwright 实现核心代码
import asyncio
from playwright.async_api import async_playwright
async def export_chat_histories():
async with async_playwright() as p:
# 启动浏览器,建议使用 chromium
browser = await p.chromium.launch(headless=False)
context = await browser.new_context()
page = await context.new_page()
# 模拟登录过程
await page.goto('https://chat.openai.com')
await page.fill('#username', 'your_email')
await page.click('button[type="submit"]')
# 等待对话列表加载
await page.wait_for_selector('.conversation-list')
# 滚动加载所有历史记录
last_height = 0
while True:
current_height = await page.evaluate('document.body.scrollHeight')
if current_height == last_height:
break
last_height = current_height
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
await asyncio.sleep(2) # 适当延迟防止被拦截
# 提取对话内容
conversations = await page.query_selector_all('.conversation-item')
for conv in conversations:
title = await conv.inner_text()
print(f"发现对话: {title}")
await browser.close()
API 实现核心代码
import openai
import time
import backoff
@backoff.on_exception(backoff.expo, openai.error.RateLimitError)
async def get_chat_history(api_key, after=None):
openai.api_key = api_key
try:
params = {"limit": 100}
if after:
params["after"] = after
# 获取对话列表
conversations = await openai.ChatCompletion.list(**params)
# 处理获取到的数据
for conv in conversations.data:
print(f"对话 ID: {conv.id}, 创建时间: {conv.created_at}")
# 返回最后一条记录的 ID 用于分页
if conversations.data:
return conversations.data[-1].id
return None
except openai.error.APIError as e:
print(f"API 错误: {e}")
raise
生产环境建议
- 性能优化:
- 对于 API 方案,合理设置并发数(建议 5 -10 个并发)
-
使用连接池管理 HTTP 请求
-
安全性:
- API 密钥使用环境变量或密钥管理服务
-
实施最小权限原则
-
稳定性:
- 实现断点续传机制
- 记录成功导出的最后位置
延伸思考
- 数据管道建设:
- 可以将导出的 JSON 数据直接摄入 ELK 栈
-
使用 Logstash 进行数据清洗和转换
-
企业级方案:
- 实现基于角色的访问控制
- 对话记录加密存储
- 操作审计日志
在实际项目中,两种方案可以结合使用 – 用 API 获取基础数据,再用 Playwright 补充渲染细节。根据具体需求场景选择最合适的方案,或者组合使用取长补短。
正文完
