Как написать парсер на python contactbase
Перейти к содержимому

Как написать парсер на python contactbase

  • автор:

Как написать парсер телеграм‑каналов на Python

Акчурин И. С.

В предыдущей статье об асинхронном программировании на Python мы разобрали основные моменты, связанные с построением асинхронного приложения и функций. Теперь рассмотрим полезный пример, который вовсю использует принципы асинхронности, — простой парсер данных из Telegram

Содержание статьи

  • async for
  • Как написать парсер телеграм-каналов на Python
    • Шаг 1. Создаем приложение API для Telegram
    • Шаг 2. Устанавливаем библиотеку telethon
    • Шаг 3. Получаем первые данные из Telegram
    • Шаг 4. Получаем диалоги
    • Шаг 5. Получаем сообщения из выбранного канала

    async for

    Чтобы пример с парсером был понятнее, сначала поговорим о циклах внутри асинхронных функций. Вспомним финальный пример из первой части статьи, посвященной асинхронному программированию на Python, с получением данных о погоде.

    Добавим еще одну функцию, которая будет отображать прогресс измерения давления.

    Теперь, если мы запустим приложение, будем видеть прогресс измерения давления.

    Все хорошо отрабатывает, и проблем нет. Но пойдем дальше в сторону реальных условий работы. Бывает, что функции отдают не готовую последовательность, а асинхронный генератор или асинхронный итератор. Это специальные контейнеры, которые содержат данные и при каждом обращении асинхронно выдают по одному объекту.

    Перепишем функцию процесса измерения.

    Раз функция def ticker ( ) теперь генератор, то мы должны каждый раз его вызывать, когда она нам понадобится. Классический способ перебора всех значений, возвращаемых генератором, — это подстановка его вызова в цикл for. Перепишем функцию измерения давления.

    Теперь, если запустим код, столкнемся с интересной ошибкой.

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

    Все снова отлично работает! Зачем это нужно, спросите вы? Прямо сейчас и узнаем.

    Как написать парсер телеграм-каналов на Python

    Основы языка Python
    Курс для всех, а не только для IT-специалистов

    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 диалогов.

    Зачем специалисту Python
    Бесплатный вебинар в Контур.Школе

    Так как в 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. На этой полезной ноте мы и завершим наше знакомство с асинхронным миром программирования.

    Как правильно написать парсер на python?

    Я написал код нахождения заголовков и скачивания изображений. Только по отдельности коды работают, а вместе нет. Мне нужно сделать чтобы название было заголовком и к нему скачивалось его изображение. Вот сам код:

    from bs4 import BeautifulSoup as bs import requests page = 1 while page '.format(link)) with open(names.format(link), 'wb') as f: f.write(requests.get(link).content) page += 1 

    Отслеживать
    82 14 14 бронзовых знаков
    задан 3 июн 2022 в 21:47
    user501488 user501488

    2 ответа 2

    Сортировка: Сброс на вариант по умолчанию

    Если я правильно понял задачу, то правильный парсер, с моей точки зрения, мог бы выглядеть так:

    pip install fake-useragent requests tqdm

    from tqdm.auto import tqdm import re from requests import Session from requests.adapters import HTTPAdapter from urllib3.util import Retry from fake_useragent import UserAgent from multiprocessing.pool import ThreadPool as Pool from bs4 import BeautifulSoup as Soup, Tag from pathlib import Path # Определим папку, куда мы будем сохранять изображения downloads = Path('downloads') # Создадим эту папку если ее не существует # exists_ok: позволит не поднимать исключение если # папка уже существует # parents: позволит создать весь путь до конечной # папки в случае его отсутствия downloads.mkdir(exist_ok=True, parents=True) ua = UserAgent() s = Session() # Для некоторых особо капризных серверов желательно добавить в сессию адаптер, # для корректной обработки запросов. В данном случае в этом нет необходимости. # Сайт не капризничает и отдает все нормально s.mount( 'https://ananasposter.ru', HTTPAdapter( max_retries=Retry( connect=5, read=10, total=25, backoff_factor=.005, allowed_methods=['GET'], ) ) ) # Добавим в заголовок сессии User-Agent, чтобы сайт думал что мы FireFox s.headers.update( ) url = 'https://ananasposter.ru/catalogue' # Не все символы разрешено использовать для названия файлов, # поэтому создадим словарь для подмены запрещенных символов subs = < '\\': '-', '/': '-', ':': '~', '*': '#', '?': '', '': ']', '"': "'", '|': '~', '\n': ' ' > def get_image(img: Tag): # Получим ссылку на изображение из тега img link = img.get('src') # Используя регулярное выражение определим системное # имя и расширение файла изображения, # опустив его размер idx, extension = m.groups() if ( m := re.search(r'(?' if isinstance( tt := img.get('title'), str ) else f'.' downloads.joinpath(title).write_bytes( s.get(link).content ) def process_page(page_num: int): # Получим response от страницы с указанным номером # Все, что идет после знака вопроса в url лучше указывать в качестве параметров, # а не придумывать способы их конкатенации resp = s.get( url, params= < 'page': page_num >) soup = Soup(resp.content, 'html.parser') # Если номер обрабатываемой страницы равен 1, то вычисляем номер последней страницы # (в противном случае в этом нет необходимости и данную операцию можно пропустить) # для этого находим все теги a, параметр href которых # соответствует регулярному выражению # получаем из них параметр href, удаляем из полученного url # все кроме номера страницы # и находим максимальный по ключу int # В случае если список необходимых url окажется пустым, # вернем единицу как максимальное значение из списка [1] pages = None if page_num - 1 else int(max( [ a.get('href').removeprefix('https://ananasposter.ru/catalogue?page=') for a in soup.find_all('a', href=re.compile(r'(?<=page=)\d+$')) ] or [1], key=int )) # Каждый найденный тег img класса main_image for image in soup.find_all('img', class_='main_image'): # Отдадим на обработку в функцию get_image get_image(image) # Функция вернет вычисленный выше номер последней страницы return pages # Запустим обработку первой страницы и получим номер последней для # использования его в создании цикла pages_total = process_page(1) # Ввиду немалого количества страниц предлагаю использовать пул из 20-ти потоков with Pool(20) as pool: # Использование бара tqdm дает возможно отслеживать процесс обработки for _ in tqdm( # imap_unordered даст возможность задействовать освободившиеся # потоки вне очереди pool.imap_unordered( process_page, # Начинаем обработку со второй страницы, так-как # первую мы уже обработали range(2, pages_total) ), total=pages_total, initial=2 ): # Поскольку функция process_page уже не возвращает # ничего нужного, здесь мы ничего не делаем pass 
    # 100%|██████████| 1303/1303 [22:40 

    UPD:

    В общем я оказался прав. Дело было в одинаковых названиях:

    Добавьте импорт:
    from uuid import uuid4 

    Пишем парсер на Python за 5 минут

    В данной статье мы рассмотрим, как парсить сайты быстро и эффективно при помощи нового инструмента LxmlSoup. Библиотека является аналогом популярной BeautifuulSoup, повторяющий её синтаксис. Что касается скорости, LxmlSoup превосходит BeautifulSoup в 2 раза, за счёт библиотеки lxml под капотом. Мы будем парсить сайт который являлся моей первой задачей - Sunlight. Тогда я ещё мало что понимал в программировании и парсинге, зато было море желания научиться этому полезному навыку. Итак, поехали!

    Пример скорости библиотек 0.7749056816101074 - LxmlSoup 1.4368107318878174 - BeautifulSoup

    Начало парсинга

    Для начала импортируем библиотеки requests и LxmlSoup. Requests используется для получения html кода сайта, в котором находится вся нужная информация для извлечения.

    from LxmlSoup import LxmlSoup import requests

    После сделаем GET запрос к сайту с целью получения html кода.

    html = requests.get('https://sunlight.net/catalog').text

    Далее создаём экземпляр объекта LxmlSoup в который передаём переменную html. Это нужно, так как LxmlSoup является классом хранящим в себе нужные нам методы для извлечения информации из html кода.

    soup = LxmlSoup(html)

    Теперь обратим внимание на структуру html. Для того чтобы извлечь ссылку на товар нужно за что-то зацепиться в коде сайта. В данном случае мы можем сделать это за тэг элемента и его класс.

    В данном случае тэг является

    Также нужно применить метод .get в котором мы передаем строку "href", для извлечения содержимого переменной href.

    links = soup.find_all('a', class_='cl-item-link js-cl-item-link js-cl-item-root-link')

    Теперь пройдемся циклом по полученным элементам и извлечем их url.

    for link in links: url = link.get("href")

    Наконец получим html блок с ценой и наименованием товара, которые извлечём с помощью метода .text() и красиво выведем все имеющееся в терминал.

    for i, link in enumerate(links): url = link.get("href") # получаем ссылку товара name = link.text() # извлекаем наименование из блока со ссылкой price = soup.find_all("div", class_="cl-item-info-price-discount")[i].text() # извлекаем цену print(i) print(f"Url - ") print(f"Name - ") print(f"Price - \n")

    Вот полный код получившегося парсера:

    from LxmlSoup import LxmlSoup import requests html = requests.get('https://sunlight.net/catalog').text # получаем html код сайта soup = LxmlSoup(html) # создаём экземпляр класса LxmlSoup links = soup.find_all('a', class_='cl-item-link js-cl-item-link js-cl-item-root-link') # получаем список ссылок и наименований for i, link in enumerate(links): url = link.get("href") # получаем ссылку товара name = link.text() # извлекаем наименование из блока со ссылкой price = soup.find_all("div", class_="cl-item-info-price-discount")[i].text() # извлекаем цену print(i) print(f"Url - ") print(f"Name - ") print(f"Price - \n") 

    На этом всё. Вы можете записать данные в файлы и использовать их для своих целей. Это была моя первая статья на Хабре, и если вы ждете продолжения будущей серии статей или у Вас есть какие-то замечания, то пишите, постараюсь ответить и прислушаюсь. До новых встреч!

    Почему стоит научиться «парсить» сайты, или как написать свой первый парсер на Python

    image

    Для начала давайте разберемся, что же действительно означает на первый взгляд непонятное слово — парсинг. Прежде всего это процесс сбора данных с последующей их обработкой и анализом. К этому способу прибегают, когда предстоит обработать большой массив информации, с которым сложно справиться вручную. Понятно, что программу, которая занимается парсингом, называют — парсер. С этим вроде бы разобрались.

    Перейдем к этапам парсинга.

    • Поиск данных
    • Извлечение информации
    • Сохранение данных

    И так, рассмотрим первый этап парсинга — Поиск данных.

    Так как нужно парсить что-то полезное и интересное давайте попробуем спарсить информацию с сайта work.ua.
    Для начала работы, установим 3 библиотеки Python.

    pip install beautifulsoup4

    Без цифры 4 вы ставите старый BS3, который работает только под Python(2.х).

    pip install requests
    pip install pandas

    Теперь с помощью этих трех библиотек Python, можно проанализировать нашу веб-страницу.

    Второй этап парсинга — Извлечение информации.

    Попробуем получить структуру html-кода нашего сайта.
    Давайте подключим наши новые библиотеки.

    import requests from bs4 import BeautifulSoup as bs import pandas as pd 

    И сделаем наш первый get-запрос.

    URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2" r = requests.get(URL_TEMPLATE) print(r.status_code) 

    Статус 200 состояния HTTP — означает, что мы получили положительный ответ от сервера. Прекрасно, теперь получим код странички.

    print(r.text) 

    Получилось очень много, правда? Давайте попробуем получить названия вакансий на этой страничке. Для этого посмотрим в каком элементе html-кода хранится эта информация.

    Комірник

    У нас есть тег h2 с классом «add-bottom-sm», внутри которого содержится тег a. Отлично, теперь получим title элемента a.

    soup = bs(r.text, "html.parser") vacancies_names = soup.find_all('h2', class_='add-bottom-sm') for name in vacancies_names: print(name.a['title']) 

    Хорошо, мы получили названия вакансий. Давайте спарсим теперь каждую ссылку на вакансию и ее описание. Описание находится в теге p с классом overflow. Ссылка находится все в том же элементе a.

    Some information about vacancy.

    Получаем такой код.

    vacancies_info = soup.find_all('p', class_='overflow') for name in vacancies_names: print('https://www.work.ua'+name.a['href']) for info in vacancies_info: print(info.text) 

    И последний этап парсинга — Сохранение данных.

    Давайте соберем всю полученную информацию по страничке и запишем в удобный формат — csv.

    import requests from bs4 import BeautifulSoup as bs import pandas as pd URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2" FILE_NAME = "test.csv" def parse(url = URL_TEMPLATE): result_list = r = requests.get(url) soup = bs(r.text, "html.parser") vacancies_names = soup.find_all('h2', class_='add-bottom-sm') vacancies_info = soup.find_all('p', class_='overflow') for name in vacancies_names: result_list['href'].append('https://www.work.ua'+name.a['href']) result_list['title'].append(name.a['title']) for info in vacancies_info: result_list['about'].append(info.text) return result_list df = pd.DataFrame(data=parse()) df.to_csv(FILE_NAME) 

    После запуска появится файл test.csv — с результатами поиска.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *