Асинхронный веб-парсинг с помощью AIOHTTP в Python

Откройте для себя AIOHTTP для веб-парсинга! Изучите настройку, функции и передовые методы, а также сравните их с Requests для эффективного извлечения данных.
7 min read
web scraping with aiohttp and python blog image

В этом руководстве вы изучите:

  • Что такое AIOHTTP, и какие основные функции это средство предоставляет
  • Пошаговый раздел об использовании AIOHTTP для веб-парсинга
  • Передовые методы веб-парсинга с помощью AIOHTTP
  • Сравнение AIOHTTP и Requests в сфере обработки автоматических запросов

Давайте рассмотрим эти вопросы подробнее!

Что такое AIOHTTP?

AIOHTTP — это асинхронный HTTP-фреймворк клиент-сервер, построенный на основе asyncio на Python. В отличие от традиционных HTTP-клиентов, AIOHTTP использует сеансы клиентов для поддержания соединений для множества запросов. Это делает его эффективным средством для задач, основанных на сеансах с высоким уровнем параллелизма.

⚙️ Особенности

  • Поддерживает как клиентские, так и серверные стороны протокола HTTP.
  • Обеспечивает встроенную поддержку WebSockets (как для клиента, так и для сервера).
  • Предлагает промежуточное ПО и подключаемую маршрутизацию для веб-серверов.
  • Эффективно обрабатывает потоковую передачу больших данных.
  • Включает сохранение сеанса клиента, позволяет повторно использовать соединение и снижает накладные расходы на выполнение множества запросов.

Парсинг с помощью AIOHTTP: пошаговое руководство

В контексте веб-парсинга AIOHTTP — это просто HTTP-клиент для получения необработанного HTML-контента страницы. Чтобы проанализировать HTML и извлечь из него данные, вам понадобится HTML-парсер, например BeautifulSoup.

Выполняйте инструкции в этом разделе, чтобы узнать, как использовать AIOHTTP для веб-парсинга с помощью BeautifulSoup!

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

Шаг 1. Настройте свой проект по парсингу

Убедитесь, что на вашем компьютере установлен Python версии 3 или более новой. Если нет, скачайте его с официального сайта и следуйте инструкциям по установке.

Затем создайте каталог для проекта парсинга AIOHTTP, используя следующую команду:

mkdir aiohttp-scraper

Перейдите в этот каталог и настройте виртуальную среду:

cd aiohttp-scraper
python -m venv env

Откройте папку проекта в предпочитаемой среде Python IDE. Visual Studio Code с расширением Python или PyCharm Community Edition — подойдет оба этих варианта.

Теперь создайте файл scraper.py в папке проекта. Сначала она будет пустая, но вскоре вы добавите в нее механизм парсинга.

В терминале своей IDE активируйте виртуальную среду. В Linux или macOS используйте:

./env/bin/activate

Аналогичным образом, в Windows запустите:

env/Scripts/activate

Отлично! У вас все настроено, и вы готовы к работе.

Шаг № 2. Настройка библиотек для парсинга

После активации виртуальной среды установите AIOHTTP и BeautifulSoup с помощью следующей команды:

pip install aiohttp beautifulsoup4

Это давбавит и aiohttp, и beautifulsoup4 в число зависимых объектов вашего проекта.

Импортируйте их в свой скрипт scraper.py:

import asyncio
import aiohttp 
from bs4 import BeautifulSoup

Обратите внимание, что для работы aiohttp требуется asyncio.

Теперь добавьте следующий рабочий процесс функции async в файл scrper.py:

async def scrape_quotes():
    # Scraping logic...

# Run the asynchronous function
asyncio.run(scrape_quotes())

scrape_quotes() определяет асинхронную функцию, в которой ваш механизм очистки будет работать многопоточно без блокировок. Наконец, asyncio.run(scrape_quotes()) запускает и выполняет асинхронную функцию.

Потрясающе! Теперь можно перейти к следующему этапу рабочего процесса парсинга.

Шаг 3. Получение HTML-кода целевой страницы

Из этого примера вы узнаете, как собрать данные с сайта «Цитаты для парсинга»:

Целевой сайт

В таких библиотеках, как Requests или AIOHTTP вы просто отправите запрос GET и получите HTML-контент страницы напрямую. Однако у AIOHTTP иной жизненный цикл запроса.

Основным компонентом AIOHTTP является ClientSession, который управляет пулом подключений и по умолчанию поддерживает Keep-Alive. Вместо того чтобы открывать новое соединение для каждого запроса, оно повторно использует соединения, повышая производительность.

