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

大背景

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

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

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

  • 普通开发者(国内)获取API KEY 是有困难的,需要海外手机号 + 信用卡等一系列条件,但是如果只是网页端,我们仅仅需要登录或者未登录的方式就可以直接聊天

  • 网页端无需调整各项参数,可以直接交互获取内容,而且内容质量更高!

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

完整源码在文章末尾哦!

基本环境要求

准备自己的梯子

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

安装Python 环境

Python要求:3.10 +

安装 Python Selenium

pip install selenium

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

浏览器

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

比如:C:Program FilesGoogleChromeApplicationchrome.exe

操作系统

这里以 windows 作为开发环境。

开发工具

自己什么顺手用什么。

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

实现过程

这里只介绍主要的代码

浏览器控制

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

创建 browser_manage.py

import subprocess, datetime
import os, signal, psutil

def run_cmd(port=9200):
    cmd = [
        # chrome浏览器路径。 必须为首个参数
        'C:/Program Files/Google/Chrome/Application/chrome.exe',
        # 【必要】设置浏览器端口
        '--remote-debugging-port=%s' % port,
        # 【必要】设置浏览器数据存储路径
        '--user-data-dir=D:/data',
        # 隐藏一些弹窗之类的信息
        '--hide-crash-restore-bubble',
        # 设置浏览器分辨率。如果要跑多个浏览器可以将每个浏览器设置小一些
        '--force-device-scale-factor=1',
        # 假设代理地址为 http://127.0.0.1:10809
        '--proxy-server=http://127.0.0.1:10809',
        # 默认打开一个空白页面
        'about:blank'
    ]
    process = subprocess.Popen(cmd)
    # 返回pid 用于关闭浏览器杀死进程
    return process.pid

def kill_process(parent_pid):
    try:
        # 获取父进程
        parent = psutil.Process(parent_pid)
        # 获取父进程的所有子进程(包括孙子进程等)
        children = parent.children(recursive=True)
        # 创建一个包含父进程PID的列表
        pids_to_kill = [parent_pid]
        # 将所有子进程的PID添加到列表中
        pids_to_kill.extend(child.pid for child in children)
        # 遍历列表,对每个PID发送SIGKILL信号
        for pid in pids_to_kill:
            try:
                os.kill(pid, signal.SIGILL)
            except PermissionError:
                # 忽略权限错误,可能我们没有权限杀死某个进程
                print("close browser PermissionError")
                pass
            except ProcessLookupError:
                # 忽略进程查找错误,进程可能已经自然死亡
                print("close browser ProcessLookupError")
                pass
    except (psutil.NoSuchProcess, PermissionError):
        # 忽略错误,如果进程不存在或者没有权限
        print("close browser PermissionError1")
        pass
    return True
    

def open_browser():
    """
    打开浏览器
    """
    # 打开指定端口的浏览器
    pid = run_cmd(9200)

def close_borwser():
    """
    关闭浏览器
    """
    # pid 为打开浏览器获取到的进程id
    kill_process(pid)

# 执行open_browser() 打开浏览器,执行 close_borwser() 关闭浏览器
初始化selenium

创建爬虫脚本 spider.py

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from fake_useragent import UserAgent
import subprocess, datetime

chrome_options = Options()
ua = UserAgent()
# 浏览器端口信息,取决于启动浏览器设置的端口
browser_host = 9200
browser_host= "127.0.0.1"

random_ua = ua.random
chrome_options.add_argument(f'user-agent={random_ua}')

# 设置要连接的浏览器端口信息
chrome_options.add_experimental_option("debuggerAddress","%s:%s" % (browser_host, browser_port))
driver = webdriver.Chrome(options=chrome_options)
进入到目标页面

文件:spider.py

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

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

# 等待某个元素可见
try:
    element = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.XPATH, "//h1"))
    )
    print(element.text)
finally:
    driver.quit()
    
# 或者直接
time.sleep(10)
发起询问

发起新的提问

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

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

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

定位到输入框

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

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

创建询问队列

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

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

输入问题

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

