効率的なスクレイピングを行うにはリクエストとレスポンス間の待ち時間をなくすこと。これを実現するためにはthreadingモジュールでマルチスレッドか、asyncモジュールで並列処理をするかだ。
マルチスレッドは気軽に実装できる反面、デバッグが困難になる。asyncの非同期処理はコード量がやや増えるが、デバッグが楽。
この記事を呼んで我に返った
よし、threading使うのやめて、async使おうってことで試作したコードが以下
やっていることは、リクエストをしてスリープを1秒間を5回実行する(※スリープの理由は逮捕は嫌なので)
ネット環境によるのだろうけど、5回で2秒近くの処理時間の差が生まれる
import aiohttp
import asyncio
import time
import requests
url = 'https://qiita.com/osorezugoing/items/4ea5249c43c0ba8b89aa'
start_time = time.time()
res_list = []
tasks = []
# 計測結果
normal_exec_time = 0
async_exec_time = 0
def normal_request():
"""同期処理を5回実行
1. リクエスト
2. レスポンスの到着を待機
3. 1秒間スリープ
"""
global normal_exec_time
print('normal_request')
for i in range(5):
res = requests.get(url)
res_list.append(res)
print(i, 'request interval')
time.sleep(1)
# 経過時間計測
normal_exec_time = time.time() - start_time
print("--- %s seconds ---" % (normal_exec_time), '\n')
def async_request():
"""非同期処理を5回実行
1. リクエスト
2. 1秒間スリープ
3. 1と2を5回実行
3. レスポンスの到着を待機
"""
global async_exec_time
print('async_request')
async def _req(i):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
res_list.append(response)
print(i, 'request interval')
time.sleep(1)
async def _run():
# 非同期オブジェクトをtaskへ追加
for i in range(5):
tasks.append(_req(i))
# taskのすべての処理を待機
await asyncio.gather(*tasks)
# この記述は実行環境がwindowsのため
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# 非同期処理実行
asyncio.run(_run())
# 経過時間計測
async_exec_time = time.time() - start_time
print("--- %s seconds ---" % (async_exec_time), '\n')
if __name__ == '__main__':
# 同期処理
normal_request()
# 非同期処理
start_time = time.time() # 初期化
async_request()
# 同期と非同期の処理時間の差
print('diff: ', normal_exec_time - async_exec_time)
normal_request
0 request interval
1 request interval
2 request interval
3 request interval
4 request interval
--- 7.513632774353027 seconds ---
async_request
0 request interval
4 request interval
1 request interval
3 request interval
2 request interval
--- 5.4084553718566895 seconds ---
diff: 2.105177402496338
さあ、これから新しいブランチ切って始めますかー