Процесс отправки запросаа обычно состоит из трех этапов:

  1. Открытие сеанса с помощью clientSession().
  2. Асинхронная отправка запроса GET с помощью session.get().
  3. Доступ к данным ответа с помощью таких методов, как await response.text().

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

Учитывая это, вы можете использовать AIOHTTP для получения HTML-кода главной страницы с помощью следующей логики:

async with aiohttp.ClientSession() as session:
    async with session.get("http://quotes.toscrape.com") as response:
        # Access the HTML of the target page
        html = await response.text()

AIOHTTP без отображения сведений о своих действиях отправляет запрос на сервер и ожидает ответа, содержащего HTML-код страницы. После получения ответа await response.text() извлекает HTML-контент в виде строки.

Распечатайте переменную html , и вы увидите:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quotes to Scrape</title>
    <link rel="stylesheet" href="/static/bootstrap.min.css">
    <link rel="stylesheet" href="/static/main.css">
</head>
<body>
    <!-- omitted for brevity... -->
</body>
</html>

Так держать! Вы успешно получили HTML-контент целевой страницы. Пришло время проанализировать этот контент и извлечь нужные данные.

Шаг 4. Парсинг HTML

Передайте HTML-контент в конструктор BeautifulSoup для его анализа:

# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(html, "html.parser")

html.parser — это HTML-парсер на Python по умолчанию, используемый для обработки контента.

Объект soup содержит подвергнутый парсингу код HTML и предоставляет методы для извлечения необходимых данных.

AIOHTTP выполнил извлечение HTML, и теперь вы переходите к обычному этапу парсинга данных с помощью BeautifulSoup. Для получения более подробной информации прочитайте наше руководство по веб-парсингу BeautifulSoup.

Шаг 5. Напишите логический механизм извлечения данных

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

# Where to store the scraped data
quotes = []

# Extract all quotes from the page
quote_elements = soup.find_all("div", class_="quote")

# Loop through quotes and extract text, author, and tags
for quote_element in quote_elements:
    text = quote_element.find("span", class_="text").get_text().get_text().replace("“", "").replace("”", "")
    author = quote_element.find("small", class_="author")
    tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

    # Store the scraped data
    quotes.append({
        "text": text,
        "author": author,
        "tags": tags
    })

Этот фрагмент инициализирует список под названием «цитаты» , предназначенный для хранения данных, полученных в ходе парсинга. Затем он идентифицирует все HTML-элементы цитат и циклические просматривает их, чтобы извлечь текст цитаты, автора и теги. Каждая извлеченная цитата хранится в виде словаря в списке «цитаты» , в котором данные упорядочены для последующего использования или экспорта.

Великолепно! Логический механизм парсинга создан.

Шаг 6. Экспорт данных, полученных в ходе парсинга

Используйте следующие строки кода для экспорта данных, полученных в ходе парсинга, в CSV-файл:

# Open the file for export
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
    
    # Write the header row
    writer.writeheader()
    
    # Write the scraped quotes data
    writer.writerows(quotes)

Приведенный выше фрагмент открывает файл с именем quotes.csv в режиме записи. Затем он настраивает заголовки столбцов (текставтортеги), записывает заголовки, а затем записывает каждый словарь из списка «цитаты» в CSV-файл.

csv.DictWriter упрощает форматирование данных, что делает проще хранение структурированных данных. Чтобы все заработало, не забудьте импортировать csv из стандартной библиотеки Python:

import csv

Шаг 7. Заключительная сборка

Вот как должен выглядеть ваш последний скрипт для веб-парсинга с помощью AIOHTTP:

import asyncio
import aiohttp
from bs4 import BeautifulSoup
import csv

# Define an asynchronous function to make the HTTP GET request
async def scrape_quotes():
    async with aiohttp.ClientSession() as session:
        async with session.get("http://quotes.toscrape.com") as response:
            # Access the HTML of the target page
            html = await response.text()

            # Parse the HTML content using BeautifulSoup
            soup = BeautifulSoup(html, "html.parser")

            # List to store the scraped data
            quotes = []

            # Extract all quotes from the page
            quote_elements = soup.find_all("div", class_="quote")

            # Loop through quotes and extract text, author, and tags
            for quote_element in quote_elements:
                text = quote_element.find("span", class_="text").get_text().replace("“", "").replace("”", "")
                author = quote_element.find("small", class_="author").get_text()
                tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

                # Store the scraped data
                quotes.append({
                    "text": text,
                    "author": author,
                    "tags": tags
                })

            # Open the file name for export
            with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
                writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])

                # Write the header row
                writer.writeheader()

                # Write the scraped quotes data
                writer.writerows(quotes)

