Python
scraping
python3
Finance

FRBの金利データをRSSから抜き出してみる-4

 前回の記事で、URLを生成するプロセスに問題があることがわかった。
 そこで、今回はJSONによるサイトマップを作成した後、JSONの形式に合わせてURL取得プログラムを変更し、モジュールを実装して正常に動作することを確認した。

作業

JSON形式の変更

まず、JSONをこのようなサイトマップ形式にした。

{
    "Yield Curve":
    {
        "FF":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFSPFF_N.B.XML",
        "M01":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM01_N.B.XML",
        "M03":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM03_N.B.XML",
        "M06":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM06_N.B.XML",
        "Y01":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY01_N.B.XML",
        "Y02":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY02_N.B.XML",
        "Y03":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY03_N.B.XML",
        "Y05":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY05_N.B.XML",
        "Y10":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY10_N.B.XML",
        "Y20":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY20_N.B.XML",
        "Y30":"https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY30_N.B.XML"
    }
}

プログラムの書き直し

そして、プログラムをこのように書き直した。

import json
import os
class site_map:
    def __init__(self, path,filename):
        #__変数で擬似ローカル変数になる
        #ファイルのあるフォルダまでのパスを取得
        __name = os.path.dirname(os.path.abspath(__name__)) 
        #変換対象のjsonファイルまでのパスを作成
        __path=__name+'/' + path + '/' + filename
        #絶対パスに変換
        __abspath=os.path.normpath(__path)

        #jsonファイルを開く
        __f = open(__abspath, 'r')
        __jsonData=json.load(__f)

        #urlの前半と後半の共通部分を取得
        #__prefix=__jsonData[idx0][prefix]
        #__suffix=__jsonData[idx0][suffix]

        #目次を取得
        __indexList = __jsonData.keys()
        #dict_keyをリストに変換
        __indexList=list(__indexList)


    def get_index(self):
        return __indexList

    def get_url(self,index):
        return self.urlList
        #戻り値となるリストを作成
        self.urlList=[]

        for __num in __indexList:
            #サイトマップに含まれるURLを一つづつ取得
            __url=__jsonData[index][num]

            #urlを生成してリストに
            self.urlList.append(__url)

        return self.ursList

    #JSONファイルを閉じるデストラクタ。不要か?
    def __del__(self):
        __f.close()

a=site_map("target","siteMap.json")

さて、実行して見よう。

Traceback (most recent call last):
  File "D:\Document\Visual Studio 2017\source\repos\PythonApplication2\PythonApplication2\modules\get_url.py", line 58, in <module>
    print(a.get_index())
  File "D:\Document\Visual Studio 2017\source\repos\PythonApplication2\PythonApplication2\modules\get_url.py", line 37, in get_index
    return __indexList
NameError: name '_site_map__indexList' is not defined
Exception ignored in: <bound method site_map.__del__ of <__main__.site_map object at 0x000001EFA3D2A9E8>>
Traceback (most recent call last):
  File "D:\Document\Visual Studio 2017\source\repos\PythonApplication2\PythonApplication2\modules\get_url.py", line 55, in __del__
NameError: name '_site_map__f' is not defined

動かない。どうやら、プライベート変数は同じクラス内でも関数が異なれば呼び出せないようだ。関数で呼び出す必要のある変数にselfをつけていく。あと、URLをリストに加える手続きに間違いがあったので直した。

import json
import os
class site_map:
    def __init__(self, path,filename):
        #__変数で擬似ローカル変数になる
        #ファイルのあるフォルダまでのパスを取得
        __name =str(os.path.dirname(os.path.abspath(__name__)) )
        #変換対象のjsonファイルまでのパスを作成
        __path=__name+'/' + path + '/' + filename
        #絶対パスに変換
        __abspath=os.path.normpath(__path)

        #jsonファイルを開く
        self.f = open(__abspath, 'r')
        self.jsonData=json.load(self.f)

        #urlの前半と後半の共通部分を取得
        #__prefix=__jsonData[idx0][prefix]
        #__suffix=__jsonData[idx0][suffix]

        #目次を取得
        __indexList = self.jsonData.keys()
        #dict_keyをリストに変換
        self.indexList=list(__indexList)


    def get_index(self):
        return self.indexList

    def get_url(self,index):
        #戻り値となるリストを作成
        self.urlList=[]
        __secIndex=list(self.jsonData[index].keys())
        for __key in __secIndex:
            #サイトマップに含まれるURLを一つづつ取得
            __url=self.jsonData[index][__key]

            #urlを生成してリストに
            self.urlList.append(__url)

        return self.urlList

    #JSONファイルを閉じるデストラクタ。不要か?
    def __del__(self):
        self.f.close()

a=site_map("target","siteMap.json")
b=a.get_index()
print(b)
print(a.get_url(b[0]))

出力

['Yield Curve']
['https://www.federalreserve.gov/feeds/Data/H15_H15_RIFSPFF_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM01_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM03_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCM06_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY01_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY02_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY03_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY05_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY10_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY20_N.B.XML', 'https://www.federalreserve.gov/feeds/Data/H15_H15_RIFLGFCY30_N.B.XML']

これでOKだ。

プログラムの実装

 これに合わせて、今回のメインモジュールである、getFFRateも少し改良した。

from time import sleep
from modules import XMLprocess
from modules import get_url
import sqlite3


class get_FFRate:
    #アクセスマップの入ったファイルを指定
    def __init__(self,path,filename,index):
        #アクセスするurlのリストを取得
        __scr=get_url.site_map(path,filename)
        self.urlList=__scr.get_url(index)
    #日付取得用の関数
    def get_date(self):   
        __XML=XMLprocess(self.urlList[0])
        __strdate=__XML.get_cutedData("dc:date",0,10)
        __XML=None
        return __strdate

    def get_rate(self,url):
        __XML=XMLprocess(url)
        __rate=__XML.get_floatData("cb:value")
        __XML=None
        return __rate
    #urlのリストに基づいてデータを取得していく
    def get_list(self):
        rateList=[]
        #日付をリストに入れる(主キーのつもり)
        rateList.append(self.get_date())

        for __url in self.urlList:
            __rate=self.get_rate(__url)
            rateList.append(__rate)
            #1秒間待つ
            sleep(1)
        __XML=None
        return rateList
a=get_url.site_map("target","siteMap.json")
b=a.get_index()
print(b)
for idx in b:
    #今後の発展性を持たせるためのテスト
    ff=get_FFRate("target","siteMap.json",idx)

print(ff.get_list())

出力:

['YieldCurve']
['2018-06-07', 1.7, 1.78, 1.94, 2.12, 2.31, 2.5, 2.63, 2.77, 2.93, 3.0, 3.08]

まとめ

 今回の改良のキモは、スクレイピングの対象をJSONファイルでコントロールできるようになったことだ。これで、getFFRateのもう一つ上位にモジュールを配置できるようになった。ただこのままだとgetFFRateのモジュールがちょっとごちゃごちゃしている気がする。
 少なくとも、親モジュールでsite_mapを呼び出しているならば、getFFRateでもう一度呼び出す理由がない。また、そのほうが他のデータを取得していく際にも使い勝手が良いだろう。モジュールをそのうち改良していきたい。
 今度は、データベースに接続してみたいと思う。
 ところで、そろそろソース管理が面倒になってきたので、今話題の「設計図管理ソフト」GitHubを使ってみたいと思っている。