Всем привет! Во второй части мы продолжим забирать у «защищенного и дважды зашифрованного» Telegram сервера данные о подписчиках из открытых чатов.

Перед прочтением статьи настоятельно рекомендую ознакомиться с первой частью в ней мы создавали аккаунт разработчика Telegram и настраивали наш проект.

На данном этапе это всего один «.py» файл, настройки и файл сессии. Но, как говорит одна известная мудрость:

Делай хорошо — плохо само получится

Поэтому мы изначально будем делать наш проект как настоящие профи. Модулями

В этой части мы получим подписчиков открытого чата мессенджера и посмотрим, какие же сведения нам отдаст Телеграм

Переходим в PyCharm

Чтобы в дальнейшем не запутаться в нашем коде, мы создадим в директории проекта несколько файлов:

Users.py
links.txt

Весь наш код в этой главе мы будем писать именно в отдельном файле Users.py. Это существенно упростит нам работу в дальнейшем. Поверьте

Давайте импортируем этот файл в наш основной проект, который мы писали в первой части

import Users

Кроме того, для дальнейшей работы с пользователями чата нам понадобятся еще парочка импортов из нашей библиотеки Telethon

from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch

Еще раз оговорюсь, что все импорты мы производим в нашем основном главном файле, который мы создавали в первой части «Update.py»

Для наглядности и удобства давайте установим в наш проект такую библиотеку как tqdm. Она позволит нам создавать в нашей консоли красивые читабельные Progress Bar ( графическую полоску прогресса нашей выгрузки )

Пишем команду pip install tqdm

Импортируем класс библиотеки в наш проект

from tqdm import tqdm

С импортами пока разобрались. Конечный итог наших импортов в основном файле выглядит так:

import configparser
from telethon import TelegramClient
import Users
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from tqdm import tqdm

У нас остался непонятный нам файл links.txt, который мы с вами будем использовать как хранилище наших ссылок на чаты из которых будем парсить данные но об этом чуть дальше поговорим.

В нашем файле Users.py давайте создадим асинхронную функцию:

async def dump_all_participants(channel,  ChannelParticipantsSearch,
                                                          client, GetParticipantsRequest, tqdm):

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

channel— это будет наш чат Телеграм который мы передадим в нашу функцию

ChannelParticipantsSearch и GetParticipantsRequest — это наши импорты которые мы делали выше они же классы библиотеки Telethon которые нам будут нужны в нашей функции.

tqdm — это наша библиотека для progress bar

client — это соответственно наше подключение которое мы создавали в первой части. Без него никак)

Теперь давайте настроим чтение ссылок на чаты из нашего файла links.txt

В нашем главном файле Update.py внутри функции main напишем такой код

async def main():
    with open("links.txt", "r") as f:
        while True:
            try:
                text = f.readline()
                url = text
                channel = await client.get_entity(url)
                await Users.dump_all_participants(channel, ChannelParticipantsSearch,
                                                  client, GetParticipantsRequest, tqdm)
            except Exception:
                pass

Здесь, мы сразу после чтения файла будем вызывать нашу функцию «dump_all_participants» из файла Users.py.

Функция сбора пользователей чата Телеграм

Давайте наполним нашу функцию сбора пользователей кодом

Заходим в файл Users.py где мы создавали нашу функцию dump_all_participants

и пропишем константы для библиотеки Telethon

async def dump_all_participants(channel, ChannelParticipantsSearch,
                                client, GetParticipantsRequest, tqdm):
    print('Сбор по каналу', channel.title)
    OFFSET_USER = 0  # номер участника, с которого начинается считывание
    LIMIT_USER = 200  # максимальное число записей, передаваемых за один раз но не более 200
    ALL_PARTICIPANTS = []  # список всех участников канала
    FILTER_USER = ChannelParticipantsSearch('')  # фильтр для определенных пользователей

Фильтром для пользователей нам пользоваться не придется, но объявить и передать его в дальнейшем в качестве аргумента мы должны.

Создадим бесконечный цикл while:

while True:
    participants = await client(GetParticipantsRequest(channel,
                                                   FILTER_USER, OFFSET_USER, LIMIT_USER,hash=0))

Мы передали запрос на получение списка пользователей нашего чата, который хранится в переменной channel.

В результате в переменной participants мы получим набор объектов библиотеки telethon, который содержит набор информации о каждом пользователе.

Пример данных пользователя из чата нашего паблика ВК

id=82197*****,

is_self=False,

contact=False,

mutual_contact=False,

deleted=False,

bot=False,

bot_chat_history=False,

bot_nochats=False,

verified=False,

restricted=False,

min=False,

bot_inline_geo=False,

support=False,

scam=False,

access_hash=79177640707*******8,

first_name=’Ar***a’,

last_name=В*****,

