В предыдущей статье об асинхронном программировании на Python мы разобрали основные моменты, связанные с построением асинхронного приложения и функций. Теперь рассмотрим полезный пример, который вовсю использует принципы асинхронности, — простой парсер данных из Telegram
Содержание статьи
async for
Чтобы пример с парсером был понятнее, сначала поговорим про циклы внутри асинхронных функций. Вспомним финальный пример из первой части статьи, посвященной асинхронному программированию на Python, с получением данных о погоде:
Добавим еще одну функцию, которая будет отображать прогресс измерения давления:
Теперь, если мы запустим приложение, будем видеть прогресс измерения давления:
Все хорошо отрабатывает и проблем нет. Но пойдем дальше в сторону реальных условий работы. Бывает, что функции отдают не готовую последовательность, а асинхронный генератор или асинхронный итератор. Это специальные контейнеры, которые содержат в себе данные и при каждом обращении асинхронно выдают по одному объекту.
Перепишем функцию процесса измерения:
Раз функция def ticker( ) теперь является генератором, то мы должны каждый раз его вызывать, когда она нам понадобится. Классический способ перебора всех значений, возвращаемых генератором, — это подстановка его вызова в цикл for. Перепишем функцию измерения давления:
Теперь, если запустим код, столкнемся с интересной ошибкой:
Исходя из текста может показаться, что генератор не является итерируемым объектом. Но на самом деле проблема здесь в другом. Мы работаем с асинхронным генератором, а значит и цикл, который его перебирает, тоже должен быть асинхронным:
Все снова отлично работает! Зачем это нужно, спросите вы? Прямо сейчас и узнаем.
Как написать парсер телеграм-каналов на Python
Telegram — один из самых популярных источников контента. Каналов в мессенджере столько, что следить за всеми самостоятельно просто нереально, не говоря уж про использование и анализ получаемой информации. Но зачем все делать вручную, когда за нас это может сделать приложение? Совместим приятное с полезным: напишем приложение для парсинга данных из любого телеграм-канала и параллельно закрепим тему асинхронных компонентов.
- Все о парсинге данных на бесплатном вебинаре Контур.Школы.
Шаг 1. Создаем приложение API для Telegram
Для начала необходимо завести аккаунт в Telegram, если его еще нет. Затем переходим по ссылке https://my.telegram.org/auth и авторизуемся с помощью номера телефона.
Код подтверждения придет прямо в мессенджере Telegram. Необходимо его ввести в поле Confirmation code:
В открывшемся окне переходим по ссылке API Development Tools. Вас перебросит в окно создания приложения. Необходимо заполнить основные поля:
Значения полей можно указывать любые. Для нас они не играют особой роли. Ставим точку напротив Desktop. И подтверждаем заполнение кнопкой Create application.
Следующее окно отобразит важные параметры вашего приложения, которые нам и будут нужны для создания приложения на Python. Это App api_id и App api_hash:
Шаг 2. Устанавливаем библиотеку Telethon
Теперь у нас есть все необходимое, чтобы приступить к созданию приложения в Python. Первым делом нам понадобится библиотека telethon, чтобы авторизоваться и получить доступ к данным аккаунта. Устанавливаем ее обычным способом:
Далее импортируем необходимые модули и сразу создадим переменные с данными для авторизации:
Естественно, в серьезных приложениях авторизационные данные не хранятся в таком открытом виде. Обычно используются переменные окружения, содержимое которых не раскрывается.
Следующий шаг — авторизация приложения и создание сессионного файла. Допишем следующую строку:
Здесь 'test_tg' — это название файла, который будет создан для хранения авторизационных данных приложения. Далее необходимо создать тот самый цикл обработки событий (event loop). Это будет асинхронная функция main( ), в которой мы пока напишем команду-заглушку pass. И сразу запустим этот цикл, используя один из методов запуска библиотеки telethon:
Теперь наше приложение необходимо запустить, чтобы пройти одноразовую авторизацию. Примерно так же вы устанавливаете приложение на компьютер и авторизуете его с помощью QR-кода. Запустите приложение — и в окне терминала увидите приглашение ввести проверочный код:
Можно ввести номер телефона — тогда следующее сообщение попросит одноразовый код, который придет в Telegram:
Если все выполнили верно, то ниже в терминале получите сообщение:
А также в списке файлов появится файл с авторизационными данными, имеющий расширение *.session Готово! Все последующие запуски вашего приложения уже будут выполняться без запроса одноразовых паролей. И код, написанный в обработчике событий main( ), будет сразу исполняться.
Шаг 3. Получаем первые данные из Telegram
Настало время сбора данных. Начнем с того, что у telethon есть неплохая документация, основные методы из которой мы и рассмотрим. Первая информация, которая нам доступна, — о нашем аккаунте:
Если проверим тип полученного объекта, то увидим знакомое:
Мы получаем корутину. То есть все методы клиента Telegram асинхронны. Еще бы: без асинхронности мы не смогли бы получать информацию одновременно из нескольких чатов, а отправка фотографии другу вообще застопорила бы все процессы на глобальном уровне.
Поэтому все методы вызываем только через await:
Полученный объект me содержит огромное количество свойств, включая ваше имя, фамилию, номер телефона, фотографию и много еще чего персонального, что афишировать не нужно. Просмотреть содержимое объекта удобно в режиме отладки:
Шаг 4. Получаем диалоги
Чтобы получить список диалогов, воспользуемся еще одним методом клиента .get_dialogs( ):
В dialogs получим общее количество диалогов, которое включает не только каналы, на которые мы подписаны, но и личные переписки. Можете посмотреть их количество в отладчике или воспользоваться функцией len(dialogs) и получить шок от итогового количества. У меня 1540 диалогов:
Так как в dialogs нам, по сути, возвращается итерируемый объект, мы можем легко его перебрать с помощью обычного цикла for, чтобы найти именно тот канал или переписку, которые нам нужны.
Каждый элемент полученного dialogs — это объект класса Dialog библиотеки telethon. Посмотреть методы и свойства этого объекта можно в документации. Переберем диалоги по названиям и найдем нужный:
Шаг 5. Получаем сообщения из выбранного канала
Убедитесь, что нужный канал находится и вы получаете об этом сообщение. Когда канал у вас на руках, можно получить из него все сообщения, которые когда-либо были написаны.
Воспользуемся методом client.iter_messages(dialog), чтобы получить сообщения:
- Важно посмотреть на тип полученного значения у messages:
Получаем не готовый список, как в случае с диалогами, а итератор. Это не должно быть проблемой, потому что элементы итератора мы можем получить так же, как и у списка, — закинуть в цикл for:
Если теперь запустим приложение, то текстов сообщений мы не увидим, а получим ошибку:
Это именно то, о чем мы говорили в самом начале: нужен асинхронный for, чтобы перебирать элементы асинхронного итератора. Исправим эту ошибку и получим желаемый результат:
Единственное, что рекомендуется добавить, — обычную задержку между чтением каждого сообщения во избежание блокировок вашего аккаунта со стороны сервисов Telegram. Пусть будет медленно, зато спокойно и уверенно.
Напоследок рекомендуем также ознакомиться со всеми свойствами и методами объекта message. Основные, которые могут пригодиться при сборе полных данных, вынесены в таблицу:
date | Свойство | Содержит дату сообщения |
edit_date | Свойство | Содержит дату редактирования сообщения |
file | Свойство | Содержит файл, который приложен к сообщению |
download_media( ) | Метод | Позволяет загрузить файл, приложенный к сообщению |
is_reply | Свойство | Показывает, является ли сообщение ответом на другое сообщение |
forward | Свойство | Содержит информацию о первоисточнике, если текущее сообщение было переслано |
Конечно, в разных ситуациях могут потребоваться разные данные, но у этой статьи нет цели перепечатать всю документацию библиотеки telethon. На этой полезной ноте мы и завершим наше знакомство с асинхронным миром программирования.