並列でWEBスクレイピングする方法

四季報データをWEBスクレイピングするスクリプトを作成したのですが、取得に約1日かかった。。。

証券コードを1000から9999までの連番で1件ずつ総当りして、四季報の情報あれば取得する処理を書いているのですが、 直列で実行して、1件あたり10秒くらいかかっているので、取得時間がすごい。8999×10=89990(約24.9時間)必要となる。

これではあんまりなので、マルチスレッドで並列に処理できるように変更を検討。

その時のメモです。環境はPython3.5.2。

pythonではthreadingモジュールが用意されているらしく、それを利用します。 利用方法としては、threadingモジュールをimportすればthreadクラスが用意されるので、 実行処理用のThreadインスタンスを作成して処理する方法か、 実行処理用のサブクラスを作成し、それで管理する方法があります。

17.1. threading — スレッドベースの並列処理 — Python 3.5.2 ドキュメント

サブクラス作成したほうが分かり易いかなあ、と今回はサブクラスを作成する方法を採用しています。

作成するサブクラスでは、init()とrun()メソッドをオーバライドできます。 それ以外はオーバライド不可。なので、initとrunの中に必要処理を書いてあげます。

生成したThreadオブジェクトは、start()メソッドを実行してあげると、 スレッドが活動化され、run()メソッドが呼び出されるとのこと。

例です。引数で与えた証券コードのyahooファイナンスページにアクセスし、会社名を取得する処理。

import os, time, threading
from time import sleep
from selenium import webdriver
from pyquery import PyQuery

class multiThread(threading.Thread):
    def __init__(self, code):
        threading.Thread.__init__(self)
        self.code = code

    def run(self):
        print("start: " + str(self.code))
        scraping(self.code)
        print("end: " + str(self.code))

def scraping(code):
    browser = webdriver.PhantomJS(service_log_path=os.path.devnull)
    url = "http://stocks.finance.yahoo.co.jp/stocks/detail/?code=" + str(code) + ".T"
    browser.get(url)

    d = PyQuery(browser.page_source)
    msg = d.find("th.symbol").text()
    print(str(code) + ": " + msg)

if __name__ == "__main__":
    thread1 = multiThread(7201)
    thread1.start()
    sleep(2)
    thread2 = multiThread(7202)
    thread2.start()

実行結果が以下。

start: 7201
start: 7202
7201: 日産自動車(株)
end: 7201
7202: いすゞ自動車(株)
end: 7202