Подробный вводный курс по парсингу на Python 2026 года

В этом бесплатном курсе вы шаг за шагом научитесь собирать данные с веб-сайтов с помощью Python. Мы начнём с основ парсинга (скрапинга) – автоматического сбора информации с веб-страниц – и постепенно перейдём к более продвинутым темам. Материал разбит на модули с понятными примерами и практическими заданиями после каждой темы. Курс рассчитан на начинающих и продолжающих (junior/middle) разработчиков, знакомых с основами Python.

Что вы узнаете в ходе курса:

  • Как работают веб-страницы и что такое HTML-дерево (DOM)
  • Как отправлять запросы к сайтам и получать HTML-код страниц
  • Как применять библиотеку BeautifulSoup для извлечения данных из HTML
  • Как анализировать структуру HTML (DOM-дерево) и эффективно находить нужные элементы
  • Как обходить защиту сайтов от парсинга, включая различные CAPTCHA и другие антибот механизмы
  • Как собирать большие объёмы данных с сайтов, соблюдать правила и не получать баны
  • Как парсить данные с динамических сайтов, где контент подгружается JavaScript’ом
  • Как использовать Selenium для автоматизации действий браузера (навигация, клики, ввод данных)
  • Как собирать данные из Telegram: получать сообщения из чатов, каналов и групп, а также извлекать списки участников
  • Лучшие практики разработки парсеров: как спроектировать своего паука, хранить данные и отлаживать код
  • Получите множество примеров кода парсеров (код предоставляется в каждом модуле), которые можно сохранить себе как шпаргалку для будущих проектов.

Каждый модуль содержит короткие теоретические блоки и практические упражнения, имитирующие реальные задачи. Выполняя эти задания, вы закрепите теорию на практике, как на интерактивном тренажёре. По завершении курса у вас сформируется прочное понимание того, как писать собственные веб-парсеры на Python.


🔥 MACHINE LEARNING — мой экспертный канал, посвященный ИИ и машинное обучение, больше уроков в нем.
🔝 А здесь мы собрали целый кладезь полезных Python ресурсов для прокачки.


Модуль 1: Введение в веб-парсинг и основные инструменты

Описание: В первом модуле мы познакомимся с понятием веб-парсинга. Разберём, как устроены сайты изнутри, что такое HTML и DOM-дерево, и почему для извлечения данных со страницы нужен специальный подход. Затем установим основные инструменты: библиотеку для HTTP-запросов (requests) и парсер HTML BeautifulSoup (библиотека bs4). Научимся делать простейший парсер: отправлять запрос к сайту, получать HTML-код страницы и извлекать из него нужную информацию.

Ключевые темы:

  • Что такое веб-парсинг (скрапинг) и для каких задач он применяется. Примеры реального использования парсеров: мониторинг цен, сбор новостей, анализ отзывов и т.д.
  • Краткий обзор HTTP: как браузер получает страницу (HTTP-запросы GET/POST, коды ответов). Почему важно учитывать заголовки (User-Agent, Cookies) при парсинге.
  • Структура веб-страницы: HTML-код и DOM. Понятие тегов, атрибутов, вложенности элементов. HTML-дерево: узлы-элементы (теги) и текстовые узлы, иерархия <html><head> / <body> и т.д.
  • Установка и использование библиотеки Requests для получения HTML. Пример: выполняем requests.get("https://example.com") и получаем response.text с HTML-контентом страницы.
  • Библиотека BeautifulSoup: разбор HTML-кода в удобную структуру Python. Подключение (from bs4 import BeautifulSoup) и создание объекта супа: soup = BeautifulSoup(html, 'html.parser'). Обзор основных методов: soup.find() (поиск первого подходящего элемента), soup.find_all() (поиск всех элементов по критерию), soup.get_text() или .text (извлечение видимого текста), доступ к атрибутам (например, element['href'] для ссылки).
  • Поиск элементов по тэгу, по классу, по id. Например, как вытащить заголовок страницы: soup.find('h1'), как найти элемент по CSS-классу: soup.find('div', {'class': 'product-name'}). Использование цепочки методов или CSS-селекторов: soup.select('div.product > span.price') для более сложных структур.
  • Пример простого парсера: напишем скрипт, который берет страницу (например, главную страницу Python.org или любой простой сайт) и извлекает заголовок страницы <title> и все ссылки <a href="..."> на ней.

Пример кода: ниже приведён упрощённый пример парсера, который получает заголовок страницы и первую ссылку:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"
response = requests.get(url)                   # Отправляем GET-запрос
html = response.text                           # Получаем HTML-текст страницы
soup = BeautifulSoup(html, 'html.parser')      # Разбираем HTML с помощью BS4

title = soup.find('title').text                # Находим тег <title> и берем его текст
print("Title of page:", title)

first_link = soup.find('a')                    # Ищем первый тег <a> (ссылку)
if first_link:
    print("First link URL:", first_link.get('href'))

Этот скрипт выводит заголовок HTML-страницы и URL первой найденной ссылки. С помощью подобных шагов можно доставать любые элементы страницы.

Практические задания:

  1. Парсинг заголовка и описания. Напишите скрипт, который загружает содержимое страницы по URL (используйте requests) и с помощью BeautifulSoup извлекает текст заголовка страницы (<h1> или <title>) и первый абзац текста (<p>). Запустите скрипт на нескольких сайтах и выведите полученные заголовки и абзацы.
  2. Сбор всех ссылок. Реализуйте парсер, который собирает все URL ссылок с заданной страницы. То есть, пройтись по всем тегам <a> и сохранить значения атрибута href в список. Проверьте работу программы на сайте example.com или другом небольшом сайте, распечатайте список ссылок.
  3. Поиск по классу. Возьмите новостную страницу или блог (например, новость на сайте BBC или habr.com) и извлеките с неё текст статьи. Для этого найдите контейнер статьи (например, <div class="article-body"> или похожий блок) – в коде страницы посмотрите класс основного блока с текстом. Используйте soup.find с нужным именем класса, затем получите чистый текст без HTML через .get_text(). Выведите первые 200 символов текста статьи.
  4. Обработка ошибок. Дополните парсер из задания 1 или 2 проверкой на ошибки: что будет, если страница недоступна (код ответа не 200) или soup.find не нашёл нужный элемент? Добавьте соответствующие проверки и вывод сообщений (например, “Элемент не найден” или код ошибки).

Модуль 2: Анализ HTML-дерева и методы парсинга данных

Описание: В этом модуле углубимся в структуру HTML-документа и научимся более эффективно вытаскивать данные, даже если они находятся глубоко в структуре страницы. Рассмотрим, как навигировать по DOM-дереву: находить родительские, дочерние и соседние узлы, разбирать сложные вложенные структуры (списки, таблицы, меню). Узнаем про разные парсеры, с которыми работает BeautifulSoup (html.parser, lxml, html5lib) и когда какой лучше применять. Также поговорим о синтаксическом анализе HTML-дерева – по сути, о том, как программно понимать и обходить HTML как древовидную структуру.

Ключевые темы:

  • DOM-дерево в деталях: узел (node), родитель, дочерние элементы, листья. Как HTML-код преобразуется в дерево: каждый тег становится узлом Element, вложенные теги – дочерними узлами. Текст внутри тегов – отдельные текстовые узлы. Пример разбора: <ul><li>Item1</li><li>Item2</li></ul> превращается в дерево: узел <ul> с двумя дочерними <li>, каждый из которых содержит текстовый узел.
  • Навигация по дереву с BeautifulSoup: свойства .contents (список дочерних элементов), .parent (родительский узел), .find_next_sibling() и .find_previous_sibling() (соседние элементы на одном уровне). Как перебрать, например, все элементы списка: найти <ul> и пройтись по ul.contents или через find_all('li').
  • CSS-селекторы и метод .select(): альтернативный способ поиска. Например, soup.select('div.content p.description') найдёт все <p class="description"> внутри <div class="content">. Селекторы позволяют точно указывать вложенность и классы, аналогично тому, как в CSS стили назначаются. Это мощный инструмент для сложных структур.
  • Извлечение табличных данных: как спарсить HTML-таблицу (теги <table>, <tr>, <td>). Стратегия: найти таблицу по id или заголовку, затем итерировать по строкам tr и извлекать ячейки td. Сохранение результатов, например, в список списков или CSV-файл.
  • Поиск элементов без прямого указания класса или id: если страница не имеет удобных идентификаторов, можно искать по содержимому или по позиции. Пример: найти ссылку по тексту (soup.find('a', string="Нужный текст")) или найти следующий элемент после определённого заголовка. Использование комбинации методов: найти заголовок, потом find_next для следующего блока.
  • Обработка нечистого HTML: часто HTML-разметка содержит ошибки. BeautifulSoup достаточно устойчив к этому, но в сложных случаях можно использовать парсер lxml (установив lxml и указав BeautifulSoup(html, 'lxml')), который лучше справляется с некорректной разметкой. Обзор, когда имеет смысл сменить парсер.
  • Пример задачи: разберём пример, как, имея HTML-страницу интернет-магазина, извлечь все названия товаров и их цены. Для этого мы найдём контейнер с товарами, затем внутри него для каждого товара найдём название (например, тег <h2> или <a> с названием) и цену (спан с классом “price”). Показана техника поиска внутри ограниченной области: сначала soup.find_all('div', class_='product-item') для списка товаров, потом внутри каждого элемента ищем нужные подэлементы.

