Понимание запросов против греков

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

  1. Возьмите какой-нибудь список URL.
  2. Получить Responseобъект от каждого.
  3. Создайте объект BeautifulSoup изtextкаждого ответа.
  4. Вытащите текст определенного тега из этого объекта BeautifulSoup.

Насколько я понимаю, это кажется идеальным для греков:

GRequests позволяет вам использовать запросы с Gevent, чтобы легко выполнять асинхронные HTTP-запросы.

Но, тем не менее, два процесса (один с запросами, другой с запросами), похоже, дают разные результаты, при этом некоторые запросы возвращаются None а не ответ.

Использование запросов

import requests

tickers = [
    'A', 'AAL', 'AAP', 'AAPL', 'ABBV', 'ABC', 'ABT', 'ACN', 'ADBE', 'ADI', 
    'ADM',  'ADP', 'ADS', 'ADSK', 'AEE', 'AEP', 'AES', 'AET', 'AFL', 'AGN', 
    'AIG', 'AIV', 'AIZ', 'AJG', 'AKAM', 'ALB', 'ALGN', 'ALK', 'ALL', 'ALLE',
    ]

BASE = 'https://finance.google.com/finance?q={}'

rs = (requests.get(u) for u in [BASE.format(t) for t in tickers])
rs = list(rs)

rs
# [<Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # ...
 # <Response [200]>]

# All are okay (status_code == 200)

Использование grequests

# Restarted my interpreter and redefined `tickers` and `BASE`
import grequests

rs = (grequests.get(u) for u in [BASE.format(t) for t in tickers])
rs = grequests.map(rs)

rs
# [None,
 # <Response [200]>,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # None,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>,
 # <Response [200]>]

Почему разница в результатах?

Обновление: я могу напечатать тип исключения следующим образом. Связанное обсуждение здесь, но я понятия не имею, что происходит.

def exception_handler(request, exception):
    print(exception)

rs = grequests.map(rs, exception_handler=exception_handler)

# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
# ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)

Информация о системе / версии

  • запросы: 2.18.4
  • греки: 0.3.0
  • Python: 3.6.3
  • urllib3: 1,22
  • pyopenssl: 17.2.0
  • Все через Анаконду
  • Система: та же проблема на Mac OSX HS и Windows 10, сборка 10.0.16299

2 ответа

Решение

Вы просто слишком быстро отправляете запросы. Как grequests это асинхронная библиотека, все эти запросы почти отправляются одновременно. Их слишком много.

Вам просто нужно ограничить количество одновременных задач grequests.map(rs, size=your_choice)Я проверил grequests.map(rs, size=10) и это работает хорошо.

Я не знаю точную причину наблюдаемого поведения с .map(), Однако, используя .imap() функция с size=1 всегда возвращал 'Response 200' за мои несколько минут тестирования. Вот фрагмент кода:

rs = (grequests.get(u) for u in [BASE.format(t) for t in tickers])
rsm_iterator = grequests.imap(rs, exception_handler=exception_handler, size=1)
rsm_list = [r for r in rsm_iterator]
print(rsm_list)

И если вы не хотите ждать завершения всех запросов, прежде чем работать над их ответами, вы можете сделать это следующим образом:

rs = (grequests.get(u) for u in [BASE.format(t) for t in tickers])
rsm_iterator = grequests.imap(rs, exception_handler=exception_handler, size=1)
for r in rsm_iterator:
    print(r)
Другие вопросы по тегам