Несколько (асинхронных) соединений с urllib2 или другой библиотекой http?

У меня есть такой код

for p in range(1,1000):
    result = False
    while result is False:
        ret = urllib2.Request('http://server/?'+str(p))
        try:
            result = process(urllib2.urlopen(ret).read())
        except (urllib2.HTTPError, urllib2.URLError):
            pass
    results.append(result)

Я хотел бы сделать два или три запроса одновременно, чтобы ускорить это. Могу ли я использовать urllib2 для этого и как? Если нет, какую другую библиотеку я должен использовать? Благодарю.

7 ответов

Итак, это 2016 год, и у нас есть Python 3.4+ со встроенным модулем asyncio для асинхронного ввода-вывода. Мы можем использовать aiohttp в качестве HTTP-клиента для параллельной загрузки нескольких URL.

import asyncio
from aiohttp import ClientSession

async def fetch(url):
    async with ClientSession() as session:
        async with session.get(url) as response:
            return await response.read()

async def run(loop, r):
    url = "http://localhost:8080/{}"
    tasks = []
    for i in range(r):
        task = asyncio.ensure_future(fetch(url.format(i)))
        tasks.append(task)

    responses = await asyncio.gather(*tasks)
    # you now have all response bodies in this variable
    print(responses)

loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(loop, 4))
loop.run_until_complete(future)

Источник: скопировано с http://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html

Вы можете использовать асинхронный ввод-вывод для этого.

запросы + gevent = греки

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

import grequests

urls = [
    'http://www.heroku.com',
    'http://tablib.org',
    'http://httpbin.org',
    'http://python-requests.org',
    'http://kennethreitz.com'
]

rs = (grequests.get(u) for u in urls)
grequests.map(rs)

Взгляните на gevent - сетевую библиотеку Python на основе сопрограмм, которая использует greenlet для обеспечения высокоуровневого синхронного API поверх цикла событий libevent.

Пример:

#!/usr/bin/python
# Copyright (c) 2009 Denis Bilenko. See LICENSE for details.

"""Spawn multiple workers and wait for them to complete"""

urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org']

import gevent
from gevent import monkey

# patches stdlib (including socket and ssl modules) to cooperate with other greenlets
monkey.patch_all()

import urllib2


def print_head(url):
    print 'Starting %s' % url
    data = urllib2.urlopen(url).read()
    print '%s: %s bytes: %r' % (url, len(data), data[:50])

jobs = [gevent.spawn(print_head, url) for url in urls]

gevent.joinall(jobs)

Ответ 2021 года с использованием современных асинхронных библиотек

Ответ 2016 года хорош, но я подумал, что добавлю другой ответ с httpx вместо aiohttp, поскольку httpx является только клиентом и поддерживает разные асинхронные среды. Я опускаю цикл for OP с URL-адресами, построенными из числа, соединенного со строкой, для того, что я считаю более общим ответом.

      import asyncio
import httpx

# you can have synchronous code here

async def getURL(url):
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        # we could have some synchronous code here too
        # to do CPU bound tasks on what we just fetched for instance
        return response

# more synchronous code can go here

async def main():
    response1, response2 = await asyncio.gather(getURL(url1),getURL(url2))
    # do things with the responses
    # you can also have synchronous code here

asyncio.run(main()) 

Код после любого ожидания в блоке async with будет запущен, как только ожидаемая задача будет выполнена. Это хорошее место, чтобы проанализировать ваш ответ, не дожидаясь завершения всех ваших запросов.

Код после asyncio.gather будет запущен после завершения всех задач. Это хорошее место для выполнения операций, требующих информации из всех запросов, возможно, предварительно обработанных в асинхронной функции, вызываемой функцией gather.

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

list_of_requests = ['http://moop.com', 'http://doop.com', ...]

from simple_requests import Requests
for response in Requests().swarm(list_of_requests):
    print response.content

Документы находятся здесь: http://pythonhosted.org/simple-requests/

Возможно, используя многопроцессорность и разделите вашу работу на 2 процесса или около того.

Вот пример (это не проверено)

import multiprocessing
import Queue
import urllib2


NUM_PROCESS = 2
NUM_URL = 1000


class DownloadProcess(multiprocessing.Process):
    """Download Process """

    def __init__(self, urls_queue, result_queue):

        multiprocessing.Process.__init__(self)

        self.urls = urls_queue
        self.result = result_queue

    def run(self):
        while True:

             try:
                 url = self.urls.get_nowait()
             except Queue.Empty:
                 break

             ret = urllib2.Request(url)
             res = urllib2.urlopen(ret)

             try:
                 result = res.read()
             except (urllib2.HTTPError, urllib2.URLError):
                     pass

             self.result.put(result)


def main():

    main_url = 'http://server/?%s'

    urls_queue = multiprocessing.Queue()
    for p in range(1, NUM_URL):
        urls_queue.put(main_url % p)

    result_queue = multiprocessing.Queue()

    for i in range(NUM_PROCESS):
        download = DownloadProcess(urls_queue, result_queue)
        download.start()

    results = []
    while result_queue:
        result = result_queue.get()
        results.append(result)

    return results

if __name__ == "__main__":
    results = main()

    for res in results:
        print(res)
Другие вопросы по тегам