Пример кода: фрагмент кода, демонстрирующий навигацию по DOM. Предположим, у нас есть HTML-фрагмент списка комментариев, где комментарии находятся внутри <div class="comment">:

# Получаем все блоки комментариев на странице
comments = soup.find_all('div', class_='comment')

for comment in comments:
    author = comment.find('span', class_='author').text   # находим имя автора внутри комментария
    content = comment.find('p', class_='content').text    # находим текст комментария
    date = comment.find('span', class_='date').text       # дата/время комментария

    # Выводим или сохраняем информацию о каждом комментарии
    print("Комментарий от", author, "(", date, "):", content[:50], "...")

В этом примере мы сначала собрали все элементы, соответствующие одному шаблону (div.comment), а затем для каждого такого элемента извлекли вложенные данные. Это демонстрирует принцип синтаксического анализа HTML-дерева: мы понимаем вложенность тегов и в коде отражаем эту структуру, последовательно опускаясь на нужный уровень.

Практические задания:

  1. Извлечение заголовков и списков. Возьмите HTML-код (можно сохранить локально небольшой фрагмент) с вложенной структурой, например: <div class="section"> <h2>Раздел 1</h2> <ul> <li>Пункт А</li> <li>Пункт B</li> </ul> </div> <div class="section"> <h2>Раздел 2</h2> <ul> <li>Пункт C</li> <li>Пункт D</li> </ul> </div> Напишите код, который пройдётся по всем блокам div.section и выведет заголовок раздела (<h2>) и список пунктов внутри него. То есть итоговый вывод может быть: Раздел 1: Пункт А, Пункт B Раздел 2: Пункт C, Пункт D Реализуйте извлечение элементов списка через find_all('li') для каждого раздела.
  2. Парсинг таблицы. Найдите в интернете HTML-страницу с таблицей (например, страница со статистикой или расписанием) либо создайте свою. Используя BeautifulSoup, напишите скрипт, который считывает эту страницу и превращает таблицу в структурированные данные. Выведите, например, каждую строку таблицы как словарь или как список значений. Убедитесь, что корректно обрабатываются заголовки столбцов (теги <th>). Для усложнения: сохраните результаты в CSV-файл (можно использовать модуль csv).
  3. Навигация по соседям. Иногда нужные данные находятся рядом друг с другом, но не вложены. Например, структура: <h3>Название</h3> <p>Описание элемента</p> <h3>Другое название</h3> <p>Другое описание</p> Здесь каждое описание находится не внутри заголовка, а сразу после него. Напишите скрипт, который найдёт все теги <h3> на странице, и для каждого заголовка получит текст его соседнего абзаца <p> (можно использовать метод find_next_sibling() у заголовка). Выведите пары “Название – Описание” для всех таких элементов.
  4. Обход DOM-дерева вручную (творческое задание). Напишите функцию, которая рекурсивно обходит DOM-дерево страницы и выводит структуру вложенности тегов с отступами. Например, для простой страницы она может вывести: html head title body div (class=section) h1 p ul li li ... Это задание повышенной сложности и носит скорее творческий характер: вам нужно использовать свойства .children или .contents и рекурсию, чтобы распечатать названия тегов и, например, их основные атрибуты. Оно поможет глубже понять устройство DOM. (Подсказка: можно ограничиться несколькими уровнями вложенности, чтобы не получить слишком длинный вывод).

Модуль 3: Обход защиты от парсинга – CAPTCHA и другие барьеры

Описание: На практике многие сайты пытаются защититься от автоматического сбора данных. Один из распространённых способов – показывать CAPTCHA (капчу), чтобы убедиться, что перед сайтом человек, а не скрипт. В этом модуле мы разберём, какие бывают виды капч и как их решать или обходить при парсинге. Помимо капч, сайты могут использовать и другие антибот-методы: блокировать частые запросы, требовать авторизацию, проверять User-Agent, и т.д. Мы обсудим эти препятствия и стратегии, как их преодолеть.

Ключевые темы:

  • Виды CAPTCHA: текстовые капчи (изображение с искаженными символами), капчи с выбором объектов (например, Google reCAPTCHA: “выберите все картинки с автобусами”), математические задачи, слайдеры, и др. Отдельно популярная reCAPTCHA v2/v3, hCaptcha, etc. Чем более сложная капча, тем труднее её решить автоматически.
  • Как работают капчи: понимание, что капча генерируется так, чтобы ботам было трудно её распознать (например, искажение текста сложное для OCR, или вообще требует распознавания объектов на изображении, что под силу только продвинутым алгоритмам или человеку). Реализация на стороне сайта: при подозрительной активности сервер может вернуть страницу с капчей или JavaScript, который её загружает.
  • Стратегии обхода капч:
    • Ручное решение: самый прямой путь – остановить автоматизацию и вывести капчу человеку для решения. Например, если парсер обнаружил, что вернулась страница с капчей, можно оповестить оператора, показав изображение, и вручную ввести ответ. В коде это можно реализовать, сохранив капчу на диск и ожидая ввода с клавиатуры. Однако это снижает автоматизацию.
    • Использование внешних сервисов: существуют специальные сервисы (Anti-Captcha, RuCaptcha (2Captcha), CapMonster и др.), где живые люди или алгоритмы решают капчи за небольшую плату. Парсер через API отправляет изображение капчи или токен reCAPTCHA и получает ответ (текст или необходимый ключ). Мы рассмотрим, как подключиться к подобным сервисам: например, используя библиотеку anticaptchaofficial или просто отправляя HTTP-запросы к API 2Captcha.
    • Machine Learning/OCR: для простых текстовых капч можно попробовать использовать OCR (оптическое распознавание символов) – например, библиотеку pytesseract для распознавания текста из картинки. Но успешность зависит от сложности капчи. Обсудим, когда имеет смысл (например, старые капчи с 5 буквами без сильных искажений могут распознаваться). Для сложных картинок или тем более reCAPTCHA — ML-решения намного сложнее и обычно не оправданы в рамках простого парсера.
    • Обход reCAPTCHA: reCAPTCHA от Google и аналогичные обычно невозможно решить программно без обращения к сервисам, потому что они требуют либо взаимодействия (нажатие чекбокса и решение картинок), либо имеют скрытый анализ поведения пользователя (v3). Стратегии: либо использовать Selenium и платный сервис для решения (например, обойти v2 можно автоматизировав ввод ответа, полученного от 2Captcha), либо искать пути избегать её появления (о чём ниже).
  • Другие антибот-методы и их обход:
    • Ограничение по IP (rate limiting): сайт может блокировать слишком частые запросы с одного IP (HTTP 429 Too Many Requests, либо временно банит). Решение: ротация прокси – использовать пул разных IP-адресов. Рассказываем, как в requests можно задать proxies и менять IP на каждый запрос. Предостережение: бесплатные прокси часто ненадежны, лучше использовать платные или собственные.
    • Проверка заголовков и поведения: простой бот может забыть прислать User-Agent или Cookies – это сразу выдаёт его. Решение: всегда указывать реалистичный User-Agent (например, браузера) в заголовках запросов. Использование requests.Session для сохранения cookies (так бот будет вести себя более как браузер, сохраняя сессию). Иногда помогает небольшая задержка между запросами (time.sleep()), чтобы не выглядеть как сверхскоростной бот.
    • Динамические проверки (JS-тесты): некоторые сайты дают при подозрении страницу, которая требует выполнить немного JavaScript (например, Cloudflare challenge) – без этого не покажут контент. В таких случаях можно либо использовать Selenium (реальный браузер выполнит скрипт), либо некоторые специальные решения вроде библиотек, умеющих имитировать это (например, облачные провайдеры как ScrapingAnt или undetected-chromedriver).
    • Требование авторизации: данные могут быть доступны только залогиненным пользователям. Тут нет универсального обхода – надо либо зарегистрировать учётную запись и в парсере осуществить логин (например, отправить POST запрос с логином/паролем, или через Selenium заполнить форму), либо если невозможно – увы, не получить данные. Мы рассмотрим пример, как можно логиниться через requests (сначала GET формы логина, потом POST с данными + сохранить cookies).
  • Этичность и правовые аспекты: кратко упомянем, что парсинг открытых данных обычно законен, но обход средств защиты (особенно авторизаций, капч) может нарушать правила сайта. Нужно быть осторожным и всегда проверять “robots.txt” и пользовательские соглашения. Этот курс технический, но ответственность на исполнителе.

