Как ускорить веб-скрейпинг с помощью конкурентного выполнения в Python

Как ускорить веб-скрейпинг с помощью конкурентного выполнения в Python

В этом руководстве я покажу, как использовать asyncio и aiohttp в Python, чтобы сделать ваш Веб-скрейпинг и автоматизация: быстрее и эффективнее. Я разберу это по шагам, чтобы вы могли повысить производительность своих скриптов для скрейпинга и быстро получать нужные данные. Давайте начнем!

Что такое конкурентное выполнение?

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

Лучшая альтернатива веб-скрейпингу на Python

Веб-скрейперы Bright Data предлагают комплексное решение корпоративного уровня для ваших задач по извлечению данных. Благодаря доступу к обширной прокси-сети и продвинутым антибот-механизмам Bright Data упрощает и делает надёжнее масштабирование проектов веб-скрапинга. Независимо от того, собираете ли вы данные для маркетинговых исследований или автоматизируете крупномасштабные задачи скрапинга, их мощная инфраструктура позволяет сосредоточиться на извлечении качественных данных без лишних сложностей.

Если вам интересно узнать и о других инструментах, загляните на мою список лучших инструментов для веб-скрейпинга.

Последовательный скрапинг: медленный способ

Let’s first look at how a typical scraping script works without concurrency. Imagine you need to scrape a website with 12 pages, each taking about 2 seconds to load. A sequential approach means:

  1. Вы отправляете запрос к первой странице.
  2. Подождите 2 секунды, пока сервер ответит.
  3. Обработайте данные.
  4. Переходите к следующей странице и повторяйте процесс.

Если на каждую страницу уходит 2 секунды, то при последовательном обходе всех 12 страниц общее время составит 24 секунды.

Вот пример того, как это можно реализовать в Python с помощью библиотеки requests:

импорт requests
с сайта bs4 импорт BeautifulSoup
импорт csv
base_url = "https://example.com/products/page"
pages = ассортимент(1, 13) # Scrape 12 pages
def extract_data(страница):
url = f"{base_url}/{page}/"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
# Extract data here (e.g., product name, price)
продукты = []
для продукт в soup.select(".product"):
products.append({
"имя": product.find("h2").text.strip(),
"цена": product.find(class_="цена").text.strip(),
"url": product.find("a").get("href")
})
возврат продукция
def store_results(продукция):
с открыть("products.csv", "w") в роли file:
writer = csv.DictWriter(file, fieldnames=products[0].keys())
writer.writeheader()
writer.writerows(products)
all_products = []
для страница в pages:
all_products.extend(extract_data(page))
store_results(all_products)

Запуск приведённого выше кода

Когда вы запустите скрипт, программа будет выполняться около 24 секунд (по 2 секунды на каждую из 12 страниц). Для небольших сайтов это нормально, но становится проблемой, когда нужно собрать большое количество страниц.

Проблема последовательного скрейпинга

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

Конкурентность: ускорение скрапинга с помощью asyncio и aiohttp

Чтобы устранить неэффективность последовательного скрейпинга, можно использовать конкурентное выполнение. Это позволяет отправлять несколько запросов одновременно и обрабатывать ответы по мере их поступления. Мы будем использовать asyncio и aiohttp, которые предназначены для асинхронного программирования на Python.

Что такое aiohttp?

Aiohttp — асинхронная библиотека HTTP-клиента и сервера для Python. Она отлично работает с asyncio, позволяя отправлять HTTP-запросы без блокировки основного потока выполнения. В отличие от библиотеки requests, которая является синхронной, aiohttp поддерживает асинхронные операции.

Скрейпинг с помощью asyncio и aiohttp

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

Шаг 1: Установка необходимых библиотек

Прежде чем начать, нужно установить aiohttp и beautifulsoup4:

pip install aiohttp beautifulsoup4

Шаг 2: Реализация конкурентного выполнения

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

импорт aiohttp
импорт asyncio
с сайта bs4 импорт BeautifulSoup
импорт csv
base_url = "https://example.com/products/page"
pages = ассортимент(1, 13) # Scrape 12 pages
async def extract_data(page, session):
url = f"{base_url}/{page}/"
async с session.get(url) в роли response:
soup = BeautifulSoup(ожидайте response.text(), "html.parser")
products = []
для продукт в soup.select(".product"):
products.append({
"имя": product.find("h2").text.strip(),
"цена": product.find(class_="цена").text.strip(),
"url": product.find("a").get("href")
})
возврат продукция
async def главная():
async с aiohttp.ClientSession() в роли session:
tasks = [extract_data(page, session) для страница в pages]
results = ожидайте asyncio.gather(*tasks)
all_products = [item для sublist в результаты для пункт в sublist] # Flatten the list
store_results(all_products)
def store_results(продукция):
с открыть("products.csv", "w") в роли file:
writer = csv.DictWriter(file, fieldnames=products[0].keys())
writer.writeheader()
writer.writerows(products)
# Run the asyncio event loop
asyncio.run(main())

Ключевые изменения в коде

  1. Asynchronous Requests: Мы заменили requests.get() with session.get() from aiohttp. The async with statement ensures that the request is handled asynchronously.
  2. Async Function: The extract_data function is now asynchronous, denoted by async def. The await keyword is used to wait for the response from the server without blocking the entire program.
  3. Concurrent Tasks: We use asyncio.gather() to execute multiple tasks concurrently. Each task is responsible for scraping one page.
  4. Flattening Results: После одновременного сбора всех страниц результаты сохраняются в results. Поскольку asyncio.gather() возвращает список списков, поэтому его нужно преобразовать в один список всех товаров.

Шаг 3: Запуск кода

Теперь, когда вы запустите обновлённый скрипт, он будет собирать все 12 страниц одновременно. Время выполнения будет значительно меньше, чем у последовательной версии. Точная скорость будет зависеть от вашей сети и времени ответа сервера, но обычно вы увидите заметное ускорение.

Performance Comparison

  • Sequential Scraping: Как уже упоминалось, последовательный скрейпинг 12 страниц занял бы около 24 секунд.
  • Конкурентный веб-скрейпинг с asyncio: В версии с одновременным выполнением скрипт может справиться с той же задачей примерно за 5–10 секунд в зависимости от сетевой задержки.

Ограничение числа одновременных запросов, чтобы не перегружать сервер

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

Пример с семафором

max_concurrency = 5 # Limit concurrent requests
sem = asyncio.Semaphore(max_concurrency)
async def extract_data(page, session):
async с sem: # This limits the number of concurrent requests
url = f"{base_url}/{page}/"
async с session.get(url) в роли response:
soup = BeautifulSoup(ожидайте response.text(), "html.parser")
products = []
для продукт в soup.select(".product"):
products.append({
"имя": product.find("h2").text.strip(),
"цена": product.find(class_="цена").text.strip(),
"url": product.find("a").get("href")
})
возврат продукция

Заключение

Ограничивая уровень конкурентности, мы гарантируем, что одновременно отправляется только определённое число запросов. Это снижает риск перегрузить сервер или быть заблокированным.

Итак, вот и все: мы узнали, как ускорить веб-скрейпинг с помощью конкурентного выполнения на asyncio и aiohttp в Python. Отправляя несколько запросов одновременно, можно значительно сократить время, необходимое для скрейпинга сайта, особенно при большом количестве страниц. Не забывайте соблюдать условия использования сайта и не перегружать сервер слишком большим числом запросов. Удачного скрейпинга!

Похожие записи