0


如何使用Python WebDriver爬取ChatGPT内容(完整教程)

大背景

虽然我们能用网页版chatGPT来聊天、写文章,但是我们采集大量的内容,就得不断地手动输入提问来获取答案,并且将结果复制到数据库来保存。如果整个过程能使用程序来做自然要节省很多的人力,精力和时间。

Python webdirver 模拟浏览器的方式来实现,刚好能实现以上功能。

另外之所以不选择API 是因为以下原因:

  • 普通开发者(国内)获取API KEY 是有困难的,需要海外手机号 + 信用卡等一系列条件,但是如果只是网页端,我们仅仅需要登录或者未登录的方式就可以直接聊天
  • 网页端无需调整各项参数,可以直接交互获取内容,而且内容质量更高!

当然,如果你有条件用API 或者直接通过 wss交互获取内容的,到这儿可以直接结束了。

完整源码在文章末尾哦!

基本环境要求

准备自己的梯子

ChatGpt属于海外项目,国内的小伙伴,翻墙得找好自己的梯子(代理),这里自己有啥用啥即可。

安装Python 环境

Python要求:3.10 +

安装 Python Selenium

  1. pip install selenium

其他扩展库说明:取决于自己电脑缺什么就安装什么。后面完整代码会提供完整的 requirements.txt

浏览器

这里以Chrome浏览器为准。也就是你本地必须要安装Chrome浏览器,并且获取其安装路径。

比如:C:\Program Files\Google\Chrome\Application\chrome.exe

操作系统

这里以 windows 作为开发环境。

开发工具

自己什么顺手用什么。

这里讲个程序界的笑话:传说级别的开发者据说用的记事本来开发。

实现过程

这里只介绍主要的代码

浏览器控制

使用程序实现浏览器的控制,这包括浏览器的打开,关闭,以及代理配置。

创建 browser_manage.py

  1. import subprocess, datetime
  2. import os, signal, psutil
  3. def run_cmd(port=9200):
  4. cmd = [
  5. # chrome浏览器路径。 必须为首个参数
  6. 'C:/Program Files/Google/Chrome/Application/chrome.exe',
  7. # 【必要】设置浏览器端口
  8. '--remote-debugging-port=%s' % port,
  9. # 【必要】设置浏览器数据存储路径
  10. '--user-data-dir=D:/data',
  11. # 隐藏一些弹窗之类的信息
  12. '--hide-crash-restore-bubble',
  13. # 设置浏览器分辨率。如果要跑多个浏览器可以将每个浏览器设置小一些
  14. '--force-device-scale-factor=1',
  15. # 假设代理地址为 http://127.0.0.1:10809
  16. '--proxy-server=http://127.0.0.1:10809',
  17. # 默认打开一个空白页面
  18. 'about:blank'
  19. ]
  20. process = subprocess.Popen(cmd)
  21. # 返回pid 用于关闭浏览器杀死进程
  22. return process.pid
  23. def kill_process(parent_pid):
  24. try:
  25. # 获取父进程
  26. parent = psutil.Process(parent_pid)
  27. # 获取父进程的所有子进程(包括孙子进程等)
  28. children = parent.children(recursive=True)
  29. # 创建一个包含父进程PID的列表
  30. pids_to_kill = [parent_pid]
  31. # 将所有子进程的PID添加到列表中
  32. pids_to_kill.extend(child.pid for child in children)
  33. # 遍历列表,对每个PID发送SIGKILL信号
  34. for pid in pids_to_kill:
  35. try:
  36. os.kill(pid, signal.SIGILL)
  37. except PermissionError:
  38. # 忽略权限错误,可能我们没有权限杀死某个进程
  39. print("close browser PermissionError")
  40. pass
  41. except ProcessLookupError:
  42. # 忽略进程查找错误,进程可能已经自然死亡
  43. print("close browser ProcessLookupError")
  44. pass
  45. except (psutil.NoSuchProcess, PermissionError):
  46. # 忽略错误,如果进程不存在或者没有权限
  47. print("close browser PermissionError1")
  48. pass
  49. return True
  50. def open_browser():
  51. """
  52. 打开浏览器
  53. """
  54. # 打开指定端口的浏览器
  55. pid = run_cmd(9200)
  56. def close_borwser():
  57. """
  58. 关闭浏览器
  59. """
  60. # pid 为打开浏览器获取到的进程id
  61. kill_process(pid)
  62. # 执行open_browser() 打开浏览器,执行 close_borwser() 关闭浏览器