# Run the asynchronous function
asyncio.run(scrape_quotes())

Вы можете запустить его с помощью:

python scraper.py

Или в Linux/macOS:

python3 scraper.py

Файл quotes.csv появится в корневой папке вашего проекта. Откройте его, и вы увидите:

Все готово! Вы только что узнали, как выполнять веб-парсинг с помощью AIOHTTP и BeautifulSoup.

AIOHTTP для веб-парсинга: расширенные функции и методы

Теперь, когда вы понимаете, как использовать AIOHTTP для базового веб-парсинга, пришло время рассмотреть более сложные сценарии.

В следующих примерах целевым сайтом будет конечная точка HTTPBin.io /anything. Это удобный API, который возвращает IP-адрес, заголовки и другие данные, отправленные запрашивающей стороной.

Приготовьтесь освоить AIOHTTP для веб-парсинга!

Настройка пользовательских заголовков

Вы можете указать собственные заголовки в запросе AIOHTTP с аргументом headers :

import aiohttp
import asyncio

async def fetch_with_custom_headers():
    # Custom headers for the request
    headers = {
        "Accept": "application/json",
        "Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,es-US;q=0.6,es;q=0.5,it-IT;q=0.4,it;q=0.3"
    }

    async with aiohttp.ClientSession() as session:
        # Make a GET request with custom headers
        async with session.get("https://httpbin.io/anything", headers=headers) as response:
            data = await response.json()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_headers())

Таким образом, AIOHTTP отправит HTTP-запрос GET с заданными заголовками Accept и Accept-Language .

Настройка особого пользовательского агента

User-Agent — один из наиболее важных HTTP-заголовков для веб-парсинга. По умолчанию AIOHTTP использует следующий пользовательский агент:

Python/<PYTHON_VERSION> aiohttp/<AIOHTTP_VERSION>

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

Чтобы снизить вероятность обнаружения, вы можете настроить собственный реальный пользовательский агент, как и раньше:

import aiohttp
import asyncio

async def fetch_with_custom_user_agent():
    # Define a Chrome-like custom User-Agent
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"
    }

    async with aiohttp.ClientSession(headers=headers) as session:
        # Make a GET request with the custom User-Agent
        async with session.get("https://httpbin.io/anything") as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_user_agent())

Узнайте про лучшие пользовательские агенты для веб-парсинга!

Установка файлов cookie

Как и в HTTP-заголовках, вы можете настроить пользовательские файлы cookie, используя файлы cookie в СlientSession():

import aiohttp
import asyncio

async def fetch_with_custom_cookies():
    # Define cookies as a dictionary
    cookies = {
        "session_id": "9412d7hdsa16hbda4347dagb",
        "user_preferences": "dark_mode=false"
    }

    async with aiohttp.ClientSession(cookies=cookies) as session:
        # Make a GET request with custom cookies
        async with session.get("https://httpbin.io/anything") as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_cookies())

Файлы cookie помогают включать необходимые данные сеанса в запросы веб-парсинга.

Обратите внимание, что файлы cookie, установленные в ClientSession, используются для всех запросов, сделанных в этом сеансе. Чтобы получить доступ к сеансовым файлам cookie, обратитесь к ClientSession.cookie_jar.

Интеграция прокси-серверов

В AIOHTTP вы можете направлять запросы через прокси-сервер, чтобы снизить риск блокировки IP-адресов. Для этого используйте аргумент proxy в функции HTTP-метода в сеансе:

import aiohttp
import asyncio

async def fetch_through_proxy():
    # Replace with the URL of your proxy server
    proxy_url = "<YOUR_PROXY_URL>"

    async with aiohttp.ClientSession() as session:
        # Make a GET request through the proxy server
        async with session.get("https://httpbin.io/anything", proxy=proxy_url) as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_through_proxy())

Узнайте, как выполнять аутентификацию и ротацию прокси-серверов, из нашего руководства по использованию прокси в AIOHTTP.

Обработка ошибок

По умолчанию AIOHTTP вызывает ошибки только из-за проблем с подключением или сетью. Чтобы создать исключения для HTTP-ответов при получении кодов состояния 4xx и 5xx , вы можете использовать любой из следующих подходов:

  1. Задайте raise_for_status=True при создании ClientSession: автоматически создавать исключения для всех запросов, сделанных в сеансе, если статус ответа равен 4xx или 5xx.
  2. Передайте raise_for_status=True непосредственно методам запроса: включите выдачу ошибок для отдельных методов запроса (например, session.get() или session.post()), не затрагивая другие.
  3. Вызовите response.raise_for_status() вручную: дайте полный контроль над тем, когда создавать исключения, и принимать решения по каждому запросу.

Пример варианта №1:

import aiohttp
import asyncio

async def fetch_with_session_error_handling():
    async with aiohttp.ClientSession(raise_for_status=True) as session:
        try:
            async with session.get("https://httpbin.io/anything") as response:
                # No need to call response.raise_for_status(), as it is automatic
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_session_error_handling())

Если на уровне сеанса установлено значение raise_for_status=True , все запросы, отправленные в этом сеансе, вызовут ошибку aiohttp.ClientResponseError для ответов 4xx или 5xx.

Пример варианта №2:

import aiohttp
import asyncio

async def fetch_with_raise_for_status():
    async with aiohttp.ClientSession() as session:
        try:
            async with session.get("https://httpbin.io/anything", raise_for_status=True) as response:
                # No need to manually call response.raise_for_status(), it is automatic
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_raise_for_status())

В этом случае аргумент raise_for_status=True передается непосредственно в вызов session.get(). Это гарантирует автоматическое создание исключения для любых кодов состояния 4xx или 5xx .

Пример варианта №3:

import aiohttp
import asyncio

async def fetch_with_manual_error_handling():
    async with aiohttp.ClientSession() as session:
        try:
            async with session.get("https://httpbin.io/anything") as response:
                response.raise_for_status()  # Manually raises error for 4xx/5xx
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_manual_error_handling())

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

Повтор неудачных запросов

AIOHTTP не предоставляет встроенной поддержки автоматической повторной отправки запросов. Чтобы реализовать это, вы должны использовать собственную логику или стороннюю библиотеку, такую как aiohttp-retry. Она позволяет настроить логику повторных попыток для неудачных запросов, что поможет решить временные проблемы с сетью, таймаутами или ограничениями скорости.

Установите aiohttp-retry с помощью:

pip install aiohttp-retry

Затем вы можете использовать ее следующим образом:

import asyncio
from aiohttp_retry import RetryClient, ExponentialRetry

async def main():
    retry_options = ExponentialRetry(attempts=1)
    retry_client = RetryClient(raise_for_status=False, retry_options=retry_options)
    async with retry_client.get("https://httpbin.io/anything") as response:
        print(response.status)
        
    await retry_client.close()

Это настраивает поведение при повторных попытках с использованием стратегии экспоненциального прерывания. Узнайте больше в официальных документах.

AIOHTTP и запросы на веб-парсинг

Ниже приведена сводная таблица для сравнения запросов AIOHTTP и Requests при веб-парсинге:

Особенности AIOHTTP Requests
Звезды на GitHub 15,3 тыс. 52,4 тыс.
Поддержка клиентов ✔️ ✔️
Поддержка синхронизации ✔️
Поддержка асинхронного режима ✔️
Поддержка серверов ✔️
Пул соединений ✔️ ✔️
/Поддержка HTTP/2
Настройка пользовательского агента ✔️ ✔️
Поддержка прокси-сервера ✔️ ✔️
Обработка файлов cookie ✔️ ✔️
Механизм повторов Доступность только через стороннюю библиотеку Доступно через HTTPAdapters
Производительность Высокая Средняя
Поддержка сообщества и популярность Средняя Большая

Для полного сравнения ознакомьтесь с нашей публикацией в блоге со сравнением Requests, HTTPX и AIOHTTP.

Узнайте, как выполнять парсинг веб-сайтов с помощью HTTPX.

Заключение

Из этой статьи вы узнали, как использовать библиотеку aiohttp для веб-парсинга. Вы узнали, что это такое, какие функции и преимущества она предлагает. AIOHTTP — это быстрый и надежный выбор для HTTP-запросов при сборе онлайн-данных.

Однако автоматические HTTP-запросы отображают ваш публичный IP-адрес. Это может раскрыть вашу личность и местоположение, что поставит под угрозу вашу конфиденциальность. Чтобы обеспечить вашу безопасность и конфиденциальность, одной из наиболее эффективных стратегий является использование прокси-сервера для сокрытия своего IP-адреса.

Bright Data контролирует лучшие прокси-серверы в мире, обслуживая компании из списка Fortune 500 и более 20 000 клиентов. Предложение компании включает в себя широкий ассортимент прокси-серверов различных типов:

Создайте бесплатную учетную запись Bright Data сегодня, чтобы протестировать наши прокси-серверы и решения для парсинга!

Кредитная карта не требуется