Пример (псевдо)решения капчи: Представим, что на сайте при парсинге вернулась картинка с капчей (текст из цифр). Псевдокод решения через сервис 2Captcha может выглядеть так (для понимания):

import requests
API_KEY = "ВАШ_API_КЛЮЧ_2CAPTCHA"
# 1. Отправляем изображение капчи на распознавание
with open("captcha_image.png", "rb") as f:
    response = requests.post("http://2captcha.com/in.php", data={"key": API_KEY, "method": "post"}, files={"file": f})
captcha_id = response.text.split('|')[1]  # получаем ID решаемой капчи

# 2. Периодически проверяем результат
captcha_text = None
check_url = f"http://2captcha.com/res.php?key={API_KEY}&action=get&id={captcha_id}"
for i in range(10):
    r = requests.get(check_url)
    if r.text == "CAPCHA_NOT_READY":
        time.sleep(5)
        continue
    elif r.text.startswith("OK|"):
        captcha_text = r.text.split('|')[1]
        break

if captcha_text:
    print("Решение капчи:", captcha_text)
    # далее можно использовать этот текст для подстановки в форму или запрос

Конечно, для разных видов капч API вызовы могут отличаться (например, reCAPTCHA v2 требует отправки специального sitekey и получения токена). Но общий подход демонстрируется: интеграция со сторонним сервисом.

Практические задания:

  1. Анализ страницы на наличие капчи. Выберите сайт, известный наличием капчи (например, сайт, который после нескольких запросов выдаёт reCAPTCHA). Попробуйте написать скрипт, который выполняет несколько запросов подряд к этому сайту (например, 5-10 запросов к какому-то публичному URL). Проверьте, возвращается ли страница капчи (по содержимому: возможно, она содержит <div class="g-recaptcha"> или слово “captcha”). Если да, запрограммируйте в скрипте обнаружение этой ситуации и соответствующее сообщение (print("Требуется ввод капчи!")). Это упражнение научит определять, когда парсинг упёрся в капчу.
  2. Настройка заголовков и задержек. Модифицируйте свой парсер (например, из Модуля 1 или 2) для более “человечного” поведения:
    • Установите заголовок User-Agent как у браузера (например, "Mozilla/5.0 ...").
    • Между запросами вставьте случайную паузу в диапазоне 1-3 секунды.
    • Используйте requests.Session() вместо одиночных запросов, чтобы сохранять куки.
      Протестируйте на сайте, который раньше блокировал вас (если был) – стало ли лучше? Это задание не имеет точного критерия успеха, но оно закрепит навыки настройки HTTP-запросов.
  3. Работа с прокси. Зарегистрируйтесь на бесплатном прокси-сервисе или возьмите несколько бесплатных прокси из открытых списков. Напишите код, который делает последовательные запросы через список прокси (пример использования requests.get(url, proxies={"http": proxy, "https": proxy})). Проверьте на сервисе httpbin.org/ip, что ваш IP меняется. Попробуйте организовать цикл, где при получении ошибки от прокси он переключается на следующий. Этот эксперимент покажет, как можно сменой IP обходить ограничения. (Будьте осторожны: бесплатные прокси ненадёжны и медленны, главное – понять принцип.)
  4. Исследование 2Captcha (теоретическое). Без необходимости оплачивать сервис, изучите документацию любого сервиса по решению капч (2Captcha, Anti-Captcha и т.п.). Поймите, какие шаги нужны, чтобы решить, например, reCAPTCHA v2: вам потребуется отправить sitekey (ключ сайта) и URL страницы на сервер решения, а потом получить токен. Опишите эти шаги в виде алгоритма (можно просто комментариями или псевдокодом). Это задание поможет структурировать понимание процесса обхода сложных капч.

Модуль 4: Сбор и обработка больших объемов данных

Описание: В реальных задачах парсингу часто подвергаются сотни тысяч страниц – например, нужно собрать информацию о всех товарах из каталога или выгрузить все посты форума. Этот модуль посвящён тому, как организовать парсинг больших данных эффективно и безопасно. Мы обсудим, как не упереться в ограничения по времени и памяти, как хранить и обрабатывать результаты, и как ускорить парсинг с помощью многопоточности или асинхронности. Также затронем вопрос соблюдения вежливости (не перегружать чужой сервер, следовать robots.txt).

Ключевые темы:

  • Разбиение задачи на части: если нужно спарсить 100 тысяч страниц, нельзя делать это бездумно подряд в один поток – это будет очень долго и, возможно, приведёт к блокировке. Стратегия: разбивать по разделам или диапазонам. Например, парсить каталог сайта по категориям, делать паузы между крупными сериями запросов, по возможности использовать API, если оно есть для части данных.
  • Автоматизация сбора множества страниц: генерация списков URL. Часто страницы имеют шаблон URL (например, page=1, page=2 для пагинации). Научимся автоматически формировать список ссылок для обхода. Например: urls = [f"https://example.com/products?page={i}" for i in range(1, 101)] для 100 страниц каталога. Или вытаскивать ссылки на статьи из оглавления и потом обходить каждую. Важный момент: избегать дублирования и зацикливания (например, проверять, не обработана ли ссылка ранее).
  • Хранение результатов парсинга: рассмотрим варианты, куда складывать большие объёмы данных. На первых порах это могут быть файлы (CSV, JSON) – научимся записывать данные построчно, чтобы не держать всё в памяти. Для действительно больших данных – базы данных (SQLite, PostgreSQL): прям в парсере можно делать INSERT, но нужно быть аккуратным с частыми вставками (можно собрать батчами или использовать ORM).
  • Обработка ошибок и надежность: на длинной серии запросов непременно что-то пойдёт не так – соединение разорвано, какая-то страница не отвечает, или формат неожиданно отличается. Важна устойчивость: блок try/except вокруг запроса (например, повторить запрос до 3 раз при таймауте), логирование пропущенных страниц в файл, чтобы потом повторно их обработать. При парсинге больших данных нужно предусмотреть перезапуск скрипта с места остановки: например, хранили прогресс (какую страницу уже обработали).
  • Повышение скорости: многопоточность vs асинхронность: если сайт позволяет, можно посылать несколько запросов параллельно. Два подхода:
    • Многопоточность/мультипроцессность: запуск нескольких потоков (ThreadPool) или процессов (ProcessPool) в Python, каждый из которых парсит свою часть. Это может ускорить загрузку, особенно для I/O-bound задач (сетевые запросы), но Python GIL ограничивает потоки на CPU-bound, однако в нашем случае основное время – ожидание сети, так что потоки помогают. Воспользуемся модулем concurrent.futures или threading. Пример: создать пул из 5 потоков, и раздать им список URL для скачивания.
    • Асинхронный парсинг: использование asyncio и библиотеки aiohttp (асинхронного аналога requests). В асинхронном подходе мы не создаём реальных параллельных потоков, но благодаря неблокирующему вводу-выводу можем сразу держать много соединений. Например, можно одновременно отправить 100 запросов и обрабатывать их по мере поступления ответов. Мы объясним принципы async/await в Python и покажем простой пример асинхронного парсера. (Подробнее про асинхронность – в следующем модуле).
  • Вежливость и ограничения: если собираете большие данные, уважайте сервер:
    • Читайте robots.txt – возможно, сайт прямо запрещает автоматический сбор некоторых разделов.
    • Не шлите 100 запросов в секунду без согласования – сервер может легитимно вас забанить. Лучше ограничить скорость (например, не более 1-2 запросов в секунду потоком, или использовать asyncio.Semaphore чтобы ограничить одновременные запросы).
    • Если требуется очень большой объём данных, рассмотрите официальные выгрузки или API – иногда есть возможность скачать данные оптом (например, сайт может предоставлять файлы экспорта). В реальных проектах это предпочтительнее парсинга.

