読者です 読者をやめる 読者になる 読者になる

ウニ’s blog

勉強した結果を書いていきます

【Python3】requestsにthreadを使用すると非常に効果的

Python

crawler,rest api等でrequestsにてgetを大量に投げる場合、ボトルネックになるのはI/Oバウンド。
入門Python3には以下の事が書いてる。

  • スレッドはI/Oバウンド問題の解決のために使う。
  • CPUバウンド問題では、プロセス、ネットワーキング、イベントを使う。

requests.get()している部分をworker threadとして切り離し、Queueにてタスクを管理するとかなり高速化できる。 計測結果とサンプルコードを記載する。

結果

requestsにて10回google検索結果を取得した結果。

タイプ 総実行時間(sec) response.elapsed合計値(sec)
thread 0.6119930744171143 0:00:01.050656
non thread 5.603970766067505 0:00:01.089367

9.3倍早く終る。素ん晴らしい!
総実行時間がresponse.elapsed合計値以下になってるのが感動する。

サンプルコード

non thread版

# non_thread.py

import datetime
import requests
import time

baseurl = "http://www.google.co.jp/search?hl=ja&q="

query_list = ['inu','neko','tori','sakana','kinoko','hebi','usagi','uma','hituzji','kabutomushi']
responses = []

start_time = time.time()

for q in query_list:
    responses.append(requests.get(baseurl+q))

elapsed_time = time.time() - start_time
print("total_time : {}".format(elapsed_time))

requests_time = datetime.timedelta()
for r in responses:
    requests_time += r.elapsed

print("requests_time : {}".format(requests_time))

thread版

# thread.py

import datetime
import queue
import threading

import requests
import time

def query_worker(query_queue):
    query = query_queue.get()
    responses.append(requests.get(baseurl + q))
    query_queue.task_done()

baseurl = "http://www.google.co.jp/search?hl=ja&q="

query_list = ['inu','neko','tori','sakana','kinoko','hebi','usagi','uma','hituzji','kabutomushi']
responses = []

# queueを設定
query_queue = queue.Queue()
for q in query_list:
    query_queue.put(q)

start_time = time.time()

# Thread start
while not query_queue.empty():
    w_thread = threading.Thread(target=query_worker, args=(query_queue,))
    w_thread.start()

# wait all thread are ended.
query_queue.join()

# total running time
elapsed_time = time.time() - start_time
print("total_time : {}".format(elapsed_time))

# http response total time
requests_time = datetime.timedelta()
for r in responses:
    requests_time += r.elapsed
print("requests_time : {}".format(requests_time))

参考

入門 Python 3

入門 Python 3

p330 11.1並行処理あたりを参考。
この本読みすぎてボロボロになってきた。

その他

PythonはGIL(グローバルインタープリタロック)が効いているのでCPUバウンド問題に対応するのにスレッド化は適さないので注意。逆に遅くなるよ!
あとはcrowlerを高速化しすぎてサーバーに怒られないように注意。

終わりに

高速化成功すると。気分いいですね。 並行処理、並列処理技術はめちゃめちゃ奥が深いので、まずは浅瀬で水浴びする程度から使っていきたい。
(おわり)