Парсинг путем отправки одновременных запросов с помощью python

У меня есть python 3.4, и я установил запросы и несколько других необходимых программ для очистки веб-страниц. Моя проблема в том, что я хотел бы очистить около 7000 страниц (только html/текст), и не хочу делать это все сразу, я хотел бы иметь какую-то задержку, чтобы я не попал на серверы со слишком большим количеством запросов и потенциально могут быть забанены. Я слышал о grequests, но, видимо, у них его нет для python 3.4 (фактическая ошибка говорит, что он не может найти vcvarsall.bat, но в документации я не видел поддержки для 3.4). Кто-нибудь знает альтернативную программу, которая могла бы управлять запросами URL? Другими словами, я не стремлюсь схватить все как можно быстрее, а скорее медленно и стабильно.


person thatandrey    schedule 14.08.2014    source источник
comment
возможный дубликат сделать несколько тысяч запросов get к sourceforge get max r">stackoverflow.com/questions/21978115/   -  person remudada    schedule 14.08.2014
comment
@remudada Спасибо, да, я видел это, и если бы я мог установить grequests, я мог бы решить свою проблему, но я не могу установить его на python 3.4, поэтому я искал альтернативные решения?   -  person thatandrey    schedule 14.08.2014
comment
Ваш вопрос не по теме, но я могу дать вам ответ: Scrapy scrapy.org   -  person Maxime Lorant    schedule 14.08.2014


Ответы (1)


Я предлагаю запустить собственную многопоточную программу для выполнения запросов. Я обнаружил, что concurrent.futures это самый простой способ многопоточного выполнения таких запросов, в частности, с помощью ThreadPoolExecutor. У них даже есть пример простого многопоточного запроса URL в документации.

Что касается второй части вопроса, это действительно зависит от того, насколько/как вы хотите ограничить свои запросы. Для меня установка достаточно низкого аргумента max_workers и, возможно, включение ожидания time.sleep в мою функцию было достаточно, чтобы избежать каких-либо проблем даже при очистке десятков тысяч страниц, но это, очевидно, намного больше зависит от сайта, который вы пытаетесь очистить. Тем не менее, не должно быть сложно реализовать какую-то пакетную обработку или ожидание.

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

import concurrent.futures as futures
import requests
from requests.exceptions import HTTPError

urllist = ...

def get_url_data(url, session):
    try:
        r = session.get(url, timeout=10)
        r.raise_for_status()
    except HTTPError:
        return None

    return r.text

s = requests.Session()

try:
    with futures.ThreadPoolExecutor(max_workers=5) as ex:
        future_to_url = {ex.submit(get_url_data, url, s): url
                         for url in urlist}

    results = {future_to_url[future]: future.result() 
               for future in futures.as_completed(future_to_url)}
finally:
    s.close() 
person Roger Fan    schedule 14.08.2014
comment
Спасибо, это выглядит многообещающе, мое программирование ужасно медленное, так как я все еще новичок, но я попробую это и вернусь к вам завтра! Означает ли это, что я буду использовать urllib вместо запросов? Но я все еще могу использовать BeautifulSoup для синтаксического анализа, верно? - person thatandrey; 14.08.2014
comment
Нет, вы почти наверняка должны использовать requests. Я посмотрю, смогу ли я найти старый код, который я использовал, и отредактировать его в ответе. - person Roger Fan; 14.08.2014
comment
Спасибо, вроде все! Я все еще пытаюсь понять, как извлечь данные, так как я также новичок в ссылках CSS. Это помещает все файлы html в результаты? Смогу ли я для каждого элемента в результатах запустить цикл for и сделать что-то вроде test = bs4.BeautifulSoup(results[0].text) ? - person thatandrey; 14.08.2014
comment
С надеждой! Или, в зависимости от количества сайтов и того, как вы хотите их обрабатывать, вы можете добавить любой анализ и сохранение в свою версию функции get_url_data, и в этом случае вам может вообще не понадобиться возвращать results dict. - person Roger Fan; 14.08.2014
comment
Спасибо, пожалуй так и сделаю. И последний вопрос: похоже, вы не использовали time.sleep. Если бы я хотел, мог бы я просто добавить sleep(1) в конец функции get_url_data? - person thatandrey; 14.08.2014
comment
Да. Хотя я почти уверен, что time.sleep выпускает GIL, поэтому он позволит другим потокам работать в это время. В качестве альтернативы, если вы хотите реализовать периодические паузы, вы можете разделить URL-адреса на пакеты и выполнять их по одному пакету за раз с паузами между ними. - person Roger Fan; 14.08.2014