Пример подхода к большим данным: Допустим, нам нужно собрать информацию о 10,000 товарах с сайта. Мы можем:

  1. Получить все ссылки на товары, пройдясь по страницам каталога (скажем, 100 страниц по 100 товаров).
  2. Сохранить список URL товаров.
  3. Запустить многопоточный парсер, который берет по очереди ссылку из списка и скачивает страницу товара, парсит её (название, цена, рейтинг и т.п.) и сразу записывает результат в файл/БД, затем берёт следующую.
  4. Контролировать прогресс: каждые N товаров выводить сообщение или писать в лог. Если скрипт упал, мы знаем с какого ID продолжить.

Пример кода (многопоточный парсинг фрагмент):

import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor

product_urls = [...]  # список ссылок на товары (предположим, мы его уже получили)

def fetch_product(url):
    try:
        r = requests.get(url, timeout=5)
        if r.status_code != 200:
            return None
        soup = BeautifulSoup(r.text, 'html.parser')
        title = soup.find('h1').text.strip()
        price = soup.find('span', class_='price').text.strip()
        return {"url": url, "title": title, "price": price}
    except Exception as e:
        print(f"Ошибка при обработке {url}: {e}")
        return None

results = []
with ThreadPoolExecutor(max_workers=5) as executor:
    for data in executor.map(fetch_product, product_urls):
        if data:
            results.append(data)
            # Тут можно сразу писать данные в файл, чтобы не держать все в памяти

print("Получено товаров:", len(results))

В этом коде мы создаём пул из 5 потоков, которые параллельно выполняют функцию fetch_product для каждой ссылки. Таким образом, обработка множества товаров идёт быстрее, чем последовательно.

