Сбор информации о подписчиках Telegram-канала

Время чтения текста – 6 минут

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

Создание приложения

Для начала необходимо создать приложение, через которое будут отправляться запросы к API. Перейдите на https://my.telegram.org и авторизуйтесь в Telegram-аккаунте:

После успешной авторизации перейдите на страницу API development tools:

Заполните все поля и жмите на создание приложения:

Из полученной конфигурации нам необходим app api_id и app api_hash:

Запрос к API

Импортируем telethon — он поможет сформировать запрос, и pandas — полученный ответ мы запишем в DataFrame.

from telethon import TelegramClient
import pandas as pd

Вводим api_id, api_hash, наш номер телефона и ссылку на канал, информацию о подписчиках которого хотим получить. Доступ к информации о подписчиках есть только у администраторов канала.

api_id = 1234567
api_hash = '1b42hj25kd8jw42b234kwj242c'
phone = '+71234567890'
channel_href = 'https://t.me/leftjoin'

Создаём новую сессию — вместо session_name можно подставить любое другое название. Методы в библиотеке работают асинхронно, поэтому ответа от них требуется ожидать:

client = TelegramClient('session_name', api_id, api_hash)
client = await client.start()
dialogs = await client.get_dialogs()

Собираем все каналы текущего пользователя. Из ссылки забираем часть с именем канала и вытаскиваем из словаря нужный:

channels = {d.entity.username: d.entity
            for d in dialogs
            if d.is_channel}
my_channel = channel_href.split('/')[-1]
channel = channels[my_channel]

Подписчиков, доступ к которым не ограничен приватностью, можно получить методом get_participants. С 20 июля 2018 года Telegram установил ограничение в 200 подписчиков для вызова метода, и установка параметра aggressive на True поможет получить всех подписчиков за раз.

members_telethon_list = await client.get_participants(channel, aggressive=True)

Из полученных библиотечных структур извлекаем информацию о пользователях — их имена и телефоны:

username_list = [member.username for member in members_telethon_list]
first_name_list = [member.first_name for member in members_telethon_list]
last_name_list = [member.last_name for member in members_telethon_list]
phone_list = [member.phone for member in members_telethon_list]

Из четырёх списков собираем DataFrame и пишем его в csv-таблицу:

df = pd.DataFrame()
df['username'] = username_list
df['first_name'] = first_name_list
df['last_name'] = last_name_list
df['phone'] = phone_list
df.to_csv('subscribers.csv', index=False)

Результат работы — такая таблица:

Для запуска в Jupyter Notebook описанный ниже код можно просто вставить в ячейку, но при запуске из Python-файла будет такая ошибка:

SyntaxError: 'await' outside function

Устранить проблему можно, записав весь код в асинхронную функцию. Целиком выглядеть код будет так:

from telethon import TelegramClient
import pandas as pd
import asyncio

async def main():
        api_id = 1234567
        api_hash = '1b42hj25kd8jw42b234kwj242c'
        phone = '+71234567890'
        channel_href = 'https://t.me/leftjoin'

	client = TelegramClient('session_name', api_id, api_hash)
	client = await client.start()
	dialogs = await client.get_dialogs()

	channels = {d.entity.username: d.entity
				for d in dialogs
				if d.is_channel}
	my_channel = channel_href.split('/')[-1]
	channel = channels[my_channel]

	members_telethon_list = await client.get_participants(channel, aggressive=True)

	username_list = [member.username for member in members_telethon_list]
	first_name_list = [member.first_name for member in members_telethon_list]
	last_name_list = [member.last_name for member in members_telethon_list]
	phone_list = [member.phone for member in members_telethon_list]

	df = pd.DataFrame()
	df['username'] = username_list
	df['first_name'] = first_name_list
	df['last_name'] = last_name_list
	df['phone'] = phone_list
	df.to_csv('subscribers.csv', index=False)

if __name__ == '__main__':
	loop = asyncio.get_event_loop()
	loop.run_until_complete(main())
Поделиться
Отправить
Запинить
 6350   2021   Analytics Engineering   python   telegram   telethon
28 комментариев
None 2021

