Код
#статьи

Парсим данные в Telegram на Python. Часть 2. Читаем и анализируем сообщения из чатов

Пишем парсер для Telegram: собираем сообщения из каналов и чатов.

Иллюстрация: Катя Павловская для Skillbox Media

Сегодня мы научимся парсить чаты из Telegram: разберёмся в том, какие модули нам пригодятся, и настроим сбор сообщений. Всё это с помощью библиотеки Telethon для Python.

Это второй урок по парсингу в Telegram. В первом мы создали парсер списка участников каналов и чатов и научились работать с инструментами разработчика API мессенджера. Если вы ещё не читали первую часть, то обязательно начните с неё.

Что у нас сейчас есть?

В первой части урока мы:

  • настроили инструменты разработчика API Telegram и научились использовать их для подключения к клиенту мессенджера;
  • установили и импортировали библиотеку Telethon, позволяющую работать с API Telegram, выбрав нужные нам классы, функции и типы;
  • научились парсить список групп и чатов из мессенджера;
  • написали код для парсинга списка пользователей в чате или мессенджере, сохраняющий их в удобном для чтения и последующего анализа виде.

У нас остаётся одна задача — спарсить сообщения из чатов. Мы сохраним только содержание чатов без дополнительной информации о времени их отправки и самом отправителе. В дальнейшем эти данные можно будет использовать для анализа переписки: например, узнать о самых часто встречающихся словах и предположить самые популярные темы для обсуждения.

Добавляем новые импорты

Откроем в IDE код из первой части урока. Мы продолжим работать с ним, а не будем писать всё с нуля. Чтобы спарсить сообщения из чатов, необходимо импортировать новый метод и функцию в начало кода:

from telethon.tl.functions.messages import GetHistoryRequest
from telethon.tl.types import PeerChannel

Что мы импортировали:

  • GetHistoryRequest — метод, позволяющий получить сообщения пользователей из чата и работать с ним;
  • PeerChannel — специальный тип, определяющий объекты типа «канал/чат», с помощью которого можно обратиться к нужному каналу для парсинга сообщений.

Пишем код парсера сообщений

Начнём с создания пустого списка, который будет хранить сообщения, и инициализируем несколько переменных, которые пригодятся позже.

all_messages = []
offset_id = 0
limit = 100
total_messages = 0
total_count_limit = 0

Список all_messages используется для хранения спарсенных сообщений. Переменная limit задаёт лимит на парсинг сообщений — за каждый цикл работы будет сохраняться только 100 сообщений.

Кроме того, мы используем ещё две переменные — total_messages и total_count_limit. Первая выступает счётчиком спарсенных сообщений, а вторая позволят нам задать ограничение на общее количество полученных сообщений. В примере кода total_count_limit равен 0, то есть парсятся все сообщения. Если история чата большая, то её парсинг может занять слишком много времени. В этом случае можно ограничить количество спарсенных сообщений вручную.

offset_id — важная переменная. К ней будет обращаться метод GetHistoryRequest для того, чтобы понять, с какого сообщения начать парсинг. Вначале присваиваем ей значение 0, чтобы парсинг шёл с самого первого сообщения в канале. То есть если бы мы указали значение, равное 100, то первые 100 сообщений парсер бы пропустил.

Добавим в код сам парсер сообщений и разберём его:

while True:
   history = client(GetHistoryRequest(
       peer=target_group,
       offset_id=offset_id,
       offset_date=None,
       add_offset=0,
       limit=limit,
       max_id=0,
       min_id=0,
       hash=0
   ))
   if not history.messages:
       break
   messages = history.messages
   for message in messages:
       all_messages.append(message.to_dict())
   offset_id = messages[len(messages) - 1].id
   if total_count_limit != 0 and total_messages >= total_count_limit:
       break

Парсинг реализуется через цикл while. Код в цикле будет выполняться до тех пор, пока в чате остаются сообщения, которые мы ещё не спарсили, или пока не будет достигнут установленный лимит по числу собранных сообщений.

Для получения сообщений используем стандартный метод библиотеки Telethon — GetHistoryRequest, передавая его внутри запроса нашего клиента к API Telegram. Для выбора группы используется параметр peer, которому мы присваиваем значение выбранной группы, полученной в начале работы парсера. В нашем случае это другая переменная — target_group.

Параметры offset_date и offset_peer мы передаём с пустыми значениями. Обычно они используются для фильтрации полученных данных, но здесь мы хотим получить весь список сообщений. Лимит по количеству элементов в ответе задаём числом 100, передавая в параметр limit переменную limit. Также у нас есть три обязательных параметра для работы GetHistoryRequest: min_id, max_id и hash. Их передаём с нулевыми значениями, так как они не будут использоваться, но указать их обязательно — если этого не сделать, метод не будет работать.

Основной цикл работает просто:

  • Проверяем, остаются ли сообщения, которые можно спарсить. Если нет, то цикл завершается. Если сообщения есть, то сохраняем их в список messages.
  • Каждое сообщение из списка message мы сохраняем в список all_messages.
  • Если количество полученных сообщений больше их количества в чате или мы достигли лимита парсинга, то цикл завершается.

GetHistoryRequest получает offset_id, который показывает, с какого сообщения GetHistoryRequest должен начать получать историю чата. Важно правильно установить смещение для offset_id, чтобы все сообщения были уникальными. Поэтому стартовое значение offset_id для следующего прохождения цикла устанавливается со смещением на одно сообщение.

