ciao a tutti, sto cercando di fare uno script python che manda delle richieste http il più velocemente possibile. In sostanza il programma prende una lista di url e fa delle richieste get in modo asyncrono con asyncio e aiohttp e poi salva le informazioni su file. Per ora il codice funziona bene, il problema è quando la lista di url supera i 200 o 300 elementi mettendoci un po di tempo... visto che asyncio usa un solo thread stavo pensando di usare il ThreadPool executor e splittare la lista di url in sottoliste e fa partire un thread asyncrono per ognuna di queste. Aggiungendo pero il ThreadPoolExecutor non funziona più il codice dandomi errori di coroutine e futures. Cercando su internet ho visto che dovrei usare "run_in_executor" o "run_thread_safe" ma non ho ben capito come usarle. Qualcuno potrebbe darmi una mano? Lascio il codice qua sotto.
e ottengo questo errore:
Python:
async def fetch(url, session):
async with session.get(url, ssl=False) as response:
# if response.status == 200:
html_body = await response.json()
url = url.split('/')[-2]
file_name = url if url != 'services' else 'live'
async with aiofiles.open(f'{output_dir}/{file_name}.json', 'w') as f:
await f.write(json.dumps(html_body, indent=4))
return html_body
async def fetch_with_sem(sem, session, url):
async with sem:
return await fetch(url, session)
async def run(url, session, sem):
tasks = [asyncio.create_task(fetch_with_sem(sem, session, url)) for url in url]
page_content = await asyncio.gather(*tasks, return_exceptions=True)
return page_content
async def main(urls):
number = len(urls) // 10 + 1
sem = asyncio.Semaphore(50)
loop = asyncio.get_event_loop()
print(loop)
connector = aiohttp.TCPConnector(limit_per_host=30, limit=50, ttl_dns_cache=100)
headers = {
'user-agent': get_user_agent(),
'sec-ch-ua-platform': "macOS",
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'x-eb-accept-language': 'it_IT',
'x-eb-marketid': '5',
'x-eb-platformid': '1',
}
async with ClientSession(loop=loop, connector=connector, headers=headers) as session:
if isinstance(urls, list):
with ThreadPoolExecutor(max_workers=25) as executor:
# page_content = [
# executor.submit(run, urls[number * i : number * (i + 1)], session, sem).result()
# for i in range(10) ]
page_content = [
await loop.run_in_executor(executor, run, urls[number * i : number * (i + 1)], session, sem)
for i in range(10)]
else:
tasks = [asyncio.create_task(fetch_with_sem(sem, session, urls))]
page_content = await asyncio.gather(*tasks, return_exceptions=True)
return page_content
uvloop.install()
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
dic = scrape()
link = get_url(dic)
start = time.time()
x = asyncio.run(main(link))
end = time.time()
print("\nIt took {} seconds to make {} API calls\n".format(end-start, len(x)))
e ottengo questo errore:
Codice:
<uvloop.Loop running=True closed=False debug=False>
Traceback (most recent call last):
File "/Users/federikowsky/Desktop/Python/Scraping/SureBet/prova.py", line 132, in <module>
x = asyncio.run(main(link))
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "uvloop/loop.pyx", line 1501, in uvloop.loop.Loop.run_until_complete
File "/Users/federikowsky/Desktop/Python/Scraping/SureBet/prova.py", line 115, in main
page_content = [
File "/Users/federikowsky/Desktop/Python/Scraping/SureBet/prova.py", line 116, in <listcomp>
await loop.run_in_executor(executor, run, urls[number * i : number * (i + 1)], session, sem)
File "uvloop/loop.pyx", line 2690, in uvloop.loop.Loop.run_in_executor
TypeError: coroutines cannot be used with run_in_executor()