for msg in ask_list:
    # 将字符串按换行分割开
    ask_msg_arr = msg.split('
')
    for msg_line in ask_msg_arr:
        textarea_dom.send_keys(ask_msg_item)
    # 发送
    textarea_dom.send_keys(Keys.ENTER)
    # TODO 这里是后续获取数据,存储到数据库环节
等待数据响应

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

idx = 0
while True:
    idx = idx + 1
    # 请求超时
    if idx > 180:
        break
    time.sleep(1)
    try:
        driver.find_element(By.CSS_SELECTOR, '[data-testid="bad-response-turn-action-button"]') 
        break
    except:
        continue

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

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

通过 class=“markdown” 来获取数据。

idx = 0
while True:
    idx = idx + 1
    # 请求超时
    if idx > 180:
        break
    time.sleep(1)
    try:
        html_dom = driver.find_element(By.CSS_SELECTOR, '.markdown') 
        break
    except:
        continue

content = html_dom .get_attribute('innerHTML')
数据清洗

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

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

完整源码

browser.py

import subprocess, datetime
import os, signal, psutil


def run_cmd(port=9200):
    cmd = [
        # chrome浏览器路径。
        'C:/Program Files/Google/Chrome/Application/chrome.exe',
        # 【必要】设置浏览器端口
        '--remote-debugging-port=%s' % port,
        # 【必要】设置浏览器数据存储路径
        '--user-data-dir=E:/browser_data',
        # 隐藏一些弹窗之类的信息
        '--hide-crash-restore-bubble',
        # 设置浏览器分辨率。如果要跑多个浏览器可以将每个浏览器设置小一些
        '--force-device-scale-factor=1',
        # 假设代理地址为 http://127.0.0.1:10809
        '--proxy-server=http://127.0.0.1:10809',
        # 默认打开一个空白页面
        'about:blank'
    ]
    process = subprocess.Popen(cmd)
    # 返回pid 用于关闭浏览器杀死进程
    return process.pid


def kill_process(parent_pid):
    try:
        # 获取父进程
        parent = psutil.Process(parent_pid)
        # 获取父进程的所有子进程(包括孙子进程等)
        children = parent.children(recursive=True)
        # 创建一个包含父进程PID的列表
        pids_to_kill = [parent_pid]
        # 将所有子进程的PID添加到列表中
        pids_to_kill.extend(child.pid for child in children)
        # 遍历列表,对每个PID发送SIGKILL信号
        for pid in pids_to_kill:
            try:
                os.kill(pid, signal.SIGILL)
            except PermissionError:
                # 忽略权限错误,可能我们没有权限杀死某个进程
                print("close browser PermissionError")
                pass
            except ProcessLookupError:
                # 忽略进程查找错误,进程可能已经自然死亡
                print("close browser ProcessLookupError")
                pass
    except (psutil.NoSuchProcess, PermissionError):
        # 忽略错误,如果进程不存在或者没有权限
        print("close browser PermissionError1")
        pass
    return True


def open_browser():
    """
    打开浏览器
    """
    # 打开指定端口的浏览器
    pid = run_cmd(9200)
    return pid


def close_browser(pid):
    """
    关闭浏览器
    """
    # pid 为打开浏览器获取到的进程id
    kill_process(pid)


pid = open_browser()
print(pid)

# close_browser(25996)

spider.py

import time
import traceback

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.keys import Keys
from fake_useragent import UserAgent
import subprocess, datetime
import os

chrome_options = Options()
ua = UserAgent()
# 浏览器端口信息,取决于启动浏览器设置的端口
browser_port = 9200
browser_host = "127.0.0.1"

random_ua = ua.random
chrome_options.add_argument(f'user-agent={random_ua}')

# 设置要连接的浏览器端口信息
chrome_options.add_experimental_option("debuggerAddress", "%s:%s" % (browser_host, browser_port))
driver = webdriver.Chrome(options=chrome_options)


def open_gpt_page():
    driver.implicitly_wait(10)
    driver.get("https://chat.openai.com/")


def new_chat():
    """
    该方案当前只适用于已经登录到chatgpt的页面。不适用非的登录页面
    """
    # 定位到新聊天元素
    new_chat_dom = driver.find_element(By.CSS_SELECTOR, '[data-testid="create-new-chat-button"]')
    # 发起点击,进入新界面
    new_chat_dom.click()


def get_input_box():
    return driver.find_element(By.ID, "prompt-textarea")


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


def get_response():
    # 等待响应完成
    idx = 0
    while True:
        idx = idx + 1
        # 请求超时
        if idx > 180:
            break
        time.sleep(1)
        try:
            driver.find_element(By.CSS_SELECTOR, '[data-testid="bad-response-turn-action-button"]')
            break
        except:
            continue

    # 获取请求结果
    idx = 0
    content = None
    while True:
        idx = idx + 1
        # 请求超时
        if idx > 180:
            break
        time.sleep(1)
        try:
            html_dom = driver.find_element(By.CSS_SELECTOR, '.markdown')
            content = html_dom.get_attribute('innerHTML')
            break
        except:
            continue

    return content


def simulator(ask_msg):
    # 进入输入框并且点击
    print("进入输入框并且点击")
    textarea_dom = get_input_box()
    textarea_dom.click()

    # 发送
    ask_msg_arr = ask_msg.split('
')
    for msg_line in ask_msg_arr:
        textarea_dom.send_keys(msg_line)
    textarea_dom.send_keys(Keys.ENTER)

    # 获取响应
    content = get_response()

    # 保存数据到文件
    root_path = "./data"
    os.makedirs(root_path, exist_ok=True)
    full_path = f"{root_path}/%s.text" % int(time.time())
    with open(full_path, 'w', encoding='utf-8') as file:
        file.write(content)
    print("save success")


def start():
    open_gpt_page()
    print("页面加载完毕")
    time.sleep(3)
    ask_list = get_ask_list()

    for ask_msg in ask_list:
        time.sleep(3)
        print("进入新的聊天")
        new_chat()
        print("current msg %s" % ask_msg)
        for retry in range(0,3):
            try:
                simulator(ask_msg)
                break
            except:
                print(traceback.format_exc())
                time.sleep(10)
                pass

start()

requirements.txt

fake_useragent==1.2.1
psutil==5.9.5
selenium==4.26.1
获取扩展源码

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

  1. 基于数据库创建问题队列

  2. 创建多个浏览器窗口多线程并行运行,提高产量和效率

  3. 解决人工校验-自动化进行人工校验

  4. 为多个浏览器配置不同的代理方案

  5. 代理配置,为浏览器自动化分配代理,

  6. 通过web端进行浏览器管理,代理管理,代理分配等

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940743.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

渗透测试-前端加密分析之RSA加密登录(密钥来源服务器)

本文是高级前端加解密与验签实战的第6篇文章,本系列文章实验靶场为Yakit里自带的Vulinbox靶场,本文讲述的是绕过RSA加密来爆破登录。 分析 这里的代码跟上文的类似,但是加密的公钥是通过请求服务端获取的 http://127.0.0.1:8787/crypto/js/…

Pytorch | 从零构建MobileNet对CIFAR10进行分类

Pytorch | 从零构建MobileNet对CIFAR10进行分类 CIFAR10数据集MobileNet设计理念网络结构技术优势应用领域 MobileNet结构代码详解结构代码代码详解DepthwiseSeparableConv 类初始化方法前向传播 forward 方法 MobileNet 类初始化方法前向传播 forward 方法 训练过程和测试结果…

Java进程占用的内存有哪些部分?

大家好,我是锋哥。今天分享关于【Java进程占用的内存有哪些部分?】面试题。希望对大家有帮助; Java进程占用的内存有哪些部分? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Java进程在运行时,会将内存划分为多个区域&#xf…

秒优科技-供应链管理系统 login/doAction SQL注入漏洞复现

0x01 产品简介 秒优科技提供的供应链管理系统,即秒优SCM服装供应链管理系统,是一款专为服装电商企业设计的全方位解决方案。是集款式研发、订单管理、物料管理、生产管理、工艺管理、收发货管理、账单管理、报表管理于一体的服装电商供应链管理解决方案。它涵盖了从企划到开…

音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现

一、引言 通过FFmpeg命令可以获取到TS文件/TS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息: ./ffmpeg -i XXX.ts 本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式 FFmpeg获取TS文…

VSCode:Markdown插件安装使用 -- 最简洁的VSCode中Markdown插件安装使用

VSCode:Markdown插件安装使用 1.安装Marktext2.使用Marktext 本文,将在Visual Studio Code中,安装和使用Markdown插件,以Marktext插件为例。 1.安装Marktext 打开VSCode,侧边栏中找到扩展模块(或CtrlShiftX快捷键)&am…

线性分类器(KNN,SVM损失,交叉熵损失,softmax)

KNN 工作机制 k-近邻算法的工作机制可以分为两个主要阶段:训练阶段和预测阶段。 训练阶段 在训练阶段,k-近邻算法并不进行显式的模型训练,而是简单地存储训练数据集。每个样本由特征向量和对应的标签组成。此阶段的主要任务是准备好数据&…

知乎 PB 级别 TiDB 数据库集群管控实践

以下文章来源于知乎技术专栏 ,作者代晓磊 导读 在现代企业中,数据库的运维管理至关重要,特别是面对分布式数据库的复杂性和大规模集群的挑战。作为一款兼容 MySQL 协议的分布式关系型数据库,TiDB 在高可用、高扩展性和强一致性方…

Git版本控制工具--基础命令和分支管理

1.Git仓库的基本概念和流程 版本库的概念:版本库又名仓库,英文名repository,你可以简单的理解一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除,Git都能跟踪,以便任何…

EMQX构建简易的云服务

基本思路: 使用EMQX作为Mqtt brokermqtt-receive-server服务,用于接收设备上报的数据mqtt-sender-service服务,用于下发数据给设备KafKa实现数据解耦,mqtt-receive-server服务接收的数据简单处理下直接扔到Kafka中云服务各业务系…

基于MindSpore NLP的PEFT微调

创建notebook 登录控制台 创建notebook 如果出现提示按如下操作 回到列表页面创建notebook参数如下: 配置mindnlp环境 打开GitHub - mindspore-lab/mindnlp: Easy-to-use and high-performance NLP and LLM framework based on MindSpore, compatible with model…

半连接转内连接 | OceanBase SQL 查询改写

查询优化器是关系型数据库系统的核心模块,是数据库内核开发的重点和难点,也是衡量整个数据库系统成熟度的“试金石”。为了帮助大家更好地理解 OceanBase 查询优化器,我们撰写了查询改写系列文章,带大家更好地掌握查询改写的精髓&…

imx6ull qt多页面控制系统(正点原子imx系列驱动开发)

开题答辩完了也考完了四六级,赶紧来更新一下一个月前留下的坑吧 QAQ首先,因为毕业设计需要用到这些知识所以就从网络上找了一个智能车机系统,借鉴了一下大佬的项目思路,缝缝补补一个月终于完成了这一内容。 在这里先感谢从两位大佬…

Intel-ECI之Codesys PLC + Ethercat 远端IO + Codesys IDE编程

目录 一、 准备工作 二、安装Codesys 软件 PLC 三、 使用Codesys IDE 编程测试 CODESYS* 是领先的独立于制造商的 IEC 61131-3 自动化软件,适用于工程控制系统。它用于 Intel Edge Controls for Industrial(Intel ECI 或 ECI),…

vscode的keil assistant 中搜索不到全局变量

搜不到 但是在包含的文件中输入 ../../../,就是全局搜索的结果 我的文件结构是:\Desktop\LVGL文件系统移植(lvgl8.3)\Projects\MDK-ARM 盲猜是keil assistant 当前文件夹打开的时候是进入到了MDK-ARM文件夹层次&…

Unity A*算法实现+演示

注意: 本文是对基于下方文章链接的理论,并最终代码实现,感谢作者大大的描述,非常详细,流程稍微做了些改动,文末有工程网盘链接,感兴趣的可以下载。 A*算法详解(个人认为最详细,最通俗易懂的一…

格式工厂,各类文件格式转换

今天给大家推荐一个老牌的软件格式工厂。这个软件早就能支持转换视频、音频、图片、文档等市面上主流格式的软件了,现在也很能打。 格式工厂 各类文件格式转换 软件无需安装,打开这个图标就能直接使用。 屏幕录像功能还是非常强大的,可以全屏…

Java web的发展历史

目录 前言: 一.Model I和Model II 1.Model I开发模式 ​编辑 2.Model II开发模式 二. MVC模式 前言: 该篇文章主要介绍了Java web的发展历史,以及MVC相关内容 一.Model I和Model II 1.Model I开发模式 Model1的开发模式是&#xff…

Pyqt6在lineEdit中输入文件名称并创建或删除JSON文件

1、创建JSON文件 代码 import osdef addModulekeyWordFile(self):if "" ! self.lineEdit_module.text():moduleFile self.lineEdit_module.text() .jsonelse:self.toolLogPrinting(请输入模块名称)returnfilePath modulekeyWordFileDir moduleFileif os.path.e…

练习题 最小栈

最小栈 最小栈 class MinStack {private Stack<Integer> stack;private Stack<Integer> minstack;public MinStack() {stacknew Stack<>();minstacknew Stack<>();}public void push(int val) {stack.push(val);if(minstack.empty()){minstack.push(…