Как ускорить работу модуля запросов при очистке? - PullRequest
0 голосов
/ 03 октября 2018

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

Людям очень легкоответьте на вопрос, думая, что «я ответил на ваш вопрос, если вы не понимаете, что это ваша вина», так что теперь мне нужна помощь в понимании или просто в упрощении процесса.

У меня естьсписок около 300 000 URL, которые я посещаю с помощью модуля запросов pythons.время, необходимое для загрузки / загрузки URL-адреса, довольно болезненное, что, как я полагаю, связано с количеством содержимого, расположенного в URL-адресе.Я, вероятно, на 15-20 секунд на запрос.Я пытаюсь придумать, каким образом я могу значительно сократить это количество времени.

Моей первой мыслью было, могу ли я каким-то образом отключить / отфильтровать изображения и все, что я знаю заранее.Я не буду нуждаться в использовании запросов.Я не уверен, как реализовать это или даже можно ли это сделать.

Моя вторая идея - отправлять «пакетные запросы», что для меня похоже на одновременную отправку нескольких запросов.Я действительно не уверен, что это на самом деле быстрее, я не смог получить точный ответ на мой запрос, так как не могу заставить свой кусок кода работать.Я предполагаю, что я могу отправить X запросов за один раз, получить X ответов в ответ и просто обработать каждый в отдельности.То, что я пытался использовать для решения этой проблемы, приведено ниже.

def getpage(list_urls):
    for url in list_urls:
        r = requests.get(url)
        dostuffwithresponse()

for file in list_files:
    list_links = open(file).readlines()
    pool = multiprocessing.Pool(processes = 10)
    pool_outputs = pool.map(getpage(), list_links)
    pool.close()
    pool.join()
    print('*')
    print(pool_outputs)

Между уменьшением размера моего ответа, если это возможно, и отправкой нескольких запросов.Моя цель - сократить время ожидания в 15 секунд + до 5 секунд и менее (или настолько хорошо, насколько я могу это сделать).

Есть ли у кого-нибудь хорошее предложение по более простому и прямому пути решения этой проблемы?

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

@ Александр Дашков дал ссылку на очень полезное руководство, чтобы можно было довольно эффективно отправлять миллионы запросов с aiohttp и asyncio

Я попытаюсь сжать эту информацию до чего-то, что должно помочь вамс вашей проблемой.

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

Начнем с того, как работает базовая выборка в aiohttp.Это очень похоже на requests.

import asyncio

import aiohttp

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            dostuffwithresponse()  # To mimic your code.

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

# If you're on Python 3.7 :o
asyncio.run(main())

Довольно просто.Если вы использовали объект session запросов, он должен быть практически идентичен, кроме синтаксиса async.

Теперь мы хотим получить много URL-адресов.Мы также не хотим каждый раз воссоздавать объект сеанса.

async def fetch(session, url):
    async with session.get(url) as response:
        dostuffwithresponse()

async def main():
    async with aiohttp.ClientSession() as session:
        for file in list_files:
            for link in open(file).readlines():
                await fetch(session, url)

Теперь мы выбираем все URL-адреса.Это то же самое поведение, все еще синхронное, потому что мы ждем завершения fetch (), прежде чем перейти к следующей ссылке.

async def fetch(session, url):
    ...

async def main():
    tasks = []
    async with aiohttp.ClientSession() as session:
        for file in list_files:
            for link in open(file).readlines():
                task = asyncio.ensure_future(fetch(session, url))
                tasks.append(fut)
        results = await asyncio.gather(*tasks)
    # results is a list of everything that returned from fetch().
    # do whatever you need to do with the results of your fetch function

Здесь я бы предложил вам попытаться понять, что такое asyncio.ensure_future() иasyncio.gather() делает.В Python 3.7 есть новая обновленная документация об этом, и есть много постов в блоге об этом.

Наконец, вы не можете получить 300 000 ссылок одновременно.Скорее всего, ваша ОС выдаст вам ошибки о том, что вы не можете открыть столько дескрипторов файлов или что-то похожее на это.

Итак, вы решили бы эту проблему, используя семафор.В этом случае вам нужно использовать asyncio.Semaphore(max_size) или asyncio.BoundedSemaphore(max_size)

async def fetch(session, url):
    ...

async def bounded_fetch(sem, url, session):
    async with sem:
        await fetch(url, session)

async def main():
    tasks = []
    sem = asyncio.Semaphore(1000)  # Generally, most OS's don't allow you to make more than 1024 sockets unless you personally fine-tuned your system. 
    async with aiohttp.ClientSession() as session:
        for file in list_files:
            for link in open(file).readlines():
                # Notice that I use bounded_fetch() now instead of fetch()
                task = asyncio.ensure_future(bounded_fetch(sem, session, url))
                tasks.append(fut)
        results = await asyncio.gather(*tasks)
    # do whatever you need to do with the results of your fetch function

Почему это все быстрее?

Итак, asyncio в основном работает, когда вы отправляете запрос в Интернетсервер, вы не хотите тратить время на ожидание ответа.Вместо этого создается событие, чтобы сообщить циклу событий о получении ответа.В то время как вы ожидаете, что произойдет 1 ответ, вы продолжаете и делаете еще один запрос (он же задает цикл событий для следующего задания) и продолжаете.

Я определенно не лучший в объяснении всего этого, но я надеюсь, что это помогло вам понять, как ускорить процесс создания веб-страницУдачи!

0 голосов
/ 03 октября 2018

Отправка пакета асинхронных запросов - это путь.Как уже упоминалось @NinjaKitty, вы можете использовать aiohttp.Недавно мне пришлось сделать нечто подобное, и я обнаружил, что мне проще использовать requests_futures.Вы можете настроить цикл, чтобы сделать N асинхронных запросов с функцией обратного вызова для каждого.Затем дождитесь завершения всех N и перейдите к следующему N.

...