Практические задания:

  1. Парсинг с возобновлением. Смоделируйте ситуацию прерываемого парсинга. Например, нужно скачать 50 страниц. Организуйте цикл по списку страниц, но сделайте так, чтобы после обработки каждой страницы скрипт записывал номер последней успешно обработанной страницы в файл (например, progress.txt). Затем “случайно” прервите выполнение (можно искусственно break после, скажем, 20 страниц). Модифицируйте скрипт так, чтобы при следующем запуске он читал progress.txt и начинал парсинг со следующей страницы. Это упражнение приучит делать устойчивые процессы для больших объёмов.
  2. Многопоточный сбор ссылок. Возьмите сайт с пагинацией (напр. форум или каталог) и попробуйте собрать ссылки со всех страниц раздела, используя ThreadPoolExecutor. Измерьте время выполнения с потоками (например, 5 рабочих) и без (последовательно) для ~50 страниц. Выведите время или количество секунд, затраченных в обоих случаях. Сделайте вывод, насколько ускорилось. (Учтите: если сайт отвечает очень быстро или, наоборот, ограничивает параллельные коннекшены, результат может быть разным.)
  3. Асинхронный парсинг (базовый). Напишите простой асинхронный парсер для примера. Используйте библиотеку aiohttp (не забудьте установить). Задача: есть список из, допустим, 20 URL (можно взять https://httpbin.org/delay/2 для теста, которое отвечает с задержкой 2 секунды). Нужно отправить запросы ко всем им параллельно. Используйте asyncio и aiohttp.ClientSession внутри async def функции. Соберите результаты (например, статус код или небольшой JSON ответ) и выведите их. Сравните суммарное время выполнения асинхронной версии со временем при последовательном requests.get – асинхронно должно быть значительно быстрее для множества внешних запросов.
  4. Ограничение скорости. Если у вас есть опасения, что асинхронный или многопоточный парсер может слишком нагружать сайт, попробуйте внедрить ограничение: в асинхронном варианте используйте asyncio.Semaphore(5) для не более 5 одновременных задач; в потоковом – делайте time.sleep(1) в потоке после запроса. Экспериментируйте с разными значениями и посмотрите, как это влияет на скорость сбора 30-50 страниц. Это поможет понять баланс между скоростью и безопасностью.

Модуль 5: Парсинг динамических сайтов (AJAX и SPA)

Описание: Часто страницы, которые вы хотите спарсить, не содержат нужных данных в исходном HTML, потому что контент подгружается динамически с помощью JavaScript. Например, лента соцсети или сайт с картой, где данные приходят через AJAX-запросы после загрузки страницы. Обычный подход с requests + BeautifulSoup тут не помогает, ведь он получает только начальный HTML (без данных, которые появились позже). В этом модуле мы научимся выявлять такие случаи и разберём подходы к парсингу динамических сайтов. Основной инструмент – Selenium, позволяющий управлять полноценным браузером через Python и получать уже отрендеренный контент. Также обсудим альтернативы (например, находить скрытое API-запросы, которые делает JS, и вызывать их напрямую).

Ключевые темы:

  • Признаки динамической загрузки: как понять, что сайт подгружает данные после загрузки? Признаки: в исходном HTML (через “Просмотр кода”) не видно ожидаемых данных; присутствуют скрипты, выполняющие запросы; при прокрутке страницы появляется новый контент (“infinite scroll”); или страница представляет собой SPA (Single Page Application) на фреймворках вроде React/Vue/Angular.
  • Инструменты для динамических сайтов:
    • Selenium – библиотека для управления браузером (Chrome, Firefox и др.) программно. С помощью Selenium можно открыть страницу, дождаться выполнения JS, взаимодействовать со страницей (нажимать кнопки, заполнять формы) и получить итоговый HTML или делать скриншоты.
    • Headless-браузеры: режимы, когда браузер запускается без графического интерфейса (для ускорения и работы на сервере). Chrome имеет headless-режим. Также есть Playwright и Puppeteer (JavaScript), но в Python обычно Selenium с ChromeDriver/GeckoDriver достаточно.
    • API-запросы: прежде чем запускать тяжёлый Selenium, стоит проверить: может, данные доступны через XHR/API, которые делает сама страница. В инструментах разработчика (Network) можно увидеть, какие запросы идут после загрузки. Если, скажем, обнаружится запрос к https://api.site.com/data?page=2 возвращающий JSON с нужной информацией, то можно обойтись без браузера – вызывать этот URL через requests, возможно с нужными заголовками. Мы научимся отслеживать такие запросы и эмулировать их.
  • Основы работы с Selenium:
    • Установка: скачивание вебдрайвера (ChromeDriver для Chrome или использование Selenium Manager), установка библиотеки selenium.
    • Запуск браузера: from selenium import webdriver driver = webdriver.Chrome() # или указать путь к драйверу driver.get("https://site.com") Selenium откроет окно браузера и загрузит страницу.
    • Ожидание загрузки: иногда нужно явно ждать, когда определённый элемент появится (особенно если данные грузятся через AJAX). Используем selenium.webdriver.support.ui.WebDriverWait и условия ожидания (например, элемент с id “content” присутствует). Либо простой time.sleep(х), хотя это менее гибко.
    • Получение HTML: page_html = driver.page_source – это HTML-код страницы после отработки JS. Его можно снова пихнуть в BeautifulSoup, если удобнее анализировать. Либо использовать селекторы Selenium (driver.find_element_by_css_selector и др.) для извлечения прямо через Selenium.
    • Интерактивные действия: driver.find_element_by_xpath(...) или по css, затем .click() чтобы нажать на кнопку, .send_keys("text") чтобы ввести текст в поле, driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") чтобы прокрутить страницу вниз (для подгрузки новых порций контента). Эти возможности делают Selenium универсальным инструментом не только парсинга, но и автоматизации любых действий на сайте.
  • Примеры задач, решаемых Selenium:
    • Парсинг сайта с бесконечной прокруткой (например, Twitter лента): можно написать скрипт, который будет несколько раз прокручивать страницу вниз (execute_script со scroll) и каждый раз ждать появления новых твитов, затем считывать HTML.
    • Прохождение SPA маршрутов: если сайт — одностраничное приложение, можно навигировать через driver.get("URL#hash") или через клики по интерфейсу, и получать HTML в разных состояниях.
    • Автоматическое логинирование: с Selenium можно зайти на страницу логина, найти поля username/password, заполнить, нажать Submit, и затем работать уже залогиненным (браузер хранит куки). Это полезно, когда нужно достать данные, доступные только внутри аккаунта.
    • Обход CAPTCHA с помощью Selenium: Selenium сам не решит капчу, но, например, reCAPTCHA v2 с чекбоксом – можно настроить парсер так, чтобы при появлении виджета вы остановили скрипт, самостоятельно решили капчу в открывшемся окне (показав, что вы человек), затем продолжили выполнение (Selenium продолжит работу с уже решённой капчей). Т.е. Selenium позволяет интегрировать ручное решение капчи в общий автоматизированный сценарий.

Пример кода (Selenium): Откроем страницу, нажмём кнопку “Показать ещё” и получим обновлённый контент:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Chrome()
driver.get("https://example.com/dynamic")

# Ждём 5 секунд, предполагая, что за это время контент загрузится
time.sleep(5)

# Ищем кнопку "Show more" и кликаем её
try:
    show_more_btn = driver.find_element(By.XPATH, "//button[text()='Show more']")
    show_more_btn.click()
    time.sleep(3)  # ждём подгрузки новых данных
except Exception as e:
    print("Кнопка 'Show more' не найдена или не кликнулась:", e)

# Теперь получаем весь HTML обновлённой страницы
html = driver.page_source

# Можно проанализировать через BS4 или напрямую:
if "New Content" in html:
    print("Новые данные появились на странице!")

driver.quit()  # закрываем браузер

Этот скрипт демонстрирует базовые приёмы Selenium: открытие страницы, поиск элемента по XPATH (в данном случае ищем кнопку по точному тексту), клик по элементу, ожидание, и получение HTML результата.

Практические задания:

  1. Определение динамичности. Выберите веб-страницу, которая, по вашему подозрению, загружает данные через JS (например, страница поиска на сайте, который подгружает результаты без перезагрузки, или лента соцсети). Откройте исходный код страницы (Ctrl+U) и поищите там содержимое, которое вы видите в браузере. Если его нет – страница динамическая. В задании опишите, как вы это определили. Далее, в DevTools Network проследите один из запросов за данными (XHR). Запишите URL или паттерн этого запроса. Это упражнение научит “диагностировать” динамические сайты.
  2. Получение данных через скрытое API. Продолжая с сайтом из задания 1: попробуйте эмулировать один из обнаруженных XHR-запросов. Например, если вы увидели, что при прокрутке уходит запрос на https://api.site.com/data?offset=..., попробуйте вызвать его через requests.get в отдельном скрипте, возможно, добавив нужные заголовки (User-Agent, авторизация, etc.). Получился ли осмысленный ответ (JSON, XML)? Если да – вы нашли способ без браузера получать данные. Выведите часть ответа. Если нет – возможно, API защищено (требует токена сессии), тогда отметьте, что без Selenium не обойтись.
  3. Парсинг с Selenium (базовый). Напишите Selenium-скрипт, который открывает страницу google.com, вводит поисковый запрос (например, “Selenium Python”) в поле поиска и имитирует нажатие Enter. Дождитесь загрузки результатов и получите HTML (или воспользуйтесь find_elements Selenium) чтобы извлечь заголовки результатов поиска (CSS-селектор для заголовков может быть что-то вроде h3). Выведите первые 5 заголовков результатов. Это упражнение поможет освоить ввод текста и извлечение информации через Selenium.
  4. Автопрокрутка Selenium. Выберите сайт с бесконечной лентой (например, Pinterest, Twitter, Instagram веб-версию или даже просто длинный список новостей) – что угодно, где нужно прокручивать. Напишите скрипт на Selenium, который будет несколько раз скроллить страницу вниз. Можно использовать: driver.execute_script("window.scrollBy(0, window.innerHeight);") в цикле, с небольшим time.sleep(2) после каждого скролла. После, скажем, 5 прокруток, соберите HTML (driver.page_source) и сохраните в файл. Откройте файл и проверьте: появились ли там элементы, которые изначально не грузились? (Например, новые изображения или посты). Таким образом, вы убедитесь, что автоматизация прокрутки позволяет получить динамически подгружаемые данные.

Модуль 6: Автоматизация действий в браузере (Selenium для различных задач)

Описание: Этот модуль расширяет использование Selenium: помимо получения данных, Selenium позволяет автоматизировать любые действия пользователя. Мы научимся писать скрипты, которые не просто читают страницы, но и выполняют определённые действия: ставят лайки, кликают кнопки, заполняют формы регистрации. Такие программы называют ботами (например, бот для автолайков, автозаполнения, или даже автоматической регистрации аккаунтов). Хотя основная тема курса – парсинг, умение автоматизировать действия тесно связано с ним (иногда нужно пройти сложный интерфейс, чтобы достать данные). Кроме того, написание таких ботов тренирует навыки работы с Selenium.

Ключевые темы:

  • Примеры автоматизации:
    • Автолайкер: Скрипт заходит в социальную сеть (предположим, авторизуется), переходит к списку постов и кликает “Like” у каждого подряд.
    • Автокликер: Бот для веб-игр или рекламных сайтов, который автоматически нажимает на появляющиеся объекты или ссылки.
    • Авточекер: Программа, которая периодически заходит на сайт (скажем, результаты экзаменов) и проверяет наличие изменений, уведомляя пользователя. Тут парсинг + периодическое действие.
    • Авторегистратор: Скрипт, который заполняет форму регистрации на сайте, придумывая уникальные данные (логин, email) и проходит весь процесс автоматически. Это может включать и капчу (в простых случаях).
    Эти примеры иллюстрируют широкий спектр задач, которые можно решить схожими методами.
  • Реализация через Selenium:
    • Поиск элементов формы (поля ввода, чекбоксы, выпадающие списки) – использование find_element с различными стратегиями (By.NAME, By.ID, By.CSS_SELECTOR) для доступа к формам.
    • Ввод текста: метод .send_keys("text") для полей ввода. При необходимости – очищать поле .clear(). Для нажатия специальных клавиш (Enter, Tab) используется from selenium.webdriver.common.keys import Keys.
    • Клики и навигация: .click() для кнопок, ссылок. Можно также напрямую делать driver.get(url) для перехода, но иногда важно именно кликнуть, чтобы эмулировать пользователя (например, некоторым сайтам важно, как вы пришли на страницу).
    • Действия мыши и клавиатуры: пакет selenium.webdriver.common.action_chains ActionChains позволяет, например, имитировать наведение курсора (hover) или drag-and-drop. Можно и простым .click() обходиться чаще всего, но знать о таких возможностях полезно.
    • Загрузка файлов: если бот должен, например, загрузить файл (клик по ссылке download) – Selenium по умолчанию откроет диалог браузера. Нужно настраивать профиль браузера (например, для Chrome задать опцию автосохранения скачиваемых файлов в заданную папку без диалога). Мы коснёмся того, что Selenium можно конфигурировать (через Options) – например, отключить GUI (headless), задать директорию для загрузок, включить/выключить изображения для скорости и т.д.
    • Ограничения Selenium: следует понимать, что Selenium – мощный, но тяжёлый инструмент. Запуск десятков браузеров параллельно – ресурсозатратно. Для массовых автоматизаций существуют облачные решения или специальные фреймворки. Однако для наших целей (учебных и единичных ботов) Selenium справится.
  • Практика: создание простого бота:
    • В качестве практики мы предложим, например, автоматизировать лайки в демо-соцсети или заполнить форму Google Forms программно.
    • Также полезно написать скрипт, который мониторит сайт: например, заходит каждые N минут и проверяет, появился ли новый элемент (обновился счетчик, вышла новая статья). Тут мы потренируем циклы и ожидания.
    • Ещё идея: скриншотер – Selenium умеет делать скриншот страницы (driver.save_screenshot("file.png")). Можно написать бота, который обходит список страниц и сохраняет скриншоты, например, сайта в разном состоянии (десктоп/мобайл и т.д.). Это демонстрирует не только сбор данных, но и визуальный контроль.

Пример кода (авторизация и лайки): Предположим, у нас есть тестовая социальная сеть http://somesocial.com. Приведём псевдокод бота, который заходит и лайкает последние 5 постов:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("http://somesocial.com/login")

# Входим на сайт
driver.find_element(By.NAME, "username").send_keys("myuser")
driver.find_element(By.NAME, "password").send_keys("mypassword")
driver.find_element(By.ID, "loginButton").click()
time.sleep(3)  # ждем перехода на главную после логина

# Ищем кнопки "Лайк" у первых 5 постов
like_buttons = driver.find_elements(By.CLASS_NAME, "like-button")[:5]
for btn in like_buttons:
    try:
        btn.click()
        time.sleep(1)  # небольшая пауза между лайками
    except Exception as e:
        print("Не удалось нажать лайк:", e)

print("Проставили лайки:", len(like_buttons))
driver.quit()

В этом примере бот:

  • Открывает страницу логина, вводит имя пользователя и пароль (находим поля по имени атрибута, кнопка по ID).
  • После клика на вход ожидает 3 секунды (в реальности стоит сделать WebDriverWait на появление чего-то на главной странице после входа).
  • Затем на главной находит все элементы с классом “like-button” (предположим, каждая такая кнопка соответствует посту) и кликает первые пять, ставя лайки.
  • Между кликами ставит задержку, чтобы не выглядеть мгновенным ботом.

Практические задания:

  1. Форма обратной связи. Создайте небольшую HTML-страницу с формой (например, файл test_form.html с полями “Имя”, “Email”, “Сообщение” и кнопкой отправить, которая никуда не отправляет). Откройте её через Selenium (используя driver.get("file:///полный/путь/до/test_form.html")). Напишите скрипт, который заполнит эту форму автоматом (например, введёт имя “Alice”, email “alice@example.com”, сообщение “Hello!”). По нажатию кнопки отправки можете просто сделать скриншот страницы. Это локальное упражнение, но оно закрепит работу с заполнением полей.
  2. Автоматический поиск и переход. Используя Selenium, зайдите на сайт Википедии. С помощью скрипта выполните поиск по какому-либо запросу (например, “Python (programming language)”). После перехода на страницу результата, скрипт должен нажать на ссылку “English” в боковой панели (сменить язык страницы). Затем сохранить HTML итоговой страницы. Таким образом, вы автоматизируете несколько шагов: поиск и изменение языка. Проверьте, что HTML сохранённой страницы соответствует английской версии статьи.
  3. Монитор изменений. Напишите Selenium-скрипт, который каждые 10 секунд проверяет, не изменился ли какой-то элемент на странице. Например, возьмите страницу, где есть меняющийся счетчик или время. Можно в качестве теста мониторить заголовок страницы (он вряд ли меняется, но для тренировки пойдёт). Либо использовать datetime.now() в HTML (если умеете вставить). Суть: скрипт в бесконечном цикле (или 5 итераций) перезагружает страницу driver.refresh(), вытаскивает, скажем, текст элемента, и сравнивает с предыдущим. Если изменилось – выводит “Обновление обнаружено”. Это упражнение тренирует обновление страницы и извлечение текста по расписанию. (Осторожно: не забывайте ставить time.sleep, чтобы не DoS-ить сайт бесконечными запросами.)
  4. Авто-скриншотер. Используя Selenium, напишите программу, которая принимает на вход список URL (можно захардкодить список из 3-5 сайтов). Для каждого URL открывает страницу и делает скриншот (driver.save_screenshot("site_name.png")). Попробуйте установить размер окна браузера driver.set_window_size(1200, 800) для консистентности. Проверьте, что скриншоты сохраняются и изображение соответствует каждой странице. Это практическое упражнение по использованию Selenium для сбора визуальной информации.

Модуль 7: Сбор данных из Telegram (чаты, группы и каналы)

Описание: Помимо веб-сайтов, ценным источником информации являются социальные сети и мессенджеры. В этом модуле мы рассмотрим парсинг данных из Telegram – популярного мессенджера, где множество открытых групп и каналов. Мы научимся собирать сообщения и посты из Telegram чатов/каналов, а также вытаскивать медиа (например, текст публикаций, ссылки на изображения). Для этого будем использовать возможности самого Telegram через API, а не “эмулировать браузер”. В частности, познакомимся с библиотекой Telethon – асинхронной Python-библиотекой для работы с Telegram API (MTProto). С её помощью можно получать историю сообщений, информацию о каналах, и многое другое, выдаваемое самим Telegram. Это надёжнее и эффективнее, чем пытаться парсить HTML-версию Telegram (которая ограничена).

Ключевые темы:

  • Обзор способов парсить Telegram:
    • Telegram Bot API: предоставляет ограниченный доступ для ботов – можно получать сообщения из чатов, где бот состоит, но боты не могут получить список участников чата (это важно) и не могут вступать сами в группы без приглашения. Bot API подходит для мониторинга сообщений в реальном времени или простого парсинга контента, но для полной информации (например, список всех подписчиков канала) он бесполезен.
    • Telethon (MTProto API): позволяет логиниться как обычный пользователь (нужно ввести номер телефона и получить код) и получать практически любые данные, которые доступны этому пользователю. Если пользователь состоит в приватном чате – он может получить участников, если подписан на канал – может читать историю и видеть ограниченную информацию о подписчиках. Telethon требует немного больше настроек (API ID и API Hash от Telegram, получение кода по телефону), но открывает широкие возможности.
    • Мы выберем Telethon как основной инструмент в этом курсе.
  • Настройка Telethon: создание приложения (API ID, API hash) через My Telegram – необходимый шаг, чтобы использовать Telegram API. Нужно зарегистрироваться, получить эти ключи.
  • Первое подключение:
    • Установка библиотеки: pip install telethon.
    • Инициализация клиента: from telethon import TelegramClient. Создаём клиента: client = TelegramClient('session_name', api_id, api_hash). Первый запуск потребует аутентификации: client.start(phone='+7XXXXXXXX') – Telethon попросит код, который придёт в Telegram (его можно программно ввести через callback или просто в консоли). После этого создаётся session_name.session файл с данными сессии, и повторные запуски не потребуют кода.
  • Получение сообщений из канала или чата:
    • Если у нас публичный канал или группа, можно обращаться по username или id. Например: client.get_messages('telegram_channel_username', limit=100) – получим последние 100 сообщений. Или асинхронно: await client.get_messages('SomeChannel', limit=10). Сообщения приходят как объекты, из которых можно извлечь message.text, message.date, message.sender_id и прочее. Если сообщение содержит медиа (фото, документ), Telethon позволяет его скачать.
    • Также рассмотрим итерацию: Telethon позволяет итерироваться по сообщениям (например, в архиве канала) с помощью client.iter_messages. Можно собрать вообще все сообщения (но осторожно с очень большими каналами – их могут быть сотни тысяч).
    • Ограничения: некоторые каналы могут быть приватные. Чтобы получить их сообщения, ваш аккаунт должен состоять в этом канале. Для публичных открытых каналов/чатов – достаточно доступа по username. Если канал слишком большой (более 100k сообщений), придется загружать порциями.
  • Формат данных сообщений: у сообщений может быть не только текст – могут быть вложения (картинки, стикеры), пересланные сообщения, опросы. В этом курсе фокус на текст, но упомянем: Telethon позволяет различать типы сообщений (класс Message), и можно, например, получить message.media для доступа к файлам.
  • Пример использования: спарсим посты из канала. Допустим, есть канал “coin_news” с крипто-новостями. Мы можем написать скрипт, который получает последние 20 постов и сохраняет их текст в файл. Telethon код (синхронный режим через client.loop.run_until_complete или упрощённый client.start()):
from telethon.sync import TelegramClient
from telethon.tl.functions.messages import GetHistoryRequest

# Замените на ваши реальные api_id, api_hash
api_id = 123456
api_hash = 'abcdef1234567890abcdef1234567890'
client = TelegramClient('session_name', api_id, api_hash)
client.start()  # при первом запуске запросит код подтверждения

channel_username = 'coin_news'  # пример юзернейма канала
history = client(GetHistoryRequest(
    peer=channel_username,
    limit=20,
    offset_date=None, offset_id=0, max_id=0, min_id=0,
    add_offset=0, hash=0
))
messages = history.messages
for msg in messages:
    print(msg.date, msg.message[:50])

(В Telethon есть и более простой метод client.get_messages, но показан низкоуровневый для иллюстрации параметров.) Этот пример выведет дату и первые 50 символов каждого из последних 20 сообщений канала.

Важно: для работы с Telethon этот скрипт должен запускаться в реальной среде, в нашем курсе мы предполагаем, что слушатель сможет это сделать на своём компьютере, так как Telethon требует интерактива при первом логине (SMS код).

Практические задания:

  1. Получение сообщений канала. Настройте Telethon (получите свои api_id и api_hash по инструкции). Напишите скрипт, который выводит последние 10 сообщений из любого публичного канала на ваш выбор. Выведите на экран дату сообщения и его текст. Попробуйте с разными каналами – например, новостной канал, канал с анекдотами. Обратите внимание на форматирование: в Telegram сообщения могут содержать ссылки, эмодзи, форматирование (HTML-разметку). Telethon может возвращать их либо в сыром виде (с тегами) либо уже очищенными – проверьте и при необходимости приведите к чистому тексту.
  2. Сохранение истории чата. Выберите публичный чат (группу) с открытой историей или пригласите бота (вашего Telethon-клиента) в группу, где вам не жалко данных. С помощью Telethon получите, например, 50 последних сообщений из этой группы. Сохраните их в файл chat_history.txt в формате: [YYYY-MM-DD HH:MM] <Sender Name>: Message text Для получения имени отправителя придётся дополнительно запросить информацию о пользователях (Telethon может автоматически заполнить msg.sender если вызвать await msg.get_sender(), либо использовать client.get_entity(msg.sender_id)). Сделайте это для практики: чтобы уметь получать подробности о пользователе по его ID.
  3. Парсинг медиа (опционально, продвинутое). Если в выбранном вами канале/чате есть сообщения с медиа (например, фотографии), попробуйте с помощью Telethon скачать одно из них. Telethon предоставляет метод client.download_media(message, file_path). Найдите сообщение, у которого msg.media не None (например, if msg.photo: или msg.document:), и вызовите download_media. Убедитесь, что файл сохраняется (можно сохранить в текущую папку). Отметьте, что для этого нужна хорошая связь и, возможно, увеличьте limit при получении сообщений, чтобы наверняка наткнуться на медиа. (Это задание для понимания полного диапазона возможностей – если сложно, можно пропустить.)
  4. Анализ содержимого сообщений. Напишите небольшую обработку текста по данным Telegram. Например, возьмите канал с объявлениями о вакансиях или с новостями, получите последние 100 сообщений. Затем проанализируйте их: посчитайте, сколько сообщений содержат слово “Python”, или извлеките все ссылки из сообщений (в Telethon URL можно вытащить, например, регулярным выражением на msg.message, либо Telethon может возвращать уже объектами MessageEntityTextUrl). Выведите результаты анализа – например: “Из 100 сообщений, 15 содержат слово ‘Python’. Ссылки, встречающиеся в сообщениях: …”. Это покажет, как на основе собранных данных можно проводить простой анализ.

Модуль 8: Сбор данных об участниках Telegram (парсинг списка пользователей)

Описание: В продолжение работы с Telegram, теперь научимся получать информацию об участниках чатов и каналов. Это отдельная задача: например, нужно собрать список всех пользователей в группе (их имена, usernam’ы, идентификаторы). Такие данные могут быть нужны для анализа аудитории, поиска конкретных участников или даже для рассылки (хотя рассылка без согласия – это спам, мы просто рассматриваем техническую возможность). С помощью Telethon можно получить список участников чата или канала, но есть ограничения Telegram: например, список подписчиков канала доступен не всегда. Мы обсудим эти нюансы и выполним парсинг участников там, где возможно.

Ключевые темы:

  • Ограничения Telegram на получение участников:
    • Групповые чаты (супергруппы): если у вас есть доступ (вы член группы, и группа открыта), вы можете получить всех участников. Telethon предоставляет метод client.get_participants(chat), возвращающий список объектов пользователей. Для очень больших групп Telegram может отдавать участников частями, но Telethon это внутренне обрабатывает (нужно осторожно с группами > 10k, может потребовать пагинацию, но библиотека обычно сама ходит).
    • Каналы: публичные каналы не позволяют просто так узнать полный список подписчиков если вы не являетесь администратором или канал очень большой. Telegram API для обычного юзера возвращает список участников канала только если канал: a) менее 200 участников и вы в нём являетесь администратором (или, кажется, если канал ваш); или b) если к каналу прикреплён чат обсуждений (Comments), тогда подписчики видны как участники группы-комментариев. Проще говоря, получить подписчиков чужого канала нельзя стандартным способом, если их больше 200.
      • Впрочем, часто под “собирать данные об участниках канала” подразумевают всё же сбор из групп или комментариев к каналу.
    • Закрытые группы/каналы: если группа или канал приватные, Telethon-клиент должен быть добавлен туда (или иметь приглашение). Без этого никак. Мы не рассматриваем взлом, только легальные методы через свой доступ.
  • Получение участников через Telethon:
    • Используем client.get_participants(entity, limit=None) – получаем всех, либо с limit если хотим ограничить. Возвращает список объектов типа telethon.tl.types.User. У них есть поля: id, username, first_name, last_name, bot (флаг бота или нет), status (онлайн/offline с датой последнего захода) и др.
    • Нужно учитывать: в больших группах вызов может быть немного долгим, но Telethon делает внутренние батчи по 200 участников за запрос. Мы можем отобразить прогресс или просто ждать.
    • Если группа очень большая (>100k), Telegram может не отдавать всех сразу, но Telethon get_participants умеет в цикле получить всех.
    • Можно фильтровать: Telethon позволяет запросить, например, только администраторов (filter=ChannelParticipantsAdmins) или недавно онлайн (ChannelParticipantsRecent), но в нашем курсе достаточно получить всех.
  • Обработка списка пользователей: получив список User, мы можем, например, вывести количество участников, список usernames (некоторые могут быть None, значит, у пользователя нет публичного @username), или собрать статистику, например, сколько ботов среди участников (по флагу).
  • Практические сценарии:
    • Анализ аудитории: собрать всех участников группы, затем, например, вывести сколько из них недавно были онлайн (в User.status можно увидеть Online/Offline with date), или распределение по языкам (по presumably last_name? – нет, лучше username домены, но это условно). Хотя Telegram не дает прямой инфы о географии.
    • Импорт контактов: допустим, у вас есть группа, и вы хотите сохранить список юзеров чтобы потом с ними взаимодействовать (вручную или иначе).
    • Скрещивание данных: если взять два разных чата, можно сравнить списки участников – найти общих (пересечение). Telethon выдаст ID, которые уникальны глобально, так что можно сравнить. Это уже небольшая аналитическая задача.