Что-то не работает... Ругается: SyntaxError: ’await’ outside function

Николай Валиотти 2021

Добрый день! Проверили способ прямо сейчас — у нас всё работает. Пришлите, пожалуйста, свой код (можно через gist.github.com) и версию Python.

None 2021

Да я вообще не очень разбираюсь в Python’е. Только начал учить буквально на днях. Парсер чатов-то сделал, но там попроще было (на другом сайте), а сейчас понадобился для дела свой канал собрать, а там такие камни подводные :)

Николай Валиотти 2021

В Python вовсе необязательно разделять отдельные участки кода на функции, как, например, в C :) Попробуйте написать без них, просто записывая последовательно все команды из материала в файл — должно получиться вот так: https://gist.github.com/Elisejj/aec7867e5da55e295d148882ae41488c

None 2021

Если так делать, то он снова на await ругается: SyntaxError: ’await’ outside function

Я ж с такой «атаки в лоб» и начинал :) В общем, вот такое решение у меня работает (всё парсит и файл сохраняет): https://gist.github.com/Mnsrff/9cba7a3231034fac900dd00b692d7c0f

Но хотелось бы разобраться, почему так :) Почему у вас без функций всё работает, а я вынужден был всё в них заворачивать и вызывать :) Python 3.7 у меня. Библиотеки установлены.

Marsel Kh 2021

Добрый день. Спасибо за код.
А с чем может быть связано несовпадение количества участников группы и количество выгруженных? Подписчики могут где-то ограничить приватность и не светиться в группах?
А то у меня из 574 юзеров выгружается только 470, хотя последний элемент в members_telethon_list total равняется 574.

Николай Валиотти 2021

Да, я подозреваю, что дело в настройках приватности у ряда пользователей, которые не дают свои данные выгрузить. У меня была аналогичная ситуация с экспортом собственных подписчиков.

Максим Игоревич 2021

Добрый день.
Подскажите пожалуйста, как вытащить ID юзеров.

Максим Игоревич 2021

Разобрался)

Михаил Артемьев 2021

Подскажите, есть ли способ получить список подписчиков канала, которые были удалены?

Николай Валиотти 2021

насколько мне известно, нет, увы, их уже не получить

Ольга Телюкова 2021

Мы пробрасываем лиды из ФБ в телегу и гугл таблицы через Webjack. Сервис работает как часы.
Можно получить месяц халявы и бесплатную настройку по промокоду semen21, напишите его в чат техподдержки на сайте

Александр 2021

Здравствуйте! Подскажите пожалуйста, возможно ли узнать, кто-нибудь мог таким образом спарсить подписчиков с моего канала?

Николай Валиотти 2021

вся статья об этом, вы ее читали?

Александр 2021

Читал, но ничего не понял)))
У меня есть канал, и ощущение что один из админов спарсил моих подписчиков. Могу ли я как-то это проверить.
П.С.: Извиняюсь, если мой вопрос для Вас совсем тупой. Я чукча)

Роман Лыжов 2022

Добрый день
У меня почему-то выдает ошибку:


KeyError Traceback (most recent call last)
C:\Users\Public\Documents\Wondershare\CreatorTemp/ipykernel_7648/3941792516.py in <module>
15 if d.is_channel}
16 my_channel = channel_href.split(’/’)[-1]
—-> 17 channel = channels[my_channel]
18
19 members_telethon_list = await client.get_participants(channel, aggressive=True)

KeyError: ’TestChannelRomanLyzhov’

Хотя канал существует, при выполнении даже был запрос на авторизацию с подтверждением через телеграм.
Канал @TestChannelRomanLyzhov свежесозданный, для теста.
Сможете помочь?

Николай Валиотти 2022

А у меня не находит ваш канал: TestChannelRomanLyzhov. Он доступен в паблике?

Давид 2022

Привет, у меня выскакивает вот такая ошибка DeprecationWarning: There is no current event loop
loop = asyncio.get_event_loop() в чём дело и как сделать чтобы ко работал

Абдуллаев Тимур 2022

Здравствуйте. Можно ли парсить телеграм каналы в которых я состою в свой личный?
Допустим у меня будут два моих канала в 1ый- будут рассылаться сообщения, картины которые парсит бот, чтобы я там мог редактировать(к примеру парсит картину и цену товара и мне нужно в своем 1ом канале иметь доступ к редактированию) и во 2ой канал уже будет рассылаться готовый контент.
Заране спасибо за ответ)

Ник Бул 2022

Добрый день!
Выскакивает ошибка :

telethon.errors.rpcerrorlist.BotMethodInvalidError: The API access for bot users is restricted. The method you tried to invoke cannot be executed as a bot (caused by GetDialogsRequest)

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

Николай Валиотти 2022

А что мешает добавить бота в свой канал?

Ник Бул 2022

В этом и суть, канал не мой)

Николай Валиотти 2022

Тогда у вас ничего не выйдет. Это метод для работы с собственным каналом

Алексей 2022

Здравствуйте, смогу ли я спарсить подписчиков с канала, в котором я администратор, а не владелец?

Руслан Трифонов 2022

Добрый день! Не могу получить данные на этапе создания нового приложения, ничего не происходит https://prnt.sc/_bby94Pk7OR3 нажимаю ок и все, дальше ничего нет.

Николай Валиотти 2022

Ну, это вам в суппорт Телеграмма

Ivan Mashtalov 2022

Такую ошибку выдает:
line 11
client = TelegramClient(’session_name’, api_id, api_hash)
TabError: inconsistent use of tabs and spaces in indentation

Николай Валиотти 2022

Очевидно, что у вас что-то не так с индентацией?

Доценко Даниил 2022

Здравствуйте, уже который месяц пытаюсь решить проблему которые вы описываете в этом посте. Но однако ваш метод не является рабочим в данный момент. Список участников канала как был ограничен 200 сотнями так и остается, если я конечно же все правильно понимаю.

Николай Валиотти 2022

Не понял, что за ограничение в 2 сотни? у меня ограничения на выгрузку не было.

Доценко Даниил 2022

Это снова я, пытаюсь достать списко своих подписчиков которых сейчас 500. Если добавляюАГРЕССИВ = true вылазит такая ошибка.
telethon.errors.common.MultiError: ([None, None, FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), None, FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), FloodWaitError(’A wait of 30 seconds is required (caused by GetParticipantsRequest)’), F[<telethon.tl.types.channels.ChannelParticipants object at 0x0000021E5B8EB130>, <telethon.tl.types.channels.ChannelParticipants object at 0x0000021E5B8E37F0>, None, <telethon.tl.types.channels.ChannelParticipants object at 0x0000021E5B8E7E50>, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None], [<telethon.tl.functions.channels.GetParticipantsRequest object at 0x0000021E5B7E1BB0>, <telethon.tl.functions.channels.GetParticipantsRequest object at 0x0000021E5B7E1C70>, <telethon.tl.functions.channels.GetParticipantsRequest object at 0x0000021E5B7E1D30>,

Александр Ко 2022

Николай, благодарю за статью, код работает!
Но агрессив не получается применить на группе, у меня там 14к человек, он пишет, что слишком много попыток, и дропает парсинг.
Можете проверить, как это работает сейчас?
Буду признателен, если подскажете, как это обойти.

Александр Ко 2022

Всё как пишет Доценко Даниил

Владислав 2022

Выходит ошибка:
«DeprecationWarning: There is no current event loop — loop = asyncio.get_event_loop()»
Если заменить на:
«loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())»
Тогда ошибка не выходит, но выдает список всего 200 записей

Николай Валиотти 2022

Видимо что-то поменялось

Никита 2022

Можно ли обойти лимит на сбор до 10 к юзеров?

Михаил 2022

Этот код уже к сожалению не работает, телеграм убрал возможность так просто парсить всех подписчиков из больших групп
Если вам нужно спарсить чат или канал, заходите в @parsetgbot

Антон 2022

Как получить Описание (Bio) профиля тг

Александр 6 мес

Есть решение посмотреть более 200 последних подписавшихся? При исполнении данного кода получается сохранить только их.

Популярное