目標:
我正在嘗試同時抓取多個 URL。我不想同時發出太多請求,所以我使用這個解決方案來限制它。
問題:
請求是針對所有任務,而不是一次針對有限數量的任務。
精簡代碼:
async def download_all_product_information():
# TO LIMIT THE NUMBER OF CONCURRENT REQUESTS
async def gather_with_concurrency(n, *tasks):
semaphore = asyncio.Semaphore(n)
async def sem_task(task):
async with semaphore:
return await task
return await asyncio.gather(*(sem_task(task) for task in tasks))
# FUNCTION TO ACTUALLY DOWNLOAD INFO
async def get_product_information(url_to_append):
url = 'https://www.amazon.com.br' url_to_append
print('Product Information - Page ' str(current_page_number) ' for category ' str(
category_index) '/' str(len(all_categories)) ' in ' gender)
source = await get_source_code_or_content(url, should_render_javascript=True)
time.sleep(random.uniform(2, 5))
return source
# LOOP WHERE STUFF GETS DONE
for current_page_number in range(1, 401):
for gender in os.listdir(base_folder):
all_tasks = []
# check all products in the current page
all_products_in_current_page = open_list(os.path.join(base_folder, gender, category, current_page))
for product_specific_url in all_products_in_current_page:
current_task = asyncio.create_task(get_product_information(product_specific_url))
all_tasks.append(current_task)
await gather_with_concurrency(random.randrange(8, 15), *all_tasks)
async def main():
await download_all_product_information()
# just to make sure there are not any problems caused by two event loops
if asyncio.get_event_loop().is_running(): # only patch if needed (i.e. running in Notebook, Spyder, etc)
import nest_asyncio
nest_asyncio.apply()
# for asynchronous functionality
if __name__ == '__main__':
asyncio.run(main())
我究竟做錯了什么?謝謝!
uj5u.com熱心網友回復:
這行有什么問題:
current_task = asyncio.create_task(get_product_information(product_specific_url))
當您創建一個“任務”時,它會立即安排執行。一旦您的代碼將執行交給 asyncio 回圈(在任何“await”運算式中),asyncio 就會回圈執行您的所有任務。
在您也指出的原始片段中,信號量保護任務本身的創建,確保一次只有“n”個任務處于活動狀態。gather_with_concurrency在該片段中傳遞的是協程。
與任務不同,協程是準備好等待但尚未調度的物件。它們可以免費傳遞,就像任何其他物件一樣——它們只會在它們被等待或被任務包裝時才會被執行(然后當代碼將控制權傳遞給 asyncio 回圈時)。
在您的代碼中,您正在使用get_product_information呼叫創建協同例程,并立即將其包裝在任務中。在await呼叫gather_with_concurrency自身的行中的指令中,它們都同時運行。
解決方法很簡單:此時不要創建任務,只需在信號量保護的代碼中即可。僅將原始協程添加到您的串列中:
...
all_coroutines = []
# check all products in the current page
all_products_in_current_page = open_list(os.path.join(base_folder, gender, category, current_page))
for product_specific_url in all_products_in_current_page:
current_coroutine = get_product_information(product_specific_url)
all_coroutines.append(current_coroutine)
await gather_with_concurrency(random.randrange(8, 15), *all_coroutines)
這段代碼中還有一個不相關的錯誤會導致并發失敗:您正在對time.sleepinside進行同步呼叫gather_product_information。這將在此時停止異步回圈,直到睡眠結束。正確的做法是使用await asyncio.sleep(...).
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/392388.html
