<div><img src="https://top-fwz1.mail.ru/counter?id=3548135;js=na" style="position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div>
Публичное облако на базе VMware с управлением через vCloud Director

Полезные советы для парсинга данных веб-сайтов

Роман Панков
Роман Панков
Технический писатель
11 февраля 2025 г.
21
13 минут чтения
Средний рейтинг статьи: 5

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

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

Предупреждаем, что получение данных автоматизированным путем может быть запрещено условиями использования сайтов. Мы не поощряем нарушения этих условий, правил, указанных в файле robots.txt или иных правовых норм. Используйте представленные методы только в рамках разрешенных сценариев, соблюдая политику владельцев ресурсов. 

Совет 1. Изучите работу с DevTools

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

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

Работа с веб-инспектором

Первое, что нужно разработчику, чтобы быстро определять источник данных, — научиться пользоваться инструментами разработчика (DevTools, от англ. «Development Tools», в тексте также будет использоваться название «веб-инспектор»). Они есть в любом браузере, открыть можно с помощью клавиши F12 или комбинации клавиш Ctrl + Alt + I на Windows или Command + Option + I на MacOS. 

Для работы в первое время понадобятся только две вкладки — «Elements» и «Network». Первая позволяет узнать структуру страницы и определить, в каком DOM-элементе находятся данные. Вкладка «Network» нужна для работы с запросами, которые мы впоследствии будем копировать.

Вкладки располагаются в верхней части инструментов разработчика. Современные браузеры могут сразу перевести все функции веб-инспектора на русский язык. Примерный вид показан на скриншоте. 

Image7

Чаще всего информация поступает на сайт двумя способами:

  • В HTML-разметке страницы. Это происходит, если данные добавляются на страницу на этапе обработки бэкендом.
  • В формате JSON. Такие данные могут запрашиваться фронтендом как на этапе загрузки страницы, так и после определенных действий пользователя на странице. 
vds

Совет 2. Используйте готовый алгоритм для начала работы с любым сайтом-донором

Ниже представлен алгоритм действий, рекомендуемый для начала работы с любым сайтом-донором:

  1. Найдите GET-запрос с типом контента text/html, который отправляется браузером при инициализации страницы.

Для этого перейдите на страницу, данные из которой нужно извлечь. Откройте веб-инспектор на вкладке «Network». Очистите запросы, нажав на иконку мусорной корзины слева от поиска по запросам. Перезагрузите страницу сочетанием клавиш Ctrl + R на Windows/Linux или Command + R на MacOS. Одним из первых запросов окажется нужный GET-запрос (1) с типом контента text/html (2). 

Image3

Нажмите на найденный запрос. Далее перейдите на вкладку «Response» — откроется режим предпросмотра ответа сервера. Верстка страницы может быть нарушена — это нормально.

Image6

Попробуйте найти нужные данные в режиме предпросмотра визуально. Например, HTML-разметка статей на Timeweb Cloud формируется сервером. Если бы нужно было автоматизировано получить текст статьи, тогда большая часть уже сделана.

Если визуально найти не удалось, перейдите в режим просмотра HTML-разметки (1) ответа сервера (не путать со вкладкой «Elements»). Активируйте режим поиска в ответе комбинацией клавиш Ctrl + F на Windows и Command + F на MacOS. Введите пример данных (2), которые точно есть на этой странице (например, разработчик знает, что в статье есть комбинация слов «автоматический поиск» — именно его можно попробовать поискать). Браузер выделит подстроку в тексте, если найдет совпадения (3).

Image4

Зачастую, если информация отдается сервером в виде HTML-разметки, то названия селекторов не меняются. Для удобства работы с селекторами можно использовать стандартный инструмент поиска элементов на странице с помощью мыши: Ctrl + Shift + C на Windows или Cmd + Shift + C на MacOS. Нажмите комбинацию клавиш и выберите элемент прямо на странице. Браузер покажет нужный элемент, его селекторы удобно сразу перенести в код.

Если нужных данных нет, переходите к следующему шагу.

  1. Найдите запросы, оставив только содержащие JSON. Сделать это проще, выполнив фильтрацию: нажмите на строку поиска (1) по запросам и введите фильтр:

mime-type: application/json

Image2

Пройдитесь по каждому запросу с доменом сайта-донора и повторите действия с поиском данных, как в предыдущем шаге.

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

Совет 3. Используйте быстрый экспорт запросов

