旧ゆるふわクオンツの日常

旧ゆるふわクオンツの日常

https://dw-dw-dt.hatenablog.com/ に引っ越します。リンク切れの記事もこちらのリンク先にあります。

ネットに落ちてるファイル(景気ウォッチャー調査)のクローリングとスクレイピング

 すでに先人達によってやり尽くされた感があって何を今更というネタではありますが(^_^;)

 Pythonを使ってウェブからデータを取得する方法について記載したいと思います。(seleniumを使うバージョンや、次のステップであるデータの解析・加工はまた別の機会にでも)
 環境は Mac OS High Sierra / Python 3.6.4 :: Anaconda custom (64-bit)となっております。
(注)ちなみになぜPythonかというと、、、ライブラリも豊富だし、普通に流行っているし、あとはコンパイラ言語ではなく、スクリプト言語であるという点ですかね。try & errorが多いクローリングやスクレイピングでは、ソースコードを修正してコンパイルして、っていうのが非常に面倒ですからね。。

====目次====

==========

内閣府景気ウォッチャー調査を取ってみよう

 内閣府がリリースしている景気ウォッチャー調査(統計表一覧:景気ウォッチャー調査 - 内閣府)とは、毎月、いろんな人の景気に対する見方を まとめたもので、詳しくはこちらをご覧ください。ここでの目標は、各月毎の景気ウォッチャー調査のページに記載のある『(参考)景気判断理由集(CSV形式)』をごっそりとダウンロードすることを目標にします。

 手順としては以下のようなイメージとなります。

  1. 統計表一覧:景気ウォッチャー調査 - 内閣府のページを確認
  2. 1.で確認したページからとべるページ(例えば2018年1月のページ)を見に行って、欲しいCSVファイルのURLを取得する
  3. 2.で取得したURLをひたすらダウンロードする

 それでは早速、コードを書いて行きたいと思います。

import requests
import lxml.html
import time
import os

mydir = os.getcwd()

# 内閣府からデータを取ってくるクラスを定義
class Cao_Keiki_Watcher:
    def __init__(self):
        self.main_url = 'http://www5.cao.go.jp/keizai3/watcher_index.html' 
        self.sub_url_head = 'http://www5.cao.go.jp/keizai3/'
        self.sub_url_tail = 'watcher/menu.html'
        self.sub_url_tail_watcher4ver = 'watcher/watcher4.csv'
        self.sub_url_tail_watcher5ver = 'watcher/watcher5.csv'
        res = requests.get(self.main_url)
        res.encoding = res.apparent_encoding # この処理なしでres.textすると文字化けしちゃう
        self.tree = lxml.html.fromstring(res.text)

    def get_watcher4_urls(self):
        anchors = self.tree.xpath('//table[contains(@class,"tableBase")]//td/a')
        watcher4_list = [self.sub_url_head + str(anchor.attrib["href"])[0:9] + self.sub_url_tail_watcher4ver for anchor in anchors]
        return watcher4_list
    
    def get_watcher5_urls(self):
        anchors = self.tree.xpath('//table[contains(@class,"tableBase")]//td/a')
        watcher5_list = [self.sub_url_head + str(anchor.attrib["href"])[0:9] + self.sub_url_tail_watcher5ver for anchor in anchors]
        return watcher5_list

def download_csv(watcher_list,watcher_type):
    if watcher_type != 'watcher4' and watcher_type != 'watcher_5':
        print('watcher_type を "watcher4" または "watcher5" に設定してください ')
        quit()
    for watcher_file in watcher_list:
        yyyymmdd = watcher_file[30:34] + watcher_file[35:39]
        # 連続でアクセスしまくると内閣府のサーバーに負荷がかかるので絶対に以下のsleepをコメントアウトしないでください
        time.sleep(1)

        r = requests.get(watcher_file)
        r.encoding = r.apparent_encoding
        if r.status_code == 200:
            f = open(mydir+'/csv_hangar/'+ yyyymmdd + '_' + watcher_type + '.csv' ,'w')
            f.write(r.text)
            f.close()
        print('download:'+ yyyymmdd + '_' + watcher_type + '.csv')

def main():
    # 内閣府の景気ウォッチャー調査のurlを取得します -> watcher4_list, watcher5_list
    kw = Cao_Keiki_Watcher()
    watcher4_list = kw.get_watcher4_urls()
    watcher5_list = kw.get_watcher5_urls()
    
    # csv保存用フォルダを作成します
    if os.path.exists(mydir+'/csv_hangar'):
        pass
    else:
        os.mkdir(mydir+'/csv_hangar')
        print('create csv_hangar dir')
    
    # csvをダウンロードします
    download_csv(watcher4_list,'watcher4')
    download_csv(watcher5_list,'watcher5')