username=N*******,

phone=375294******,

photo=UserProfilePhoto(photo_id=35303467026******4, photo_small=FileLocationToBeDeprecated(volume_id=200397****55, local_id=278**5),

photo_big=FileLocationToBeDeprecated(volume_id=200397****55, local_id=27****), dc_id=2, has_video=False),

status=UserStatusRecently(),

bot_info_version=None,

restriction_reason=[],

bot_inline_placeholder=None,

lang_code=None

Для наглядности заранее пропишем запись полученных сведений в текстовый файл

Обязательно нужно указать кодировку, что бы избежать проблем с никами содержащими символы.

with open(f"{channel.title}.txt", "w", encoding="utf-8") as write_file:
     for participant in tqdm(ALL_PARTICIPANTS):
            try:
                write_file.writelines(f" ID: {participant.id} "
                                      f" First_Name: {participant.first_name}"
                                      f" Last_Name: {participant.last_name}"
                                      f" Username: {participant.username}"
                                      f" Phone: {participant.phone}"
                                      f" Channel: {channel.title} \n")
            except Exception as e:  # noqa
                print(e)
print('Сбор по каналу завершен')

В нашем цикле for указываем наш итерируемый объект как аргумент функции tqdm.

Смотрим результат на примере первого попавшегося чата про Python

Парсинг телеграм-чатов отдельным приложением (Часть 2 Подписчики чатов), изображение №1

В корне нашей программы у нас появился файл {название канала}.txt который содержит сведения о пользователях

Парсинг телеграм-чатов отдельным приложением (Часть 2 Подписчики чатов), изображение №2

Для того, что бы запустить наш парсинг, в файл links.txt нужно вставить ссылку на чат или группу. Найти эту ссылку можно в описании любого чата.

Парсинг телеграм-чатов отдельным приложением (Часть 2 Подписчики чатов), изображение №3
Парсинг телеграм-чатов отдельным приложением (Часть 2 Подписчики чатов), изображение №4

ВАЖНО! Спарсить канал не получится т.к вы не являетесь его администратором.

прим. Автора

Ссылок на чаты может быть много. Важно писать их с новой строки.

Ура! Мы только что скачали список пользователей чата. Узнали их уникальные идентификаторы (id), что поможет нам в дальнейшем найти сообщения определённого пользователя в этом (либо в любом другом чате).

Об выгрузке сообщений из чата мы поговорим в следующей части.

Всем спасибо за внимание! Пишите о ваших впечатлениях в комментарии либо в наш паблик ВК

Полный код парсера Телеграм чатов

Файл Update.py

import configparser
from telethon import TelegramClient
import Users
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from tqdm import tqdm
config = configparser.ConfigParser()
config.read("config.ini")
# Присваиваем значения внутренним переменным
api_id: str = config['Telegram']['api_id']
api_hash = config['Telegram']['api_hash']
username = config['Telegram']['username']
client = TelegramClient(username, api_id, api_hash)
client.start()
async def main():
    with open("links.txt", "r") as f:
        while True:
            try:
                text = f.readline()
                url = text
                channel = await client.get_entity(url)
                print(url)
                await Users.dump_all_participants(channel, ChannelParticipantsSearch,
                                                  client, GetParticipantsRequest, tqdm)
            except Exception:
                pass
with client:
    client.loop.run_until_complete(main())

Файл Users.py

async def dump_all_participants(channel, ChannelParticipantsSearch,
                                client, GetParticipantsRequest, tqdm):
    print('Сбор по каналу', channel.title)
    OFFSET_USER = 0  # номер участника, с которого начинается считывание
    LIMIT_USER = 200  # максимальное число записей, передаваемых за один раз но не более 200
    ALL_PARTICIPANTS = []  # список всех участников канала
    FILTER_USER = ChannelParticipantsSearch('')  # фильтр для определенных пользователей
    while True:
        participants = await client(GetParticipantsRequest(channel,
                                                           FILTER_USER, OFFSET_USER, LIMIT_USER,
    hash=0))
        if not participants.users:
            break
        ALL_PARTICIPANTS.extend(participants.users)
        OFFSET_USER += len(participants.users)
        with open(f"{channel.title}.txt", "w", encoding="utf-8") as write_file:
            for participant in tqdm(ALL_PARTICIPANTS):
                try:
                    write_file.writelines(f" ID: {participant.id} "
                                          f" First_Name: {participant.first_name}"
                                          f" Last_Name: {participant.last_name}"
                                          f" Username: {participant.username}"
                                          f" Phone: {participant.phone}"
                                          f" Channel: {channel.title} \n")
                except Exception as e:  # noqa
                    print(e)
    print('Сбор по каналу завершен')

Подпишитесь на рассылку

Если это было вам полезно — вы можете сказать нам спасибо!