В большинстве случаев вместе с запросом браузер отправляет на сервер заголовки запроса (headers) и куки (cookies). Заголовки передают метаданные, позволяющие серверу понять, какой формат данных запрашивается и как их оптимально отдать. Куки хранят информацию о сессии и предпочтениях пользователя. Благодаря этому сервер формирует персонализированный ответ.

Без этих данных сервер может отклонить запрос, если сочтет его недостаточно безопасным.

Экспорт запроса через cURL

Способ позволяет экспортировать готовый код для совершения запроса, причем не только на Python. Он подойдет для любых запросов.

Найдите нужный запрос в веб-инспекторе. Нажмите правой кнопкой мыши на запрос, далее «Copy» (1) и «Copy as cURL» (2). Теперь информация о запросе скопирована в буфер обмена. 

Image1

Перейдите на сайт curlconverter.com — швейцарский нож для разработчиков скриптов для парсинга и автоматизации. Нажмите на Python в строке выбора языка программирования. Далее вставьте скопированный запрос в поле ввода. Вы получили готовый шаблон кода вместе со всеми параметрами запроса, готовый к импорту в IDE. 

Image5

Код содержит словари с заголовками, данными куки, параметрами запроса JSON (json_data, если есть) и всё необходимое, чтобы полностью дублировать запрос, происходящий в браузере. 

Совет 4. Используйте виртуальное окружение при работе на Python

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

Подробнее о виртуальном окружении и об их создании мы рассказывали в другой статье.

Чтобы быстро перенести проект на сервер, при условии что на локальном компьютере вы работали в виртуальном окружении, сперва сохраните список библиотек с версиями из pip в файл requirements.txt:

pip freeze > requirements.txt

Если вы только что создали сервер на Ubuntu, то можете воспользоваться универсальным скриптом для установки Python, виртуального окружения и всех зависимостей на чистый сервер. Для этого прежде перенесите файлы проекта (через утилиту scp или протокол FTP), перейдите в директорию проекта, вставьте готовую команду в терминал. В начале команды укажите нужную версию Python в переменную PYVER и выполните команду. 

export PYVER=3.9 && sudo apt update && sudo apt upgrade -y && sudo apt install -y software-properties-common && sudo add-apt-repository ppa:deadsnakes/ppa -y && sudo apt update && sudo apt install -y python${PYVER} python${PYVER}-venv python${PYVER}-dev python3-pip && python${PYVER} -m venv venv && source venv/bin/activate && pip install --upgrade pip && [ -f requirements.txt ] && pip install -r requirements.txt

Совет 5. Включите в алгоритм обработчики ошибок

При разработке парсера важно предусмотреть механизм обработки ошибок. Сетевые сбои, изменения в структуре HTML или неожиданные блокировки со стороны сайта могут привести к сбоям в работе скрипта. 

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

В Python для этого можно использовать:

  • конструкции try, except, finally;
  • библиотеку logging для логирования;
  • циклы для повторении запросов при неудачах;
  • таймауты, например:
    • requests:

requests.get("timeweb.cloud", timeout=20)
    • aiohttp:

timeout = aiohttp.ClientTimeout(total=60, sock_connect=10, sock_read=10) async with aiohttp.ClientSession(timeout=timeout) as session: 
    async with session.get(url) as response: 
        return await response.text()

Совет 6. Оформляйте парсер в виде генератора

Генератор — это класс, реализующий логику объекта, который итеративно выдает элементы по мере необходимости. 

Генераторы особенно удобно использовать при разработке скрипта для парсинга по следующим причинам:

  • Ленивые вычисления (lazy evaluation). Генераторы вычисляют и возвращают данные «на лету», что позволяет обрабатывать большие объемы информации без значительного расхода памяти. При парсинге больших файлов или веб-страниц это критично: данные обрабатываются постепенно, и в памяти хранится лишь текущая часть, а не весь результат сразу.

  • Повышенная производительность. Благодаря тому, что элементы генерируются по мере необходимости, можно начать обработку и передачу данных (например, в базу данных или бот) до того, как весь набор данных будет получен. Это уменьшает задержки и позволяет быстрее реагировать на поступающие данные.

  • Удобство организации кода. Генераторы упрощают реализацию итеративных процессов, позволяя сконцентрироваться на логике парсинга, а не на управлении состоянием итерации. Это особенно полезно, когда нужно обрабатывать поток данных и передавать их в другие части системы.

Пример реализации парсера как генератора на Python

В цикле, где используется генератор, удобно инициировать запись данных в базу данных или, например, отправлять уведомления через Telegram-бот. Использование генераторов делает код более читабельным. 