Пример кода (получение участников):

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

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

group = 'test_group_name'  # username или айди группы, где мы состоим
all_participants = []
offset = 0
limit = 100
while True:
    participants = client(GetParticipantsRequest(
        group, ChannelParticipantsSearch(''), offset, limit, hash=0
    ))
    if not participants.users:
        break
    all_participants.extend(participants.users)
    offset += len(participants.users)

print("Всего участников:", len(all_participants))
for user in all_participants[:5]:
    name = (user.first_name or "") + " " + (user.last_name or "")
    print(user.id, name, "@"+user.username if user.username else "")

Этот пример показывает ручной метод через GetParticipantsRequest с постраничным получением. Telethon также позволяет client.get_participants(group) без цикла – сделает то же самое, но цикл даёт наглядность. Код выводит количество участников и информацию по первым 5.

Практические задания:

  1. Список участников группы. Используйте Telethon, чтобы получить всех участников выбранной вами Telegram-группы (публичной или той, где вы состоите). Выведите: общее число участников, и, например, первые 10 пользователей в формате “Username (Name)”. Где Username – это @username или <без username> если None, а Name – комбинация first_name + last_name. Обратите внимание, что может быть first_name без last_name. Сделайте аккуратно.
  2. Фильтрация участников. Для той же группы, попробуйте отфильтровать полученный список:
    • Посчитайте, сколько участников не имеют username (т.е. общаются только по телефону/ид, приватные).
    • Сколько пользователей являются ботами (у Telethon User.bot флаг True).
    • Выведите список всех администраторов группы. Telethon позволяет после получения участников проверить user.id на наличие в client.get_participants(group, filter=ChannelParticipantsAdmins). Но проще: при обычном списке у админа может быть флаг participant.admin_rights (если привязать через participants.participants Telethon response). Для упрощения, можно повторно вызвать get_participants с фильтром Admins. Выведите их имена.
      Эти действия помогут лучше понять свойства, которые можно извлечь из User.
  3. Парсинг подписчиков канала (если возможно). Найдите небольшой публичный канал, в котором не очень много подписчиков (менее 200) и на который вы подпишетесь своим аккаунтом. Попробуйте client.get_participants(channel) на нём. Получилось ли извлечь список? Если да, выведите аналогично список (например, всех или первых 20) подписчиков с именами. Если нет – опишите, почему (скорее всего, Telegram API не позволит, если условие не выполнено). Это задание демонстрирует ограничения: например, если канал чужой и >200 подписчиков, Telethon может вернуть ошибку “ChannelPrivate” или пустой список.
  4. Поиск пользователя в нескольких группах. (Дополнительное сложное задание) Предположим, у вас есть несколько групп, и вы хотите найти пользователя с определённым username или именем, который состоит в более чем одной из них. Напишите программу, которая берёт список групп (например, 3 группы по их username) и собирает множества участников (по user.id) каждой. Затем находит пересечение этих множеств – то есть пользователей, присутствующих во всех этих группах. Выведите эти общие пользовательские имена. (Если у вас нет таких данных, можете просто смоделировать на 2 небольших группах; или вывести пересечение двух групп – кто состоит и там, и там.) Это демонстрирует практическое применение: например, нахождение активной аудитории пересекающихся по интересам групп.