初始化selenium

创建爬虫脚本 spider.py

  1. from selenium import webdriver
  2. from selenium.webdriver.common.by import By
  3. from selenium.webdriver.chrome.options import Options
  4. from selenium.webdriver.chrome.service import Service
  5. from fake_useragent import UserAgent
  6. import subprocess, datetime
  7. chrome_options = Options()
  8. ua = UserAgent()
  9. # 浏览器端口信息,取决于启动浏览器设置的端口
  10. browser_host = 9200
  11. browser_host= "127.0.0.1"
  12. random_ua = ua.random
  13. chrome_options.add_argument(f'user-agent={random_ua}')
  14. # 设置要连接的浏览器端口信息
  15. chrome_options.add_experimental_option("debuggerAddress","%s:%s" % (browser_host, browser_port))
  16. driver = webdriver.Chrome(options=chrome_options)

进入到目标页面

文件:spider.py

  1. # 页面加载等待:最多10s
  2. driver.implicitly_wait(10)
  3. driver.get("https://chat.openai.com/")

页面等待除了以上的方案也可以用其他方法:

  1. # 等待某个元素可见
  2. try:
  3. element = WebDriverWait(driver, 10).until(
  4. EC.visibility_of_element_located((By.XPATH, "//h1"))
  5. )
  6. print(element.text)
  7. finally:
  8. driver.quit()
  9. # 或者直接
  10. time.sleep(10)

发起询问

发起新的提问

每次提问都应该是基于一个新窗口来提问。如果你的问题需要上下文的基础来回答,可以直接跳过这里。

新询问输入框可以根据 **button data-testid="create-new-chat-button" **来定位。

  1. # 定位到新聊天元素
  2. new_chat_dom = driver.find_element(By.CSS_SELECTOR, '[data-testid="create-new-chat-button"]')
  3. # 发起点击,进入新界面
  4. new_chat_dom.click()

定位到输入框

输入框使用的是div contentediable作为文本域。其id为 prompt-textarea

  1. # 因为元素提供了id,则直接通过id获取最方便
  2. textarea_dom = driver.find_element(By.ID, "prompt-textarea")

创建询问队列

因为我们的目标是自动化对问题列表发起询问,而不是一次性询问。所以需要创建一个问题队列。问题队列可以来源于数据库或者队列文件。这里为了演示,直接创建一个list

  1. ask_list = [
  2. "请用Python写一个平均等分list的方案",
  3. "请写一个关于小猪佩奇的笑话,要求:小猪佩奇可能不是猪,而是河马, 100字",
  4. "称赞一个女生长得漂亮,如何不直接称赞也能看出来在形容她漂亮"
  5. ]

输入问题

这里我们按行来输入:一次输入一行。

  1. for msg in ask_list:
  2. # 将字符串按换行分割开
  3. ask_msg_arr = msg.split('\n')
  4. for msg_line in ask_msg_arr:
  5. textarea_dom.send_keys(ask_msg_item)
  6. # 发送
  7. textarea_dom.send_keys(Keys.ENTER)
  8. # TODO 这里是后续获取数据,存储到数据库环节

等待数据响应

可以根据回答结束后,出现的交互按钮来确认是否回答完毕

  1. idx = 0
  2. while True:
  3. idx = idx + 1
  4. # 请求超时
  5. if idx > 180:
  6. break
  7. time.sleep(1)
  8. try:
  9. driver.find_element(By.CSS_SELECTOR, '[data-testid="bad-response-turn-action-button"]')
  10. break
  11. except:
  12. continue

也可以使用以下方法来校验:

  1. # 设置最大等待时间
  2. wait = WebDriverWait(driver, 180)
  3. # 等待直到元素出现
  4. element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="bad-response-turn-action-button"]')))

获取响应数据

通过 class="markdown" 来获取数据。

  1. idx = 0
  2. while True:
  3. idx = idx + 1
  4. # 请求超时
  5. if idx > 180:
  6. break
  7. time.sleep(1)
  8. try:
  9. html_dom = driver.find_element(By.CSS_SELECTOR, '.markdown')
  10. break
  11. except:
  12. continue
  13. content = html_dom .get_attribute('innerHTML')

数据清洗

具体的清洗规则,看具体的业务需求。大多数清洗,需要将一些总结类的语句删除,不合法的返回内容删除。这里不提供相应的清洗的方案。

至此,一个完整的数据链就已经完结。

完整源码

browser.py

  1. import subprocess, datetime
  2. import os, signal, psutil
  3. def run_cmd(port=9200):
  4. cmd = [
  5. # chrome浏览器路径。
  6. 'C:/Program Files/Google/Chrome/Application/chrome.exe',
  7. # 【必要】设置浏览器端口
  8. '--remote-debugging-port=%s' % port,
  9. # 【必要】设置浏览器数据存储路径
  10. '--user-data-dir=E:/browser_data',
  11. # 隐藏一些弹窗之类的信息
  12. '--hide-crash-restore-bubble',
  13. # 设置浏览器分辨率。如果要跑多个浏览器可以将每个浏览器设置小一些
  14. '--force-device-scale-factor=1',
  15. # 假设代理地址为 http://127.0.0.1:10809
  16. '--proxy-server=http://127.0.0.1:10809',
  17. # 默认打开一个空白页面
  18. 'about:blank'
  19. ]
  20. process = subprocess.Popen(cmd)
  21. # 返回pid 用于关闭浏览器杀死进程
  22. return process.pid
  23. def kill_process(parent_pid):
  24. try:
  25. # 获取父进程
  26. parent = psutil.Process(parent_pid)
  27. # 获取父进程的所有子进程(包括孙子进程等)
  28. children = parent.children(recursive=True)
  29. # 创建一个包含父进程PID的列表
  30. pids_to_kill = [parent_pid]
  31. # 将所有子进程的PID添加到列表中
  32. pids_to_kill.extend(child.pid for child in children)
  33. # 遍历列表,对每个PID发送SIGKILL信号
  34. for pid in pids_to_kill:
  35. try:
  36. os.kill(pid, signal.SIGILL)
  37. except PermissionError:
  38. # 忽略权限错误,可能我们没有权限杀死某个进程
  39. print("close browser PermissionError")
  40. pass
  41. except ProcessLookupError:
  42. # 忽略进程查找错误,进程可能已经自然死亡
  43. print("close browser ProcessLookupError")
  44. pass
  45. except (psutil.NoSuchProcess, PermissionError):
  46. # 忽略错误,如果进程不存在或者没有权限
  47. print("close browser PermissionError1")
  48. pass
  49. return True
  50. def open_browser():
  51. """
  52. 打开浏览器
  53. """
  54. # 打开指定端口的浏览器
  55. pid = run_cmd(9200)
  56. return pid
  57. def close_browser(pid):
  58. """
  59. 关闭浏览器
  60. """
  61. # pid 为打开浏览器获取到的进程id
  62. kill_process(pid)
  63. pid = open_browser()
  64. print(pid)
  65. # close_browser(25996)

