@ Александр Дашков дал ссылку на очень полезное руководство, чтобы можно было довольно эффективно отправлять миллионы запросов с 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 ответ, вы продолжаете и делаете еще один запрос (он же задает цикл событий для следующего задания) и продолжаете.
Я определенно не лучший в объяснении всего этого, но я надеюсь, что это помогло вам понять, как ускорить процесс создания веб-страницУдачи!