Модуль 9: Лучшие практики разработки парсеров (как писать свои парсеры)

Описание: Завершающий модуль посвящён обобщению знаний. Здесь мы не вводим новых инструментов, а систематизируем подход к созданию собственного парсера с нуля. Вы научились множеству отдельных приёмов – теперь важно понять, как правильно спланировать парсер, на что обращать внимание при его разработке и отладке, как оптимизировать. Также поговорим о том, как хранить и организовывать код (при расширении проектов), и где искать примеры/шпаргалки для помощи. К концу этого модуля у вас должно сложиться цельное понимание процесса разработки веб-парсера на Python.

Ключевые темы:

  • Планирование парсера: перед тем, как начать кодить:
    1. Анализ цели: чётко определить, какие данные нужны и откуда. Например: “собрать названия и цены всех телефонов с сайта X”.
    2. Изучение источника: вручную зайти на сайт, посмотреть структуру страниц, выявить URL паттерны (страницы пагинации, или API, или RSS/фид если есть). Посмотреть HTML-код нужных элементов (через инспектор браузера) – определить, по каким селекторам будете искать (id, классы, теги). Проверить, не динамические ли данные (может быть, при прокрутке подгружаются). Если динамические – проверить Network, есть ли API-эндпоинты, или решить, что нужен Selenium.
    3. Выбор инструментов: в зависимости от предыдущего шага. Для статичных HTML – Requests + BeautifulSoup. Для AJAX/SPA – возможно Selenium, или mix: requests к скрытым API. Для очень больших данных – подумать про asyncio. Для sites with heavy anti-scraping – возможно, продумать proxies, или хотя бы random delays.
    4. Проектирование структуры данных: решить, как будете хранить результат. Таблица CSV? JSON-файл? База данных? Если данные табличные (товары: название, цена, рейтинг) – CSV хорошо. Если иерархические (например, переписка) – JSON или база. Если изображения – возможно, скачивать в папки. Также оценить объём: тысяча записей или миллион – от этого зависит подход (CSV для миллиона может быть, но лучше уж база).
  • Реализация и отладка:
    • Начинать с малого: сначала сделать прототип на одну страницу. Например, научиться парсить 1 страницу товара и извлекать нужные поля. Проверить, что всё правильно достается (распечатать). Затем обернуть цикл по множеству страниц.
    • Обработка ошибок: добавить try/except вокруг запросов, если страница не отвечает – логировать и продолжать. Обрабатывать возможные AttributeError, когда soup.find не нашёл элемент (например, если у некоторых товаров нет какого-то поля – надо предусмотреть, что .find вернёт None).
    • Логирование и Debug: вставлять print/log сообщений на важные этапы: “Загружаю страницу 5”, “Получено товаров: 20”, “Ошибка на странице 7: … (пропускаю)”. Это поможет потом понять, где сбой, если что.
    • Тестирование на выборке: прежде чем запускать на 100k страниц, протестировать на 10-20 страниц, убедиться, что данные парсятся в правильном формате, записываются корректно.
    • Не забывать соблюдать таймауты и паузы если нужно (чтобы не дать серверу подумать, что вы бот, хотя вы бот, но “вежливый” 😄).
  • Оптимизация кода парсера:
    • Если скрипт слишком медленный: подумать, что главный тормоз – сеть. Можно параллелить (многопоточность или asyncio как мы учились). Но тоже дозированно.
    • Если скрипт ест много памяти: например, вы собираете результаты в огромный список – лучше писать в файл/БД постепенно и очищать из памяти.
    • Профилирование: для очень больших проектов, можно профилировать, но обычно узкое место – I/O.
    • Повторное использование кода: выделять функции. Например, функция parse_product_page(html) которая на вход берёт HTML текста товара, а на выход — словарь с нужными данными. Это делает код чище, можно отдельно юнит-тестировать на сохранённом кусочке HTML.
    • Конфигурация: если парсер нужно запускать регулярно или с разными параметрами, хорошо выносить настройки наверх (URL, например, или лимиты) либо делать аргументы командной строки.
  • Примеры кода и шпаргалки: У вас по ходу курса накопилось много примеров – сохраните их. Часто новый проект парсинга можно начать с копирования шаблона: взять готовый код requests+BS4 цикла и просто поменять URL и селекторы. Это нормально. Многие опытные парсеры так и работают – берут типовой код.
    • Есть онлайн-сообщества (Stack Overflow, форумы) – можно искать по ключевым словам если что-то не получается, велик шанс, что кто-то уже делал подобное. Но будьте осторожны с бездумным копированием, понимайте, что делает код.
    • Открытые проекты на GitHub: можно посмотреть парсеры, написанные другими, почерпнуть идеи по структуре кода, логике обхода.
  • Этика и легальность: Напоследок подчеркнём: собирать открытые данные – нормально, но убедитесь, что вы не нарушаете ничьи условия использования. Не перегружайте сайты. Если планируете коммерческое использование парсера – точно прочитайте Terms of Service источника. В некоторых случаях лучше попросить разрешения или использовать официальный API.
  • Ваш дальнейший путь: Вы теперь знаете базу. Дальше – можно углубляться:
    • Изучить фреймворк Scrapy – мощный инструмент для веб-скрейпинга, который многое из наших ручных шагов умеет делать из коробки (асинхронно, управление очередью URL, среда для организации парсеров). Научившись основам, освоить Scrapy будет легче.
    • Изучить Browser automation глубже – например, Playwright (современная альтернатива Selenium) или puppeteer.
    • Использовать машинное обучение на данных из парсера (например, вы scraped отзывы – можно применить sentiment analysis).
    • Ну и делиться своими парсерами, возможно, open-source, если они могут помочь другим.

