Сильный ИИ. Элира2. Сохранение диалогов ChatGPT

e5e4ae5e04778ebd0e7b4183a5a432cc.jpg

Всем привет!

В этой статье я расскажу о том, как можно сохранить диалоги в ChatGPT вместе с изображениями из чата!

Несмотря на уже довольно долгое существование ChatGPT в нем до сих пор отсутствует функция сохранения диалогов с изображениями. Более того, при наличии изображений в диалоге, ссылка для сохранения вообще не создается. Именно поэтому я решил создать скрипт, который позволит сохранять диалоги напрямую через веб-интерфейс ChatGPT, используя Selenium.

Подготовка

Для начала необходимо установить необходимые зависимости. Создаем файл requirements.txt с содержимым:

undetected-chromedriver
selenium
requests

Затем устанавливаем зависимости с помощью команды (в консоли):
pip install -r requirements.txt

Далее создаем файл credentials.txt, в который записываем логин и пароль для автоматического входа в ChatGPT:

myemail@example.com
mypassword

Создание скрипта

Начнем с импорта необходимых библиотек и чтения учетных данных:

import os
import time
import requests
from undetected_chromedriver import Chrome, ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Чтение учетных данных из файла
with open('credentials.txt', 'r', encoding='utf-8') as f:
    credentials = f.read().splitlines()
email = credentials[0]
password = credentials[1]

Для автоматизации браузера (и обхода cloudflare) используем библу undetected_chromedriver.
Настраиваем его и открываем страницу для входа в ChatGPT:

# Настройка undetected_chromedriver
options = ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')

with Chrome(options=options) as driver:
    driver.get('https://chat.openai.com/')
    wait = WebDriverWait(driver, 10)
    login_button = wait.until(EC.element_to_be_clickable((By.XPATH, '/html/body/div[1]/div[1]/div[2]/div[1]/div/div/button[1]/div/span')))
    login_button.click()

    # Ввод логина
    email_field = wait.until(EC.presence_of_element_located((By.XPATH, '/html/body/div/div/main/section/div[2]/div[1]/input')))
    email_field.send_keys(email)
    next_button = driver.find_element(By.XPATH, '/html/body/div/div/main/section/div[2]/button')
    next_button.click()

    # Ввод пароля
    password_field = wait.until(EC.presence_of_element_located((By.XPATH, '/html/body/div[1]/main/section/div/div/div/form/div[1]/div/div[2]/div/input')))
    password_field.send_keys(password)
    login_button = driver.find_element(By.XPATH, '/html/body/div[1]/main/section/div/div/div/form/div[2]/button')
    login_button.click()

    input("Выберите нужный диалог и нажмите Enter для продолжения...")

Создаем папки для сохранения данных и функции для сохранения текста и скачивания файлов:

# Создание папок для сохранения данных
if not os.path.exists('Диалог'):
    os.makedirs('Диалог/Вложения')

# Функция для сохранения текста сообщения
def save_text(message, filename):
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(message)

# Функция для скачивания файла по URL
def download_file(url, dest_path):
    response = requests.get(url)
    with open(dest_path, 'wb') as f:
        f.write(response.content)

Далее создаем основной HTML-файл для сохранения диалогов. В HTML включены стили, сделанные в цветах веб-интерфейса ChatGPT (для крОсоты):

# Создание основного HTML файла
html_content = """



    
    Диалог
    
    
    
    


×
"""

Теперь приступаем к сбору всех сообщений и их обработке. Будем определять «авторство» сообщения и обрабатывать текст, код и изображения отдельно:

# Инициализация счетчиков
total_messages = 0
user_messages = 0
ai_messages = 0
skipped_msg = 0