spider.py

  1. import time
  2. import traceback
  3. from selenium import webdriver
  4. from selenium.webdriver.common.by import By
  5. from selenium.webdriver.chrome.options import Options
  6. from selenium.webdriver.chrome.service import Service
  7. from selenium.webdriver.common.keys import Keys
  8. from fake_useragent import UserAgent
  9. import subprocess, datetime
  10. import os
  11. chrome_options = Options()
  12. ua = UserAgent()
  13. # 浏览器端口信息,取决于启动浏览器设置的端口
  14. browser_port = 9200
  15. browser_host = "127.0.0.1"
  16. random_ua = ua.random
  17. chrome_options.add_argument(f'user-agent={random_ua}')
  18. # 设置要连接的浏览器端口信息
  19. chrome_options.add_experimental_option("debuggerAddress", "%s:%s" % (browser_host, browser_port))
  20. driver = webdriver.Chrome(options=chrome_options)
  21. def open_gpt_page():
  22. driver.implicitly_wait(10)
  23. driver.get("https://chat.openai.com/")
  24. def new_chat():
  25. """
  26. 该方案当前只适用于已经登录到chatgpt的页面。不适用非的登录页面
  27. """
  28. # 定位到新聊天元素
  29. new_chat_dom = driver.find_element(By.CSS_SELECTOR, '[data-testid="create-new-chat-button"]')
  30. # 发起点击,进入新界面
  31. new_chat_dom.click()
  32. def get_input_box():
  33. return driver.find_element(By.ID, "prompt-textarea")
  34. def get_ask_list():
  35. return [
  36. "请用Python写一个平均等分list的方案",
  37. "请写一个关于小猪佩奇的笑话,要求:小猪佩奇可能不是猪,而是河马, 100字",
  38. "称赞一个女生长得漂亮,如何不直接称赞也能看出来在形容她漂亮"
  39. ]
  40. def get_response():
  41. # 等待响应完成
  42. idx = 0
  43. while True:
  44. idx = idx + 1
  45. # 请求超时
  46. if idx > 180:
  47. break
  48. time.sleep(1)
  49. try:
  50. driver.find_element(By.CSS_SELECTOR, '[data-testid="bad-response-turn-action-button"]')
  51. break
  52. except:
  53. continue
  54. # 获取请求结果
  55. idx = 0
  56. content = None
  57. while True:
  58. idx = idx + 1
  59. # 请求超时
  60. if idx > 180:
  61. break
  62. time.sleep(1)
  63. try:
  64. html_dom = driver.find_element(By.CSS_SELECTOR, '.markdown')
  65. content = html_dom.get_attribute('innerHTML')
  66. break
  67. except:
  68. continue
  69. return content
  70. def simulator(ask_msg):
  71. # 进入输入框并且点击
  72. print("进入输入框并且点击")
  73. textarea_dom = get_input_box()
  74. textarea_dom.click()
  75. # 发送
  76. ask_msg_arr = ask_msg.split('\n')
  77. for msg_line in ask_msg_arr:
  78. textarea_dom.send_keys(msg_line)
  79. textarea_dom.send_keys(Keys.ENTER)
  80. # 获取响应
  81. content = get_response()
  82. # 保存数据到文件
  83. root_path = "./data"
  84. os.makedirs(root_path, exist_ok=True)
  85. full_path = f"{root_path}/%s.text" % int(time.time())
  86. with open(full_path, 'w', encoding='utf-8') as file:
  87. file.write(content)
  88. print("save success")
  89. def start():
  90. open_gpt_page()
  91. print("页面加载完毕")
  92. time.sleep(3)
  93. ask_list = get_ask_list()
  94. for ask_msg in ask_list:
  95. time.sleep(3)
  96. print("进入新的聊天")
  97. new_chat()
  98. print("current msg %s" % ask_msg)
  99. for retry in range(0,3):
  100. try:
  101. simulator(ask_msg)
  102. break
  103. except:
  104. print(traceback.format_exc())
  105. time.sleep(10)
  106. pass
  107. start()

requirements.txt

  1. fake_useragent==1.2.1
  2. psutil==5.9.5
  3. selenium==4.26.1

获取扩展源码

扩展源码是基于 chatgpt聊天页面,通过多个浏览器并行数据爬取,包含以下功能:

  1. 基于数据库创建问题队列
  2. 创建多个浏览器窗口多线程并行运行,提高产量和效率
  3. 解决人工校验-自动化进行人工校验
  4. 为多个浏览器配置不同的代理方案
  5. 代理配置,为浏览器自动化分配代理,
  6. 通过web端进行浏览器管理,代理管理,代理分配等

需要以上扩展源码,QQ:1186969412


本文转载自: https://blog.csdn.net/youmypig/article/details/143464003
版权归原作者 youmypig 所有, 如有侵权,请联系我们删除。

“如何使用Python WebDriver爬取ChatGPT内容(完整教程)”的评论:

还没有评论