import requests
from bs4 import BeautifulSoup
 
 
class MyParser:
    def __init__(self, url):
        self.url = url
 
    def parse(self):
        """
        Генератор, который последовательно возвращает данные
        (например, названия элементов на странице).
        """
        response = requests.get(self.url)
        if response.status_code != 200:
            raise Exception(f"Не удалось получить страницу, статус: {response.status_code}")
 
        soup = BeautifulSoup(response.text, "html.parser")
        items = soup.select("div")
 
        for item in items:
            title = item.select_one("h1").get_text(strip=True) if item.select_one("h1") else "Без заголовка"
            yield {
                "title": title,
                "content": item.get_text(strip=True)
            }
 
 
if __name__ == "__main__":
    parser = MyParser("https://example.com")
    for data_item in parser.parse():
        print(data_item["title"], "--", data_item["content"])

Совет 7. Применяйте асинхронный подход для ускорения обработки большого объема запросов

При парсинге большого числа страниц синхронный подход часто становится узким местом, так как каждый запрос ждет завершения предыдущего. Асинхронные библиотеки, например aiohttp в Python, позволяют выполнять множество запросов одновременно, что существенно ускоряет сбор данных. Однако, чтобы избежать перегрузки как вашего приложения, так и серверов-доноров, важно грамотно регулировать поток запросов. Здесь на помощь приходят техники троттлинга, экспоненциального бэкафа и организация очередей задач.

Как это работает?

  • Асинхронные запросы. Создайте асинхронную сессию с заданными таймаутами (например, общий timeout, таймаут подключения и чтения). Это позволяет параллельно обрабатывать множество запросов без блокировки основного потока выполнения.

  • Троттлинг. Для предотвращения чрезмерной нагрузки на сервер донора разумно ограничивать число одновременных запросов. Это можно сделать с помощью семафоров или других механизмов управления конкурентностью (например, asyncio.Semaphore), чтобы не посылать запросы быстрее, чем это допустимо.

  • Экспоненциальный бэкофф. Если запрос завершился с ошибкой (например, из-за таймаута или временной блокировки), используйте стратегию экспоненциального бэкоффа. При повторной попытке интервал ожидания увеличивается (например, 1 секунда, затем 2, 4, 8…), что позволяет серверу восстановиться и уменьшает вероятность повторных ошибок.

  • Очереди задач. Организация очередей (например, с помощью asyncio.Queue) помогает управлять большим потоком запросов. Сначала формируется очередь URL-адресов, затем запросы обрабатываются по мере освобождения «слотов» для выполнения, что обеспечивает равномерное распределение нагрузки и стабильность работы парсера.

Пример реализации инструментов на Python с использованием aiohttp

import asyncio
import aiohttp
from aiohttp import ClientTimeout
 
# Ограничиваем число одновременных запросов
semaphore = asyncio.Semaphore(10)
 
 
async def fetch(session, url):
    async with semaphore:
        try:
            async with session.get(url) as response:
                return await response.text()
        except Exception:
            # Применяем экспоненциальный бэкаф при возникновении ошибки
            for delay in [1, 2, 4, 8]:
                await asyncio.sleep(delay)
                try:
                    async with session.get(url) as response:
                        return await response.text()
                except Exception:
                    continue
            return None
 
 
async def main(urls):
    timeout = ClientTimeout(total=60, sock_connect=10, sock_read=10)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        tasks = [asyncio.create_task(fetch(session, url)) for url in urls]
        results = await asyncio.gather(*tasks)
        # Обработка полученных данных
        for result in results:
            if result:
                print(result[:200])  # Вывод первых 200 символов ответа
 
# Пример списка URL для парсинга
urls = ["http://timeweb.cloud"] * 100
 
asyncio.run(main(urls))

Рекомендации разработчикам

Существуют рекомендации, следование которым также поможет упростить работу разработчика:

  • Проверьте, есть ли у сайта-донора открытый API. Бывает так, что задача по написанию алгоритма парсинга уже выполнена, а у сайта обнаруживается весьма удобный API, который полностью покрывает задачи.

  • Следите за изменениями структуры сайта. Разработчики сайта-донора могут изменить верстку, и тогда придется изменить селекторы задействованных элементов в коде.

  • Тестируйте выполнение функций на каждом этапе. Автоматические тесты (unit-тесты, интеграционные) помогают своевременно выявить поломки, связанные с изменением структуры сайта или внутренними правками кода.

Чек-лист для определения метода парсинга

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

Image8

Надежные VDS/VPS в Timeweb Cloud

Заключение

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

Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
11 февраля 2025 г.
21
13 минут чтения
Средний рейтинг статьи: 5
Пока нет комментариев