# Сбор всех сообщений
messages = driver.find_elements(By.XPATH, '//div[contains(@class, "group") and contains(@class, "flex") and not(contains(@class, "h-48"))]')
for idx, message in enumerate(messages):
    if not message.text.strip():
        skipped_msg += 1
        continue  # Пропускаем пустые сообщения

    total_messages += 1
    
    role = ""
    # Проверка, является ли автор сообщения пользователем или ИИ
    try:
        author_role_element = message.find_element(By.XPATH, './/div[@data-message-author-role]')
        author_role = author_role_element.get_attribute('data-message-author-role')
        if author_role == 'user':
            user_messages += 1
            role = "Пользователь"
            role_class = "user-message"
        elif author_role == 'assistant':
            ai_messages += 1
            role = "ИИ"
            role_class = "ai-message"
        else:
            continue  # Пропускаем неизвестные роли
    except:
        continue  # Пропускаем сообщения без атрибута data-message-author-role

    # Заменяем роль на значок для ИИ
    if role == "ИИ":
        role_icon = "ChatGPT Icon"
        role_html = f"

{role_icon}

" else: role_html = "" html_content += f"
{role_html}" # Сохранение текста и кода text_elements = message.find_elements(By.XPATH, './/div[contains(@class, "whitespace-pre-wrap")]') if text_elements: text_content = text_elements[0].text save_text(text_content, f'Диалог/message_{idx}.txt') code_elements = message.find_elements(By.XPATH, './/code[contains(@class, "!whitespace-pre hljs")]') if code_elements: for code_element in code_elements: code_content = code_element.text save_text(code_content, f'Диалог/code_{idx}.txt') text_content = text_content.replace(code_content, f"
{code_content}
») html_content += f»

{text_content}

» print (f«Сообщение {idx — skipped_msg} сохранено (Текст: 1, Код: {len (code_elements)})») # Сохранение изображений img_elements = message.find_elements (By.TAG_NAME, 'img') if img_elements: for img_idx, img_element in enumerate (img_elements): img_url = img_element.get_attribute ('src') img_path = f’Вложения/image_{idx}_{img_idx}.png' download_file (img_url, f’Диалог/{img_path}') html_content += f»
» print (f«Изображения: {len (img_elements)}», end=»,») else: print (f«Изображения: 0», end=»,») # Сохранение прикрепленных файлов file_elements = message.find_elements (By.XPATH, './/a[contains (@href,»/attachment»)]') if file_elements: for file_element in file_elements: file_url = file_element.get_attribute ('href') file_name = file_element.text file_path = f’Вложения/{file_name}' download_file (file_url, f’Диалог/{file_path}') html_content += f»{file_name}» print (f«Файлы: {len (file_elements)})») else: print (f«Файлы: 0)») html_content += »
»

Не забудем корректно закрыть HTML-структуру, сохранить основной HTML-файл и вывести статистику:

# Закрытие HTML структуры
html_content += """













"""

# Сохранение основного HTML файла
with open('Диалог/диалог.html', 'w', encoding='utf-8') as f:
    f.write(html_content)

# Вывод статистики
print(f"Всего сообщений: {total_messages}")
print(f"Сообщений пользователя: {user_messages}")
print(f"Сообщений ИИ: {ai_messages}")

print("Диалог сохранен.")

Ссылка на репо с исходным кодом:

https://github.com/Devastor/DevastorSaveDialogues_ChatGPT

там же можно скачать файл иконки (для обозначения ИИ в финальном диалоге)

Что можно (и нужно) улучшить

  1. Добавить возможность сохранения не только изображений, но и других форматов вложений, например, таблицы xls, csv (другие вложения живут в чате недолго, в отличие от таблиц).

  2. Пофиксить ошибку «OSError: [WinError 6] Неверный дескриптор», которая вылетает после сохранения диалога (у меня не получилось, хотя я не особо старался, ибо лень, и работе скрипта не мешает, это должно быть довольно просто).

  3. Добавить сохранение всех диалогов аккаунта в цикле (легко).

  4. Добавить сохранение названия диалога (не так тривиально, как кажется — нужно как-то придумать, каким образом ловить имя контейнера того диалога, который выбран).

  5. Убрать лишнюю кнопку «Копировать» из правого верхнего угла :)

  6. Сделать логи отключаемыми (лучше всего прописать в args)

© Habrahabr.ru