基于Selenium的ChatGPT非官方API:原理、实现与避坑指南

📅 2026/7/4 10:42:03
基于Selenium的ChatGPT非官方API:原理、实现与避坑指南
1. 项目概述当Selenium遇上ChatGPT最近在折腾一些AI应用的原型经常需要调用ChatGPT的API来做对话测试。官方API固然稳定强大但一来需要付费二来对于某些需要模拟真实用户交互流程的测试场景比如测试一个基于Web界面的对话引导逻辑直接调用API反而少了点“真实感”。于是我把目光投向了另一种思路既然ChatGPT有一个功能完善的网页版我们能不能用自动化工具来模拟用户操作间接实现一个“API”呢这就是基于Selenium的ChatGPT非官方API项目的核心想法。简单来说这个项目就是利用Selenium WebDriver这个浏览器自动化工具完全模拟一个真实用户在ChatGPT网页端的操作流程打开浏览器、登录、找到输入框、输入问题、点击发送、等待回复、提取回复文本。然后我们把这一系列操作封装成几个简单的函数比如start_chat_gpt(),make_gpt_request()这样在其他Python脚本里就能像调用普通函数一样和ChatGPT对话了。它解决的痛点很明确为那些暂时不想或无法使用官方API比如额度用尽、组织被禁用、或者只是想快速做个概念验证的开发者、测试人员或爱好者提供一个低成本、高仿真的替代方案。当然你得清楚它的定位——它本质上是一个“网页机器人”速度、稳定性和可靠性都无法与官方API相提并论官方文档也明确说了“不推荐用于生产环境”但对于测试、学习、快速原型搭建它绝对是一把趁手的“瑞士军刀”。2. 核心原理深度拆解机器人如何“看见”并“操作”网页要理解这个非官方API怎么工作我们得先抛开“API”这个抽象概念回到最本质的问题一个程序如何像人一样使用网页版ChatGPT整个过程可以分解为“感知-决策-执行”三个核心环节而Selenium在其中扮演了“眼睛”和“手”的角色。2.1 Selenium的核心工作机制WebDriver协议Selenium本身不是一个单一体而是一个生态系统。我们常说的“用Selenium”通常指的是使用Selenium WebDriver。它的工作原理非常巧妙你的Python脚本或其他语言客户端通过一个称为“WebDriver协议”的标准化JSON Wire Protocol向一个特定的“浏览器驱动”如ChromeDriver发送HTTP请求。这个驱动就像一个翻译官和指挥官它接收你的指令如“打开某个URL”、“点击某个元素”然后通过浏览器提供的调试接口如Chrome DevTools Protocol来实际操控浏览器实例执行这些操作。同时浏览器驱动也会将浏览器的状态如页面HTML、元素属性打包成HTTP响应返回给你的脚本。这就好比你在远程操控一个机器人你的代码是控制台WebDriver协议是无线电指令ChromeDriver是机器人身上的接收器而Chrome浏览器就是机器人本体。你发送“移动右手到坐标(X,Y)”的指令机器人接收并执行。这个架构决定了基于Selenium的方案必然比直接HTTP请求慢因为每一步操作都涉及多个网络往返和浏览器渲染开销。2.2 定位与交互如何找到输入框并发送消息ChatGPT的网页界面虽然美观但对自动化脚本来说就是一堆HTML元素构成的“迷宫”。Selenium提供了多种“寻路”方法核心是定位器Locators。在这个非官方API项目中最可能用到的是XPath和CSS Selector。为什么是XPath查看ChatGPT页面的源代码以2024年常见的界面为例你会发现它的DOM结构非常动态元素ID和类名可能随着版本更新而改变或者本身就由前端框架动态生成缺乏唯一性。XPath的强大之处在于它可以通过元素的层级关系、属性包含关系甚至文本内容来进行定位灵活性极高。例如项目代码中可能通过类似//textarea[contains(placeholder, Send a message)]这样的XPath来定位主输入框。这种方式比依赖脆弱的id或class更健壮尽管编写起来稍复杂。交互流程模拟等待元素就绪网页是动态加载的直接操作可能因为元素未加载而失败。因此在定位输入框或发送按钮前必须使用“等待”。项目里如果只用time.sleep()是一种简单但低效的方式因为它固定等待不管元素是否提前就绪。更好的实践是使用Selenium的“显式等待”Explicit Wait例如WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, input_box_xpath)))。这会让脚本智能地等待最多等10秒一旦元素出现就立刻继续大大提升了效率和稳定性。输入与点击找到输入框元素假设变量名为input_elem后使用input_elem.send_keys(“你的问题”)来输入文本。这里有个细节有时页面JavaScript会监听其他事件单纯send_keys可能无法触发。更可靠的做法是先用input_elem.click()聚焦再send_keys。发送按钮同理定位后执行.click()。处理身份验证与弹窗这是项目中最棘手的部分之一。如果浏览器会话未登录打开chat.openai.com会重定向到登录页。项目提到的“自动登录”思路是依赖于用户已在该浏览器实例中登录了Google或OpenAI利用Chrome的用户数据目录User Data Dir来保持登录状态。另一种方式是脚本自动填充账号密码但这涉及安全风险明文存储凭证和可能遇到的验证码Captcha实现复杂且易失效因此大多数此类项目建议用户手动登录一次。2.3 响应获取与解析从流式输出到完整文本ChatGPT网页版的回复是“流式”输出的即一个字一个字地显示。这对自动化抓取提出了挑战。你不能在点击发送后立即去抓取回复区域的内容那样只能抓到部分回复。策略一等待特定标识出现。可以等待回复区域出现一个表示“正在输入”或“回复完成”的标识。例如等待一个“停止生成”按钮消失或者等待回复区域内的某个特定类名的元素如表示AI消息的容器的内容不再变化。这通常需要结合循环检查和超时机制。策略二监听网络请求高级。更底层的方法是监听浏览器与后端通信的WebSocket或Fetch/XHR请求直接截获原始的流式数据。但这需要更复杂的配置可能用到Selenium的execute_cdp_cmd来启用Chrome DevTools Protocol的网络域监听超出了基础项目的范畴。在基础实现中项目很可能采用一种务实但有效的方法在发送问题后等待一个足够长的时间比如time.sleep(30)假设回复已经完成然后通过XPath定位到所有AI回复的div取最后一个或特定类名的元素获取其.text属性。这种方法虽然笨拙但在网络稳定、问题不复杂时是可行的。关键在于提取到的文本需要做清洗去除可能包含的代码块标记、引用符号等无关字符以返回纯净的对话内容。注意网页结构变更是这个方案最大的风险。OpenAI前端团队的任何一次UI升级都可能导致原有的XPath定位器全部失效。这就是为什么这类项目需要持续维护也解释了为什么它“不适用于生产环境”——生产环境无法承受这种不可控的变更风险。3. 实战搭建从零构建你的ChatGPT自动化对话机器人理解了原理我们动手搭建一个。我会基于常见的实践补充一个比基础模板更健壮、更适合学习的版本。我们假设使用Python和Chrome浏览器。3.1 环境准备与依赖安装首先确保你的系统有Python 3.7。强烈建议使用虚拟环境来隔离项目依赖。# 创建并激活虚拟环境 python -m venv chatgpt_selenium_env # Windows: chatgpt_selenium_env\Scripts\activate # Linux/Mac: source chatgpt_selenium_env/bin/activate安装核心库。除了Selenium我们还会安装webdriver-manager这是一个神器它能自动下载和匹配当前Chrome浏览器版本的ChromeDriver省去手动查找和配置的麻烦。pip install selenium webdriver-manager3.2 核心脚本编写一个增强版的chatgpt_automation.py我们将创建一个脚本包含浏览器初始化、登录保持、发送消息和获取回复等核心功能。这里会融入更多最佳实践比如显式等待和错误处理。# chatgpt_automation.py import time 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 from selenium.common.exceptions import TimeoutException, NoSuchElementException from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service class ChatGPTAutomation: def __init__(self, user_data_dirNone, headlessFalse): 初始化自动化实例。 :param user_data_dir: Chrome用户数据目录路径用于保持登录状态。例如 rC:\Users\YourName\AppData\Local\Google\Chrome\User Data :param headless: 是否以无头模式运行不显示浏览器界面 self.driver None self.user_data_dir user_data_dir self.headless headless self.wait_timeout 30 # 显式等待超时时间 def start(self): 启动浏览器并打开ChatGPT chrome_options webdriver.ChromeOptions() # 关键添加参数防止WebDriver检测有些网站会屏蔽自动化工具 chrome_options.add_argument(--disable-blink-featuresAutomationControlled) chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 使用用户数据目录保持登录状态 if self.user_data_dir: chrome_options.add_argument(f--user-data-dir{self.user_data_dir}) # 通常需要指定Profile目录默认是Default chrome_options.add_argument(--profile-directoryDefault) # 无头模式选项 if self.headless: chrome_options.add_argument(--headless) chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--window-size1920,1080) # 使用webdriver-manager自动管理ChromeDriver service Service(ChromeDriverManager().install()) self.driver webdriver.Chrome(serviceservice, optionschrome_options) # 执行CDP命令进一步规避检测 self.driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); }) self.driver.get(https://chat.openai.com/) print(浏览器已启动正在加载ChatGPT...) # 等待页面基本加载完成 time.sleep(3) def is_logged_in(self): 检查当前是否已登录。通过判断页面是否存在登录按钮或对话输入框 try: # 尝试寻找登录按钮如果找到说明未登录此定位器需根据实际页面调整 login_button self.driver.find_element(By.XPATH, //button[contains(text(), Log in)]) return False except NoSuchElementException: # 未找到登录按钮尝试寻找输入框找到则说明已登录 try: # ChatGPT主输入框的XPath这是一个示例可能需要更新 input_box WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.XPATH, //textarea[idprompt-textarea])) ) return True except TimeoutException: # 既没有登录按钮也没有输入框可能是页面未加载完全或其他状态 return False def send_message(self, prompt, wait_for_responseTrue): 向ChatGPT发送一条消息并获取回复。 :param prompt: 要发送的文本 :param wait_for_response: 是否等待并返回回复 :return: 如果wait_for_response为True返回AI的回复文本否则返回None if not self.driver: raise RuntimeError(浏览器未启动请先调用 start() 方法。) # 定位输入框 - 这里的XPath是关键且极易失效 # 备用方案可以尝试其他属性如placeholder、data-id等 input_box_xpaths [ //textarea[idprompt-textarea], //textarea[contains(placeholder, Message)], //div[roletextbox and contenteditabletrue] ] input_box None for xpath in input_box_xpaths: try: input_box WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) break except TimeoutException: continue if not input_box: raise NoSuchElementException(无法找到聊天输入框页面结构可能已变更。) # 清空输入框如果有旧内容并输入新文本 input_box.clear() input_box.send_keys(prompt) # 定位并点击发送按钮 # 发送按钮的定位同样需要适配页面变化 send_button_xpaths [ //button[data-testidsend-button], //button[contains(class, send)], //button[.//svg]//ancestor::button # 有时按钮里面只有一个svg图标 ] send_button None for xpath in send_button_xpaths: try: send_button self.driver.find_element(By.XPATH, xpath) if send_button.is_enabled(): break else: send_button None except NoSuchElementException: continue if send_button: send_button.click() else: # 如果找不到按钮尝试用回车键发送某些界面支持 input_box.send_keys(Keys.RETURN) print(未找到发送按钮已尝试使用回车键发送。) if not wait_for_response: return None # 等待并获取回复 return self._wait_and_get_response() def _wait_and_get_response(self): 等待AI回复完成并提取文本 print(等待AI回复...) # 策略等待代表“AI正在思考”的指示器消失并等待完整的回复div出现 # 1. 先等待“停止生成”按钮出现表示开始生成 try: stop_button WebDriverWait(self.driver, self.wait_timeout).until( EC.presence_of_element_located((By.XPATH, //button[contains(aria-label, Stop generating) or contains(text(), Stop)])) ) except TimeoutException: print(未检测到‘正在生成’状态可能回复很快或页面结构不同。) # 2. 等待“停止生成”按钮消失表示生成结束 try: WebDriverWait(self.driver, 60).until( # 给回复最多60秒时间 EC.invisibility_of_element_located((By.XPATH, //button[contains(aria-label, Stop generating) or contains(text(), Stop)])) ) except TimeoutException: print(警告等待回复超时可能仍在生成或网络问题。) # 3. 定位最新的AI回复消息 # 寻找所有AI消息的容器通常有特定的类名或属性这里是一个通用性较强的尝试 ai_message_xpaths [ //div[contains(class, group)][.//div[contains(text(), ChatGPT) or contains(text(), Assistant)]]//div[classmarkdown], //div[data-message-author-roleassistant]//div[contains(class, message)], //div[starts-with(class, result-streaming)]/following-sibling::div # 针对流式结束后的最终块 ] for xpath in ai_message_xpaths: try: # 获取所有AI回复取最后一个最新的 ai_messages self.driver.find_elements(By.XPATH, xpath) if ai_messages: latest_message ai_messages[-1] # 滚动到该元素确保内容完全加载 self.driver.execute_script(arguments[0].scrollIntoView(true);, latest_message) time.sleep(0.5) # 短暂等待滚动和渲染 full_text latest_message.text if full_text: return full_text except NoSuchElementException: continue # 如果上述方法都失败尝试一个更暴力的方法获取整个对话容器的最后一大块文本 try: conversation self.driver.find_element(By.TAG_NAME, main) or self.driver.find_element(By.XPATH, //div[rolepresentation]) all_text conversation.text # 简单分割取最后一段风险可能包含用户问题 paragraphs [p for p in all_text.split(\n) if p.strip()] if len(paragraphs) 1: return paragraphs[-1] except Exception as e: print(f提取回复文本时发生意外错误: {e}) return [未能成功提取回复文本请检查页面结构或网络状态。] def close(self): 关闭浏览器 if self.driver: self.driver.quit() print(浏览器已关闭。) # 使用示例 if __name__ __main__: bot ChatGPTAutomation( # user_data_dirr你的Chrome用户数据路径, # 取消注释此项以保持登录 headlessFalse # 调试时设为False可以看到浏览器操作 ) try: bot.start() # 检查登录状态 if bot.is_logged_in(): print(检测到已登录状态。) else: print(未登录。请手动在打开的浏览器窗口中登录ChatGPT登录完成后按回车继续...) input() # 等待用户手动登录 # 进行对话 response bot.send_message(用Python写一个快速排序函数并加上注释。) print(fAI回复:\n{response}) # 可以继续对话 response2 bot.send_message(解释一下时间复杂度。) print(fAI第二次回复:\n{response2}) except Exception as e: print(f运行过程中出现错误: {e}) finally: # 确保浏览器被关闭 bot.close()这个脚本实现了一个相对健壮的类。它使用了webdriver-manager自动处理驱动加入了规避自动化检测的选项提供了多种备选的XPath定位策略以增加容错并实现了等待生成完成的逻辑。user_data_dir参数是关键通过指定你日常使用的Chrome用户数据目录可以让Selenium打开的浏览器携带你的所有Cookie和登录状态从而实现“一次登录长期使用”。3.3 封装为简易APIapi_backend.py的增强思路参考开源项目的结构我们可以将上面的自动化类进一步封装提供更简洁的API式调用。核心是管理好浏览器的生命周期单例模式思想避免重复启动关闭。# api_backend.py from chatgpt_automation import ChatGPTAutomation import threading import time # 全局变量用于管理唯一的浏览器实例 _chatgpt_bot None _bot_lock threading.Lock() def start_chat_gpt(user_data_dirNone, headlessTrue): 启动ChatGPT自动化会话全局单例。 建议在程序开始时调用一次。 global _chatgpt_bot with _bot_lock: if _chatgpt_bot is None: _chatgpt_bot ChatGPTAutomation(user_data_diruser_data_dir, headlessheadless) _chatgpt_bot.start() # 等待并检查登录状态 time.sleep(5) if not _chatgpt_bot.is_logged_in(): print(警告未检测到登录状态。请确保已通过user_data_dir参数提供了已登录的浏览器数据或稍后手动处理。) return True else: print(ChatGPT会话已启动。) return False def make_gpt_request(prompt, max_retries2): 向ChatGPT发送请求并获取回复。 :param prompt: 提示词 :param max_retries: 失败重试次数 :return: 回复字符串失败时返回错误信息 global _chatgpt_bot if _chatgpt_bot is None: return 错误请先调用 start_chat_gpt() 初始化会话。 for attempt in range(max_retries): try: response _chatgpt_bot.send_message(prompt) return response except Exception as e: print(f第 {attempt 1} 次请求失败: {e}) if attempt max_retries - 1: print(等待3秒后重试...) time.sleep(3) # 可以尝试刷新页面等恢复操作 try: _chatgpt_bot.driver.refresh() time.sleep(5) except: pass else: return f请求失败最终错误: {e} def stop_chat_gpt(): 关闭ChatGPT自动化会话 global _chatgpt_bot with _bot_lock: if _chatgpt_bot is not None: _chatgpt_bot.close() _chatgpt_bot None print(ChatGPT会话已停止。) return True return False # 测试函数 if __name__ __main__: # 示例使用用户数据目录请替换为你的实际路径 # start_chat_gpt(user_data_dirrC:\Users\YourName\AppData\Local\Google\Chrome\User Data) # 示例不使用用户数据目录需要手动登录 start_chat_gpt(headlessFalse) # 等待用户手动登录如果未使用user_data_dir input(请在浏览器中完成登录然后按回车键继续...) # 测试请求 answer make_gpt_request(你好请介绍一下你自己。) print(回复:, answer) # 可以连续对话 answer2 make_gpt_request(刚才的回复是用什么语言写的) print(第二次回复:, answer2) stop_chat_gpt()这样在其他脚本中你就可以像调用一个简易库一样使用它了import api_backend api_backend.start_chat_gpt(headlessTrue) # 无头模式启动 response api_backend.make_gpt_request(什么是机器学习) print(response) # ... 进行更多对话 api_backend.stop_chat_gpt()4. 避坑指南与实战经验那些官方文档不会告诉你的细节在实际使用和开发这类Selenium自动化方案时你会遇到无数坑。下面是我从多次实践中总结出的核心经验和解决方案。4.1 稳定性与反自动化对抗ChatGPT网页端和其他现代Web应用一样或多或少会有一些检测自动化脚本的机制。直接使用默认的Selenium驱动很容易被识别导致操作失败或被限制。解决方案添加规避检测的选项如前面代码所示--disable-blink-featuresAutomationControlled和相关的excludeSwitches选项至关重要。修改WebDriver属性通过CDP命令execute_cdp_cmd覆盖navigator.webdriver属性为undefined这是最有效的办法之一。模拟人类行为在关键操作之间加入随机延迟如time.sleep(random.uniform(0.5, 2.0))避免操作过于规律和迅速。移动鼠标轨迹虽然Selenium不直接支持但可通过ActionChains模拟一些无用移动也能增加真实性。使用真实的用户数据目录这不仅是为了保持登录状态其携带的浏览器指纹字体、插件、屏幕分辨率等也更像一个真实用户环境。4.2 元素定位失效永恒的挑战这是基于UI自动化方案最脆弱的环节。OpenAI前端的一个小更新就可能让你的脚本瘫痪。应对策略使用相对稳定且唯一的属性优先寻找元素的>chrome_options.add_argument(--blink-settingsimagesEnabledfalse) chrome_options.add_argument(--disable-extensions) prefs {profile.managed_default_content_settings.images: 2} chrome_options.add_experimental_option(prefs, prefs)4.5 错误处理与日志记录一个健壮的系统必须能妥善处理各种异常并留下清晰的日志供排查。关键错误类型及处理NoSuchElementException/TimeoutException定位器失效或页面未加载。应触发重试机制并记录下当前页面的HTML快照driver.page_source和截图driver.save_screenshot(error.png)这对于事后调试定位器至关重要。ElementNotInteractableException元素存在但不可交互如被遮挡、未显示。可以尝试用JavaScript直接点击driver.execute_script(arguments[0].click();, element)。WebDriverException(如 session deleted)浏览器进程意外崩溃。需要在代码最外层捕获此类异常并实现整个浏览器实例的重建流程。网络错误与429 Too Many Requests过于频繁的请求可能导致限流。需要在请求间加入更长的、带有随机性的间隔并监控此类响应。建议的日志记录使用Python的logging模块为不同操作启动、发送、接收、错误设置不同级别INFO, WARNING, ERROR的日志。记录时间戳、操作类型、关键元素状态和遇到的异常。这将是后期维护和问题定位的生命线。5. 进阶应用场景与替代方案探讨虽然这个方案被标记为“仅用于测试”但在一些特定场景下它依然有其独特的价值。5.1 适合的应用场景UI/UX流程测试如果你在开发一个集成AI对话功能的产品需要测试从用户输入到AI回复显示的完整前端交互流程模拟真实用户操作的Selenium方案比直接调用后端API更贴近真实场景。官方API不可用时的应急方案当遇到“This organization has been disabled”、“insufficient balance”或“models maximum context length”等API错误时网页版可能依然可用。此方案可作为临时的替代通道。获取网页版特有功能某些功能如使用特定的插件、上传文件进行分析、访问特定的GPT版本可能暂时未在官方API中开放通过自动化网页可以间接调用这些功能。教育与研究用于理解大型语言模型的交互协议、研究网页端的反爬机制、或作为学习Selenium自动化技术的综合案例。5.2 性能与稳定性更强的替代方案如果你需要更可靠、更快的“非官方”接入方式可以考虑以下方向逆向工程官方接口通过浏览器开发者工具仔细分析ChatGPT网页版与后端通信的HTTP请求特别是WebSocket。直接模拟这些请求可以绕过UI渲染获得接近官方API的速度和稳定性。但这涉及对认证令牌如accessToken、请求签名等机制的破解技术门槛高且一旦OpenAI更改通信协议就会失效法律和合规风险也更高。使用PlaywrightPlaywright是微软推出的新一代浏览器自动化库相比Selenium它在速度、稳定性、API设计以及规避检测方面通常表现更好。它的locatorAPI 更强大自动等待机制也更智能。将本项目迁移到Playwright是一个不错的升级选择。社区维护的反向工程库GitHub上存在一些通过逆向工程实现的、不依赖浏览器自动化的第三方ChatGPT客户端库例如某些以“ChatGPT”命名的Python库。它们直接模拟了登录和对话的HTTP请求。这些库通常比Selenium方案快得多但同样面临因官方更新而失效的风险并且需要妥善处理账号安全。5.3 长期维护的思考如果你决定长期使用或改进一个这样的项目需要建立一套维护机制监控与告警编写一个简单的定时任务每隔一段时间发送一个测试性问题如“回复‘ping’”。如果连续失败则通过邮件、钉钉、Telegram Bot等方式发送告警。定位器自动更新探索可以尝试编写一个辅助脚本当主脚本检测到定位失败时自动打开一个调试浏览器非无头模式提示用户手动点击目标元素然后脚本记录下该元素在当前页面结构下的新XPath或Selector并更新到配置文件中。版本锁定与回归测试如果ChatGPT的某个旧版网页UI对你来说足够稳定可以尝试通过浏览器插件或修改Hosts文件等方式强制浏览器访问某个旧版本CDN地址这非常困难且不推荐。更务实的做法是接受变化并提高自己脚本的适应能力。最后必须再次强调合规与道德。使用此类自动化工具应严格遵守OpenAI的使用条款。不要用于制造垃圾信息、进行欺诈、绕过访问限制或任何其他滥用行为。尊重服务提供者的规则将工具用于学习、测试和合法的自动化需求这才是技术爱好者的正确打开方式。