if __name__ == '__main__':
    start = time.time()
    print('=== 開始時刻 ===',time.ctime())
    main()
    end = time.time()
    print('=== 終了時刻 ===',time.ctime())
    print('所要時間は{0}秒です'.format(end-start))

 基本的にはr = requests.get(file_url)でファイルを取り込んで、r.encoding = r.apparent_encodingエンコードを直して、それをファイルに書き込んでいく(f = opne(file_path, 'w') f.write(r.text) f.close())作業となります。

いったい何をやっているのか

 始めのブロックでは何をしているかというと、内閣府のサイトからデータをとってくるツールセットを定義しています。
 ライブラリlxmlを使って、http://www5.cao.go.jp/keizai3/watcher_index.html のソースを解析し、そのサブページに存在するcsvファイルのURLを特定しています。
 具体的には http://www5.cao.go.jp/keizai3/watcher_index.html からとべる、http://www5.cao.go.jp/keizai3/2018/0208watcher/menu.html (これは例えば2018年1月の調査結果のページ)というサブページに、csvファイルへのリンク http://www5.cao.go.jp/keizai3/2018/0208watcher/watcher4.csv という目的のリンクが埋まっています。
 こういったcsvファイルへのリンクをまとめて返してくれるのが get_watcher4_urlsget_watcher5_urls というメソッドです。

class Cao_Keiki_Watcher:
    def __init__(self):
        self.main_url = 'http://www5.cao.go.jp/keizai3/watcher_index.html' 
        self.sub_url_head = 'http://www5.cao.go.jp/keizai3/'
        self.sub_url_tail = 'watcher/menu.html'
        self.sub_url_tail_watcher4ver = 'watcher/watcher4.csv'
        self.sub_url_tail_watcher5ver = 'watcher/watcher5.csv'
        res = requests.get(self.main_url) # main_urlのページのソースを取得します
        res.encoding = res.apparent_encoding # この処理なしでres.textすると文字化けしちゃう
        self.tree = lxml.html.fromstring(res.text) # xpathで処理しやすい形にしています。

    def get_watcher4_urls(self): # watcher4.csv達のリンクを取得する関数。アウトプットはリスト形式です。
        anchors = self.tree.xpath('//table[contains(@class,"tableBase")]//td/a')
        watcher4_list = [self.sub_url_head + str(anchor.attrib["href"])[0:9] + self.sub_url_tail_watcher4ver for anchor in anchors]
        return watcher4_list
    
    def get_watcher5_urls(self): # watcher5.csv達のリンクを取得する関数。アウトプットはリスト形式です。
        anchors = self.tree.xpath('//table[contains(@class,"tableBase")]//td/a')
        watcher5_list = [self.sub_url_head + str(anchor.attrib["href"])[0:9] + self.sub_url_tail_watcher5ver for anchor in anchors]
        return watcher5_list

 次のブロックでは、取得したURLのリストのcsvファイルをローカルに保存する関数を定義しています。
 ライブラリrequestsを使って、ファイルを読み取ります。

def download_csv(watcher_list,watcher_type):
    if watcher_type != 'watcher4' and watcher_type != 'watcher_5':
        print('watcher_type を "watcher4" または "watcher5" に設定してください ')
        quit()
    for watcher_file in watcher_list:
        yyyymmdd = watcher_file[30:34] + watcher_file[35:39]
        # 連続でアクセスしまくると内閣府のサーバーに負荷がかかるので絶対に以下のsleepをコメントアウトしないでください
        time.sleep(1)

        r = requests.get(watcher_file,timeout=10)
        r.encoding = r.apparent_encoding
        if r.status_code == 200: # ページが取れないときは404エラーとかそんな感じになっている。
            f = open(mydir+'/csv_hangar/'+ yyyymmdd + '_' + watcher_type + '.csv' ,'w')
            f.write(r.text)
            f.close()
        print('download:'+ yyyymmdd + '_' + watcher_type + '.csv')

 あとは main() にてこれらをループさせているだけですね。

定期的な実行について

 さて、こんな感じでデータを回収するコードがかけたら、これを定期的に動かして更新情報とかがないか確認したくなります。  そんなとき、macユーザーが一番手軽に実行できるのは cron ではないかと思います。
 コマンドラインcrontab -e とおして

0 17 * * * cd (実行ディレクトリのパス) && python (実行ファイル名)

 とすれば、毎日実行できますね。