Использование grequests для отправки нескольких тысяч запросов на получение к sourceforge, превышение максимального количества попыток с URL-адресом

Я очень новичок во всем этом; Мне нужно получить данные о нескольких тысячах проектов sourceforge для статьи, которую я пишу. Все данные находятся в свободном доступе в формате json по адресу http://sourceforge.net/api/project/name/[имя проекта]/json. У меня есть список из нескольких тысяч таких URL-адресов, и я использую следующий код.

import grequests
rs = (grequests.get(u) for u in ulist)
answers = grequests.map(rs)

Используя этот код, я могу получить данные для любых 200 или около того проектов, которые мне нравятся, т.е. rs = (grequests.get(u) for u in ulist[0:199]) работает, но как только я перехожу к этому, все попытки встречают

ConnectionError: HTTPConnectionPool(host='sourceforge.net', port=80): Max retries exceeded with url: /api/project/name/p2p-fs/json (Caused by <class 'socket.gaierror'>: [Errno 8] nodename nor servname provided, or not known)
<Greenlet at 0x109b790f0: <bound method AsyncRequest.send of <grequests.AsyncRequest object at 0x10999ef50>>(stream=False)> failed with ConnectionError

Затем я не могу делать больше запросов, пока не выйду из python, но как только я перезапущу python, я могу сделать еще 200 запросов.

Я пытался использовать grequests.map(rs,size=200), но это, похоже, ничего не дает.


person crf    schedule 24.02.2014    source источник
comment
Бьюсь об заклад, у sourceforge есть лимит запросов API. Они, вероятно, разрешают только 200 запросов в 10 секунд на IP-адрес или около того.   -  person ajon    schedule 24.02.2014
comment
@ajon проблема, похоже, сохраняется ровно до тех пор, пока у меня работает один сеанс Python. Я просто попытался подождать две минуты между отправкой двух фрагментов размером 200 и получил сообщения об ошибках во втором. Но я могу отправлять запросы без ошибок практически сразу, пока я выхожу из Python между ними. Имеет ли это какой-то смысл для вас?   -  person crf    schedule 24.02.2014
comment
Да, как только вы превысите лимит, ваше соединение будет остановлено sourcforge. Таким образом, перезапуск сеанса будет работать, но вы не можете продолжать это делать.   -  person aIKid    schedule 24.02.2014
comment
@alKid Понятно. Есть ли способ перезапустить соединение, не выходя из python?   -  person crf    schedule 24.02.2014
comment
просто ограничьте скорость своих запросов, добавив time.sleep в свой цикл.   -  person roippi    schedule 24.02.2014
comment
@roippi скорость, похоже, не имеет значения. Я попробовал это с 4 секундами, и это не удалось, но, как я сказал ajon, похоже, ему все равно, как долго я жду между запросами. Это останавливает меня после 200-й, несмотря ни на что, и не позволяет мне делать больше, пока я не перезапущу python.   -  person crf    schedule 24.02.2014


Ответы (2)


В моем случае это было не ограничение скорости целевым сервером, а что-то гораздо более простое: я явно не закрывал ответы, поэтому они держали сокет открытым, а процессу python не хватало файловых дескрипторов.

Мое решение (не знаю точно, какое из них устранило проблему - теоретически любой из них должен) заключалось в следующем:

  • Установите stream=False в grequests.get:

     rs = (grequests.get(u, stream=False) for u in urls)
    
  • Явно вызовите response.close() после того, как я прочитаю response.content:

     responses = grequests.map(rs)
     for response in responses:
           make_use_of(response.content)
           response.close()
    

Примечание: просто уничтожить объект response (присвоить ему None, вызвать gc.collect()) было недостаточно — это не закрыло дескрипторы файла.

person Virgil    schedule 03.04.2014
comment
Вам приходилось делать куски запросов, чтобы это работало? Было бы идеально, если бы был способ передать значительно больший список URL-адресов в grequests и автоматически закрыть ответы, но это не похоже на вариант, основанный на коде и документации github. - person neelshiv; 09.03.2016

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

MAX_CONNECTIONS = 100 #Number of connections you want to limit it to
# urlsList: Your list of URLs. 

results = []
for x in range(1,pages+1, MAX_CONNECTIONS):
    rs = (grequests.get(u, stream=False) for u in urlsList[x:x+MAX_CONNECTIONS])
    time.sleep(0.2) #You can change this to whatever you see works better. 
    results.extend(grequests.map(rs)) #The key here is to extend, not append, not insert. 
    print("Waiting") #Optional, so you see something is done. 
person MaikelDotUk    schedule 21.12.2016