Сохраняем полученные сообщения в файл

Полученные сообщения сохраняем в формате CSV по аналогии с парсингом пользователей:

print("Сохраняем данные в файл...") #Cообщение для пользователя о том, что начался парсинг сообщений.
 
with open("chats.csv", "w", encoding="UTF-8") as f:
   writer = csv.writer(f, delimiter=",", lineterminator="\n")
   writer.writerow(["message"])
   for message in all_messages:
       writer.writerow([message])     
print("Парсинг сообщений группы успешно выполнен.") #Сообщение об удачном парсинге чата.

После этого переходим к сохранению данных в файл формата CSV с помощью стандартного модуля csv. Открываем файл в режиме записи, указывая кодировку UTF-8.

Если файл отсутствует в директории, то будет в ней создан. После этого создаём объект CSV writer и записываем в него сообщение из нашего списка all_messages.

Запустим парсер и выберем группу из списка:

Скриншот: Telethon

Всё получилось. Откроем нашу директорию. Теперь в ней два файла в формате CSV: members.csv и chats.csv. Откроем последний:

Скриншот: Telethon 

Мы видим, что сохранились не только сами сообщения, но и техническая информация про их id, id запроса и так далее. Такой результат нам не подходит. Вернёмся к циклу while и добавим к методу message параметр message. Он позволяет сохранять только само сообщение.

if not history.messages:
       break
   messages = history.messages
   for message in messages:
       all_messages.append(message.message) #Добавим параметр message к методу message.
   offset_id = messages[len(messages) - 1].id
   if total_count_limit != 0 and total_messages >= total_count_limit:
       break

Повторно запустим код и посмотрим на результат:

Скриншот: Telethon 

Теперь всё работает как надо. Сохранились только сообщения без служебной информации о запросах.

Код парсера для Telegram на Python полностью

В двух частях урока по парсингу информации из Telegram мы написали много кода. Посмотрим на него полностью:

from xmlrpc.client import DateTime
from telethon.sync import TelegramClient
 
from telethon.tl.functions.messages import GetDialogsRequest
from telethon.tl.types import InputPeerEmpty
from telethon.tl.functions.messages import GetHistoryRequest
from telethon.tl.types import PeerChannel
 
import csv
 
api_id = 18377495
api_hash = "a0c785ad0fd3e92e7c131f0a70987987"
phone = "+79991669331"
 
client = TelegramClient(phone, api_id, api_hash)
 
client.start()
 
chats = []
last_date = None
chunk_size = 200
groups=[]
result = client(GetDialogsRequest(
            offset_date=last_date,
            offset_id=0,
            offset_peer=InputPeerEmpty(),
            limit=chunk_size,
            hash = 0
        ))
chats.extend(result.chats)
for chat in chats:
   try:
       if chat.megagroup== True:
           groups.append(chat)
   except:
       continue
print("Выберите группу для парсинга сообщений и членов группы:")
i=0
for g in groups:
   print(str(i) + "- " + g.title)
   i+=1
g_index = input("Введите нужную цифру: ")
target_group=groups[int(g_index)]
print("Узнаём пользователей...")
all_participants = []
all_participants = client.get_participants(target_group)
print("Сохраняем данные в файл...")
with open("members.csv", "w", encoding="UTF-8") as f:
   writer = csv.writer(f,delimiter=",",lineterminator="\n")
   writer.writerow(["username", "name","group"])
   for user in all_participants:
       if user.username:
           username= user.username
       else:
           username= ""
       if user.first_name:
           first_name= user.first_name
       else:
           first_name= ""
       if user.last_name:
           last_name= user.last_name
       else:
           last_name= ""
       name= (first_name + ' ' + last_name).strip()
       writer.writerow([username,name,target_group.title])     
print("Парсинг участников группы успешно выполнен.")
 
offset_id = 0
limit = 100
all_messages = []
total_messages = 0
total_count_limit = 0
 
while True:
   history = client(GetHistoryRequest(
       peer=target_group,
       offset_id=offset_id,
       offset_date=None,
       add_offset=0,
       limit=limit,
       max_id=0,
       min_id=0,
       hash=0
   ))
   if not history.messages:
       break
   messages = history.messages
   for message in messages:
       all_messages.append(message.message)
   offset_id = messages[len(messages) - 1].id
   if total_count_limit != 0 and total_messages >= total_count_limit:
       break
  
print("Сохраняем данные в файл...")
with open("chats.csv", "w", encoding="UTF-8") as f:
   writer = csv.writer(f, delimiter=",", lineterminator="\n")
   for message in all_messages:
       writer.writerow([message])  
print('Парсинг сообщений группы успешно выполнен.')

Что дальше?

Библиотека Telethon используется не только для парсинга информации о пользователях и сообщений в каналах и чатах, но и для автоматизации работы с Telegram. Например, администраторы каналов могут создать бота, позволяющего писать сообщения, отправлять стикеры и управлять списком участников. Эти примеры разобраны в документации библиотеки.

Изучайте IT на практике — бесплатно

Курсы за 2990 0 р.

Я не знаю, с чего начать
Научитесь: Профессия Python-разработчик Узнать больше
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована