Parsing / Сбор информации, Анализ данных

Многопоточный парсинг на Python

Время прочтения: 3 мин.

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

В статье я опишу один из способов борьбы с этой проблемой.

Использоваться будет библиотека «concurrent.futures». Использовать «concurrent» нужно умеренно, не стоит устраивать «DDoS» атаку на сайт.

Для создания многопотока ничего мудрёного делать не придётся, код очень прост и понятен. После установки импортируем библиотеки. Добавлены 2 дополнительные библиотеки, это «tqdm» индикатор процесса и «pandas» для создания датафрейма.

import requests
import pandas as pd
from tqdm import tqdm
import concurrent.futures

Прописываем «headers» в request запросы, создаем сессию, объявляем функцию отправки запросов

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.146 Safari/537.36'
}
TIMEOUT = 10

session = requests.Session()
session.headers = headers

def load_url(url):
    answer = requests.get(url, headers=headers, timeout=TIMEOUT)
    return [url, answer.text]

Создаем датафрейм со списком «url» из файла «test.xlsx». Для тестирования берем только первые 2000 записей

df = pd.read_excel('test.xlsx')
df.head()
 Index  source.objectHrefTerm
0https://test.ru/PurchaseView/43/0/0/705675
1https://test.ru/PurchaseView/30/0/0/705663
2https://test.ru/PurchaseView/43/0/0/705661
3https://test.ru/PurchaseView/30/0/0/705653
4https://test.ru/PurchaseView/20/0/0/705300
urls = list(df['source.objectHrefTerm'])[:2000]

Вначале протестируем время выгрузки одним потоком

out = []
for url in tqdm(urls):
    out.append(load_url(url))

Прогресс-бар показывает скорость выполнения — 2 минуты и 21 секунду. Среднее количество запросов в секунду — 14.15.

Теперь сделаем те же 2000 запросов, используя concurrent.futures и 2 потока. Изменяется только параметр CONNECTIONS

out = []
CONNECTIONS = 2

with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
    future_to_url = (executor.submit(load_url, url) for url in urls)
    for future in tqdm(concurrent.futures.as_completed(future_to_url), total=len(urls)):
        try:
            data = future.result() 
        except Exception as exc:      
            data = str(type(exc))
        finally:
            out.append(data)

Прогресс-бар показал скорость выполнения 1 минута и 16 секунд. В среднем 26.06 запроса в секунду.

Наблюдается уже значительное увеличение скорости. Попробуем использовать 5 потоков.

out = []
CONNECTIONS = 5

Скорость парсера увеличилась почти в 5 раз.

При использовании большего количества потоков не на все запросы приходили ответы либо на время блокировался ip адрес.

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

Советуем почитать