Практические задания:

  1. Проектирование своего парсера (теоретическое). Выберите веб-сайт или источник данных, который вас интересует, и составьте план парсинга (пока без кода). Выпишите:
    • Какие данные вы хотите получить и для чего.
    • Какие страницы/разделы нужно для этого обойти (например: “все страницы каталога новостей”, или “раздел вопросов на форуме”).
    • Есть ли особенности: нужно ли логиниться? Есть ли капчи/защита?
    • Чем вы будете парсить (requests/BS4 или Selenium или API).
    • В каком формате сохраните данные (CSV, JSON, …).
      Этот план опишите текстом или псевдокодом. Это поможет структурировать подход прежде, чем писать код.
  2. Написание и тестирование прототипа. Реализуйте небольшой парсер для вашего выбранного сайта (или упрощённой задачи). Например, спарсить первые 3 страницы списка товаров и вывести названия. Или скачать заголовки последних 5 статей с блога. Главное – пройти полный цикл: написать код запроса, парсинга, вывода/сохранения, запустить, убедиться, что работает. Затем прикинуть, что надо добавить, чтобы масштабировать на весь сайт (но можно не запускать на весь, если он большой).
  3. Рефакторинг кода. Возьмите один из кодов, что вы писали в заданиях ранее (например, парсер из Module 1 или 2), и попробуйте улучшить его структуру:
    • Вынесите повторяющийся код в функцию.
    • Добавьте обработку ошибок, где её не было.
    • Добавьте логирование (например, print(f"Parsing page {i}") в цикле).
    • Если у вас там были “магические числа” или URL, вынесите их в переменные в начало скрипта (например, BASE_URL = “…”).
      Сравните до и после – второй вариант должен быть легче читаем и надёжнее.
  4. Изучение чужого парсера (по желанию). Найдите на GitHub или в блогах пример проекта веб-парсера на Python (можно воспользоваться поиском, например “BeautifulSoup scraper GitHub”). Посмотрите, как он организован: есть ли разделение на модули, как они выбирают селекторы, используют ли __main__. Необязательно понимать весь код, главное – увидеть стиль. Возьмите оттуда что-то полезное для себя. Возможно, вы найдёте там трюк, как обходить Cloudflare или как парсить необычный формат. Поделитесь кратко (для себя): чему вы научились из чужого примера.

Заключение: Поздравляем с прохождением курса! 🎉 Вы освоили основы веб-парсинга на Python, научились работать с BeautifulSoup, автоматизировать браузер через Selenium, обходить типичные препятствия вроде капч, собирать данные из Telegram, и многое другое. Теперь перед вами открыты широкие возможности: вы можете собирать информацию практически из любого открытого источника в интернете. Этот навык востребован: будь то анализ цен, мониторинг новостей или даже научные исследования данных из соцсетей. Помните о лучших практиках и ответственности: используйте ваши навыки этично.

+1
0
+1
3
+1
0
+1
0
+1
1

Ответить

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