【Python入門】プログラミングで自分だけの株価データを手に入れよう



【Python入門】プログラミングで自分だけの株価データを手に入れよう

株式投資、いつの時代も根強い人気がありますよね、私も好きです。

そして気になる銘柄のデータを分析しようとパソコンやスマホを開いたら、概ねチャートしか出てこないと思います。これではエクセルなどでデータとして株価を扱えないために、データ分析ができません。ところが、プログラミングを使えば無料で株価データを取得できます。

今回は 「API」 と 「Webスクレイピング」 2つ の方法を用いて、 日経225 にリストアップされている会社の株価を Python で取得してみました。

株に興味ある、プログラミング(Python)にも興味あるという方、ご参考ください。

目次
  1. 【Python入門】プログラミングで自分だけの株価データを手に入れよう
  2. プログラミングを使わずに株価データを取得する方法
  3. プログラミングで株価を取得する方法
  4. PythonとAPIを使って株価データの取得にチャレンジ
  5. APIサービス:Alpha Vantageで過去の株価情報を取得
  6. APIサービス: Yahoo Finance API で過去の株価情報を取得
  7. Webスクレイピングで株価データを取得
  8. まとめ

【Python入門】プログラミングで自分だけの株価データを手に入れよう

プログラミングを使わずに株価データを取得する方法

まず最初に "プログラミングなし" で株価を取得する方法を改めて確認。

  • Webサービス
  • 取引ツール
  • 四季報

Webサービスは、 「Yahoo!Japan ファイナンス」 のような Webサイトで、簡単に過去のデータを確認する事が可能。

image

Yahoo!Japanで大和証券の過去のデータを確認している様子(上図の URL

通常は Yahoo! で株価を検索するとチャートが表示されると思いますが、 「時系列」 のメニュータブをクリックすると過去のデータを確認することが可能。ただし、このデータをエクセルなどで使おうと思うと 月2,178円払って会員になるか、各ページのデータをコピペする必要があります。 "お金" か "手間" がかかるということですね。

次に取引ツールの代表格である 「楽天マーケットスピード」 で過去のデータを確認してみました。

楽天マーケットスピードの場合は、過去データを確認すると同時に CSV ファイルで無料ダウンロードできるのですが、 最大 10年前までのデータです。そして選択できる "時間足" は、 "日"、 "週"、 "月" のみ。 "分" や "時間" 足の過去データは取得できません。

このように提供されている汎用ツールを使うとある程度のデータは取得できるものの、いくつかの妥協点が生じます。こうした問題はプログラミングを使うと幾分か解決されますのでご紹介していきますね。

株価データ提供元による仕様の違い
過去データの時間単位 取得可能な最古データ 取得できるデータ形式
Yahoo!Japan ファイナンス 日次、週、月 36年前 CSV(有償)
楽天マーケットスピード 日次、週、月 10年前 CSV
API(Yahoo!) 分、時間、日、週、月 20年前 JSON


プログラミングで株価を取得する方法

image

プログラミングを使って株価を取得する方法は、大きく分けて2つあります。

  • Webスクレイピング
  • API

Webスクレイピングというのは、 Webページ上のデータに "プログラムから" アクセスして、 必要な HTMLコード(データ) を取得するというもの。 Webスクレイピングに関する情報量は多く、難易度も比較的低いことから、プログラミング初心者でも扱いやすい技術として有名です。 ただし、 "HTML" からデータ取得しますので、 Webデザインが変わったりすると該当のデータを得られません。また概ね Web上の株価データというのは、 "日足" が最小単位。 "時間" や "分足" などの細かいデータの取得はできません。

一方 API については、 "分足" や "時間足" のデータ取得も可能。 "API" というと敷居が高いように感じられる方もいらっしゃるかもしれませんが、サンプルコードを使えば大丈夫でしょう。 "Webスクレイピング" と "API" の違いについては以下のような感じに。

WebスクレイピングとAPIの違い
過去データの時間単位 データ取得までの時間 プログラム作成時間 アクセス回数の制限
Webスクレイピング 日次、週、月 長め 長め なし?
API 分、時間、日、週、月 短い 短め あり

Webスクレイピングは、 Webページからレスポンスがあって初めて HTMLコードを解析できるために、プログラムを実行しても即座にデータ取得できる訳ではありません。そのため API に比べると Webスクレイピングの方が "データ取得までにかかる時間" が "長い" としています。 プログラム作成時間については "慣れ" もあるかもしれませんが、 Webスクレイピングの場合は スクレイピング失敗時の処理が必須です。 API の場合は、アクセスに失敗する機会が少ないので、 "必須" でなくても大丈夫。

まずは API を使って、株価データを取得してみたいと思います。

API ・・・ Yahoo や Rakuten などの APIプロバイダーが定める URL にアクセスすると、データを取得できるサービス。無料のモノから有料のモノ、 キーが必要なものから不要なものまで種類がある。 アプリ開発などでよく使われるデータサービス。

PythonとAPIを使って株価データの取得にチャレンジ

"APIを使って株価を取得" というわけですが、次は "どの API" を使うか、という点が問題に。 Google で 「株 API」 と検索すると Rakuten RapidAPI が紹介されますが、こちらは難易度が高めです。いくつか検索した結果、以下の API サービスが "使いやすいな" と思いました。

  • Alpha Vantage
  • Yahoo! Finance API

2つとも株価を取得できる APIサービス ですが、利用できる内容や取得できる情報は違ってきます。

APIサービスによる違い
APIの制限(無料プラン) 1分足での取得期間 データ形式 PIP
Yahoo API 2000回/時間 7日間分 JSON あり
alphavantage.co 300回/時間 1日 JSON 不要

「何のために株価データを集める必要があるのか?」 という点を考慮して APIサービスを検討するようになると思います。 例えば 「APIサービス: Alpha Vantage」 の方は、 1時間に 300回までしかアクセスできません。つまり 1分あたり 5回、 12秒に 1回、 APIからたくさん情報を欲しい場合、例えば 5秒おきの株価データや東証全銘柄のデータサーチなどには厳しいということ。

ただ 「APIサービス: Alpha Vantage」 については、比較的簡単なプログラム処理できることから、まずはこちらのサービスから使って情報をゲットしてみたいと思います。


APIサービス:Alpha Vantageで過去の株価情報を取得

APIサービス:Alpha Vantage を使う様子

今回株価データを取得する目的は、 「ブログを運営している会社と運営していない会社を比較するために株価を収集」 となり、トレーダーの方とはデータ収集の目的が若干異なりますが、データを扱う様子は参考になります。

【収集するデータ】

日経225に登録されている会社の今日の株価と 10年前の株価

まずは APIサービス:Alpha Vantage がどんな様子で使えるのか確認してきます。

image

公式ページ: https://www.alphavantage.co/ にアクセスして、画面中央右に表示されている 「GET YOUR FREE API KEY TODAY」 をクリック(上図参照)。すると上図右のようにいくつか入力する項目があって、最後に 「GET FREE API KEY」 をクリックすると、メールアドレスに紐付いた "API キー" が発行されます(下図参照)。

image

一旦 "API キー" をメモ帳か何かに貼り付けておきましょう。

image

次はその "APIキー" の使い方で、ページ上部の 「DOCUMENTATION」 をクリックし、左サイドバーの 「Intraday日中」 をクリック。

少しページを下にスクロールすると、サンプルの URL が書かれています(下図参照)。

上図のURL

https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=5min&apikey=demo

試しにサンプルの URL にそのままアクセスしてみると、株価データが返ってきます。これは JSON形式のデータで、ページを更新してアクセスする度にデータ内容が変化します。

実際に先ほど取得した "API キー" を使って、ソフトバンクの今の株価データを取得してみました。

API URL : https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=9434.T&interval=5min&apikey=APIキー

するとソフトバンクの株価データは取得できているのですが、よく見ると時間がアメリカ東部時間。そして東証は 9時から、つまり 約40分前から始まっているのに、 JSON データは 約20分前のものしかありません。

つまり APIサービス:Alpha Vantage は手軽に使えるが、タイムラグがあったりしてライブデータとしてはやや不十分です。今回はデータ分析が目的で、時間にはそれほどシビアではないので大丈夫ですが...

先程まではソフトバンク株(9434.T)を見ていましたが、 こちらは日経225には組み込まれていないため、大和証券(8601.T)の株価を API で取得してみたいと思います。

上図のコード

import csv
import requests
import json

response = requests.get("https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=8601.T&apikey=APIキー")
data = json.loads(response.text)

data

まずは取得する株価の URL を確認。 ドキュメントページ https://www.alphavantage.co/documentation/ にアクセスして、 Monthly の項目を確認。サンプルの URL を元に証券番号を 大和証券の 8601.T に変更し、APIキーをセットします。

https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=8601.T&apikey=APIキー

そしてプログラム(Python)上で URL にアクセスし、情報取得できるよう必要なモジュール、ライブラリをインポート(上図参照)。 URL から取得できたデータを確認すると、先ほどブラウザで見ていた JSON データが表示されていることが確認できます。

上図のコード

data.keys()

data = data["Monthly Time Series"]

data

APIを使って取得したデータを、 変数: data に格納したわけですが、どんなデータが入っているか data.keys() で確認してみましょう(上図参照)。すると株価データとはあまり関係ない Meta Data という項目も。今回は株価データだけでいいので、 Monthly Time Series の要素だけを使用。 変数: data を Meta Data のない、 Monthly Time Series だけにします(上図参照)。改めて data を出力すると、 先程はあった Meta Data が消えていることが確認できます。

上図のコード

print(type(data))
print(len(data))

data.keys()

new_date = sorted(data.keys())[-1]
old_date = sorted(data.keys())[0]
print(new_date)
print(old_date)

作り変えた 変数: data の内容を確認すると、情報量は 239項目で膨大(上図の一番上のコード)。試しに 変数: data の キー を確認すると日付データが 239 出力(data.keys())。

一番古いデータと一番新しいデータは、Pythonモジュール: sorted() を使えば取得できそうで、実際のコードが上図最下段のプログラムになります。

-1 とすることで一番最後のデータ、つまり直近のデータを取得することができました。

上図のコード

old_price = data[old_date]["4. close"]
print(old_date + "の株価: " + old_price)
new_price = data[new_date]["4. close"]
print(new_date + "の株価: " + new_price)

現在の 変数: data は辞書型で、そのキーは日付データ(オレンジ色塗りつぶし部分)、キーとセットになっている値は ["〇〇"] (紫色塗りつぶし部分)の追加で取得できますので、上図最下段のように data[new_date]["4. close"] とすることで、 変数: data の最後の値の価格を取得できます。 この10年で大和証券の株価は、 3分の1になっているんですね。

さて今回わざわざ API を使って株価データを取得しているのは、 日経225の銘柄について昔と今の価格を比較するため。最初に API の URL 設定で使った "証券番号" を任意のモノに変えれば全銘柄のデータを収集できそうです。

上図のコード

import csv
import requests
import json
import time

API = "APIキー"
csvfile = "225.csv"

count = 1

with open(csvfile, "r") as f:
    rows = csv.reader(f)
    for row in rows:
        print("ループ回数: " + str(count))
        row_str = str("".join(row))

        response = requests.get("https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=" + row_str + ".T&apikey=" + API)
        data = json.loads(response.text)
        data = data["Monthly Time Series"]
        new_date = sorted(data.keys())[-1]
        old_date = sorted(data.keys())[0]
        new_price = data[new_date]["4. close"]
        old_price = data[old_date]["4. close"]
        change_rate = float(new_price)/float(old_price)
        change_rate = str('{:.2f}'.format(change_rate))
    
        with open("225_Stock_Price.csv", "a") as csvFile:
            writer = csv.writer(csvFile)
            writer.writerow([row_str, old_date, old_price, new_date, new_price, change_rate])
    
            count = count + 1
            time.sleep(1)

日経225の証券番号が入った CSVファイルを用意して、上図のようにループ処理を実行してみました。

すると 6回目の実行時に KeyError: 'Monthly Time Series' とエラーが。これは API のアクセスリミットを超えたために発生したと思われるエラーです。

ここのプログラムで利用している APIサービス:Alpha Vantage は、 1分間に 5回までしか URL にアクセスすることができませんDocuments

実際に 6番目の証券番号 4519 に個別アクセスして、価格取得のプログラムを実行すると、エラーなくデータ取得できました。

APIサービス:Alpha Vantage の仕様にしたがって、 "1分間に 5アクセス" とすると 225銘柄チェックするだけで 25分も時間がかかってしまいます。この問題をクリアするために、次は Yahoo! Finance API を使ってデータ取得を試みてみました。

APIサービス:Alpha Vantage で株価を取得する Pythonコード
Google Colab
APIキーを変更してご利用ください


APIサービス: Yahoo Finance API で過去の株価情報を取得

Yahoo Finance API を使う様子

Yahoo API

先ほどの APIサービス:Alpha Vantage に比べてアクセス制限が緩い "Yahoo! Finance API" を使って過去の株価データ取得にチャレンジ。

Yahoo Finance API の使い方は、 公式ドキュメント をみると YQL とか登場して、ちょっと学習時間が必要そう... ということで、 PyPi に公開されている Yahoo Finance API のライブラリを使って対応することに。

【Yahoo Finance API】 公式ドキュメントpip のドキュメント(今回使用)

!pip install yahoo-finance-api2

まずは Yahoo Finance API のライブラリを読み込み。 Google Colab で pip をインストールする場合は、最初に ! が必要。

上図のコード

import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError

my_share = share.Share('MSFT')
symbol_data = None

try:
    symbol_data = my_share.get_historical(share.PERIOD_TYPE_DAY,
                                          10,
                                          share.FREQUENCY_TYPE_MINUTE,
                                          5)
except YahooFinanceError as e:
    print(e.message)
    sys.exit(1)

print(symbol_data)


symbol_data.keys()

次に pip のページに書かれているサンプルを実行。 PyPi ページに書かれているコードをそのまま Colab にコピペ。そして実行してみるとタイムスタンプ(timestamp)のデータが表示されます。

symbol_data.keys() でデータの内容を確認すると、 タイムスタンプ以外に株価データがあることが確認できます(上図参照)。

上図の左側のコード

import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError

my_share = share.Share('8601.T')
symbol_data = None

try:
    symbol_data = my_share.get_historical(share.PERIOD_TYPE_YEAR,
                                          30,
                                          share.FREQUENCY_TYPE_DAY,
                                          1)
except YahooFinanceError as e:
    print(e.message)
    sys.exit(1)

print(symbol_data.keys())

上図の右側のコード

import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
my_share = share.Share('8601.T')
symbol_data = None
try:
    symbol_data = my_share.get_historical(share.PERIOD_TYPE_YEAR,
                                          20,
                                          share.FREQUENCY_TYPE_DAY,
                                          1)
except YahooFinanceError as e:
    print(e.message)
    sys.exit(1)
print(symbol_data.keys())

Yahoo Finance API でどれぐらい過去データが取得できるか確認してみます。上図左が 30年前、 上図右が 20年前、 APIへのアクセス結果 20年前も 30年前も同じだけのデータ取得量(5007) となっていることが確認できます。

上図のコード

date = symbol_data["timestamp"]
print(len(date))

old_date = date[0]
now_date = date[122]
print(old_date)
print(now_date)

from datetime import datetime
old_date = datetime.utcfromtimestamp(old_date)

from datetime import datetime
old_time = datetime.utcfromtimestamp(int(old_date/1000))
now_time = datetime.utcfromtimestamp(int(now_date/1000))
print(old_time)
print(now_time)

昔と今の株価を取得するにあたって "いつ" の株価データであるか、ということが必要になってきますので、まずは Yahoo Finance API で返ってくるデータの中から timestamp だけ 変数: date に格納(上図最上段のコード)。

次は 変数: date の最初と最後のデータをピックアップ(上図上から2段目のコード)。すると 946944000000 という、とても時刻とは思えないような結果が。これは "タイムスタンプ" 形式の日時データで、普通の 19:05 みたいに変換することもできます。

タイムスタンプの時刻表示変換は、以下のコードで OK。

from datetime import datetime
old_date = datetime.utcfromtimestamp(old_date)

しかし上記コードではエラーがでてます。これは取得した時刻データのタイムスタンプが、ミリセカンド単位となっているため。タイムスタンプのデータをセカンド単位に変換することで時刻表示を変換することができます(上図最下段)。


【タイムスタンプの単位を確認する方法】

EpochConverter

タイムスタンプを上記のようにコピペしてチェックすると、タイムスタンプの単位を確認できます。



次は Yahoo Finance API から返ってきたデータの close キー部分を確認。 close キーの価格データを一旦 変数: price に格納し、最初と最後のデータを抽出します。すると20年前と今の株価を取得することができましたね。

今回は一つの銘柄のみに対して、日時や価格を取得してきました。 "銘柄" を決める証券コード部分を 日経225 にセットされている企業のコードに変えれば、簡単にたくさんの株価データを取得できそう、ループでいけそうですね。

APIサービス:Yahoo Finance API で株価を取得する Pythonコード
Google Colab


APIサービス:Yahoo Finance API で株価を 225種類 取得する Pythonコード
Google Colab
上図のコード

!pip install yahoo_finance_api2

import csv
import time
from datetime import datetime
import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError

def get_data():
    global old_time, now_time, old_price, now_price, change_rate, data_volume

    my_share = share.Share(row_str + '.T')
    symbol_data = None

    try:
        symbol_data = my_share.get_historical(share.PERIOD_TYPE_YEAR, 10, share.FREQUENCY_TYPE_MONTH, 1)
        time.sleep(5)
        date = symbol_data["timestamp"]

        old_date = date[0]
        now_date = date[-1]
        old_time = datetime.utcfromtimestamp(int(old_date/1000))
        now_time = datetime.utcfromtimestamp(int(now_date/1000))
        price = symbol_data["close"]
        old_price = price[0]
        now_price = price[-1]
        change_rate = '{:.2f}'.format(now_price/old_price)
        data_volume = len(date)

    except YahooFinanceError as e:
        print(e.message)
        sys.exit(1)

def write_csv():
    with open("225PriceData.csv", "a") as csvFile:
        writer = csv.writer(csvFile)
        writer.writerow([row_str, data_volume, old_time, old_price, now_time, now_price, change_rate])

count = 1

csvfile = "225.csv"

with open(csvfile, "r") as f:
    rows = csv.reader(f)
    for row in rows:
        print("ループ回数: " + str(count))
        row_str = str("".join(row))

        get_data()
        write_csv()

        count = count + 1

日経225に登録されている企業の証券コード 225 個分をループ処理している様子です。

取得できたデータを確認してみると、ところどころデータが取得できていないことが確認できます。 データが取れていない原因としては、「APIキーアクセスエラー」「API側のデータ不足」が考えられますね。 Yahoo Finance API のアクセス制限は、 2000回/時、 1分間に 30回、 2秒で 1回という計算に。試しに time.sleep(3) を入れて実行してみましたが、データは取得できませんでした。

上図のコード

import sys
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
from datetime import datetime

number = "6752"
my_share = share.Share(number + ".T")
symbol_data = None

try:
    symbol_data = my_share.get_historical(share.PERIOD_TYPE_YEAR,
                                          10,
                                          share.FREQUENCY_TYPE_DAY,
                                          1)
except YahooFinanceError as e:
    print(e.message)
    sys.exit(1)

date = symbol_data["timestamp"]
data_volume = int(len(date))
print("取得できたデータは何日分?  " + str(data_volume))

old_date = date[0]
now_date = date[data_volume-1]
old_time = datetime.utcfromtimestamp(int(old_date/1000))
now_time = datetime.utcfromtimestamp(int(now_date/1000))
print(old_time)
print(now_time)

price = symbol_data["close"]
old_price = price[0]
now_price = price[data_volume-1]
print(str(old_time) + "の時の株価: " + str(old_price))
print(str(now_time) + "の時の株価: " + str(now_price))
print('{:.2f}'.format(now_price/old_price))

上図のように個別に銘柄データをチェックしてみると、やはり 10年前のデータは取得できず、 4ヶ月ぐらいまでのデータに。どうもAPI側にデータがないようです...

一部データが不十分という結果にはなりましたが、 Yahoo Fina APIを使って株価を取得することができました。今回は東証の株価データでしたが、銘柄部分を仮想通貨やゴールドなど Yahoo!Finance で公開されているモノに変えれば色々な価格データを収集することが可能になります。


Webスクレイピングで株価データを取得

Webスクレイピングで株価データを収集する様子

API を使った後なので少し "インパクト" に欠けるかもしれませんが、参考までに Webスクレイピングを使って株価データを取得する例もご紹介します。

Webスクレイピングを使って株価データを取得する場合、データを取得しやすいようになるべくキレイにフォーマットされたページが Good です。いくつか確認した結果、 Yahoo! Finance のページがいいかな、という結論に(下図参照)。

image

Yahoo! Finance の Webページをスクレイピングして株価データを収集

URLに証券コードを盛り込み、アクセスすればページ移動することなく 18年前(2001年)のデータにアクセスできます。 ただ URLでアクセスした際、過去のデータを確認しようと思うとページを最下部までスクロールダウンする必要があります。ページ操作が伴うことから今回は スクレイピングライブラリ:Selenium を使って挑戦してみました。

まずは Yahoo!Finance にアクセスして、適当な銘柄を入力し、検索。 今回は 4151(協和キリン) をセット、そして Time Piriod の項目で期間を選択(今回は 2009年1月1日から直近)。

このページの一番上の項目の "Date" と "Close" をスクレイプできれば直近データを取得できます。古いデータはページの最下部にありますので、スクロールダウンして確認します。

まず今画面に表示されている直近のデータですが、表の中のデータとなるため find_element_by_xpath() でスクレイプ指定するのが妥当そう。 XPath の値は、ブラウザ上で欲しいデータ上で右クリック、 "要素を調査" を選択。そしてデベロッパーモード内の HTML コード上でまた右クリックし、コピー、 XPath を順に選択。これで XPath を取得でき、ブラウザ上の欲しいデータを取得できます。

【上図の Date の Nov 29, 2019 の XPath】
/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[1]/span

古いデータの XPath は画面をスクロールダウンして、 Date と Close の XPath を取得します。

【古い Date の Jan 05, 2009 の XPath】
/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[1]/span

以上をまとめてプログラムを作成すると下記のように。

上図のコード

import time
import os
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

options = webdriver.FirefoxOptions()
options.set_preference("dom.push.enabled", False)
driver = webdriver.Firefox(firefox_options=options)
driver.implicitly_wait(5)

url = "https://finance.yahoo.com/quote/4151.T/history?period1=1230735600&period2=1575126000&interval=1d&filter=history&frequency=1d"
driver.get(url)
time.sleep(5)

page = driver.find_element_by_tag_name("html")
page.send_keys(Keys.END)

old_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[1]/span').text
old_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[5]/span').text
print(old_date)
print(old_stock)

now_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[1]/span').text
now_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[5]/span').text
print(now_date)
print(now_stock)

Yahoo! Finance のページにアクセスした後、 page.send_keys(Keys.END) でスクロールダウンしていますが、スクロールが足りないようで結局古いデータの XPath を取得できていません。スクロールダウンの回数を増やして対応してみました。

上図のコード

import time
import os
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

options = webdriver.FirefoxOptions()
options.set_preference("dom.push.enabled", False)
driver = webdriver.Firefox(firefox_options=options)
driver.implicitly_wait(2)

url = "https://finance.yahoo.com/quote/4151.T/history?period1=1230735600&period2=1575126000&interval=1d&filter=history&frequency=1d"
driver.get(url)
time.sleep(5)

page = driver.find_element_by_tag_name("html")

i = 0
while i < 20:
    page.send_keys(Keys.END)
    print("スクロールダウン:  " + str(i))
    i = i + 1
    time.sleep(0.5)

old_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[1]/span').text
old_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[5]/span').text
print(old_date)
print(old_stock)

now_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[1]/span').text
now_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[5]/span').text
print(now_date)
print(now_stock)

while文を使ってスクロールダウンを 20回実行、すると古いデータの 2009年1月の位置まで画面を下げることができ、古いデータの XPath の取得にも成功。そして古い 2009 年 1月 5日の株価も取得できています。あとは最初にアクセスする URL の証券コードを 日経225 の企業のモノに変えれば、 225社の株価データを取得できそうです。

上図のコード

import time
import os
import csv
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

options = webdriver.FirefoxOptions()
options.set_preference("dom.push.enabled", False)
driver = webdriver.Firefox(firefox_options=options)
driver.implicitly_wait(5)

count = 1
csvfile = "225.csv"

with open(csvfile, "r") as f:
    rows = csv.reader(f)
    for row in rows:

        print("ループ回数: " + str(count))
        row_str = str("".join(row))

        url = "https://finance.yahoo.com/quote/" + row_str + ".T/history?period1=1230735600&period2=1575126000&interval=1d&filter=history&frequency=1d"
        driver.get(url)
        time.sleep(5)

        page = driver.find_element_by_tag_name("html")

        i = 0
        while i < 20:
            page.send_keys(Keys.END)
            print("スクロールダウン:  " + str(i))
            i = i + 1
            time.sleep(0.5)

        try:
            old_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[1]/span').text
            old_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[2713]/td[5]/span').text
            print(old_date)
            print(old_stock)

            now_date = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[1]/span').text
            now_stock = driver.find_element_by_xpath('/html/body/div[1]/div/div/div[1]/div/div[3]/div[1]/div/div[2]/div/div/section/div[2]/table/tbody/tr[1]/td[5]/span').text
            print(now_date)
            print(now_stock)


            old_stock = old_stock.replace(',', '')
            now_stock = now_stock.replace(',', '')
            change_rate = float(now_stock)/float(old_stock)
            print(change_rate)
            change_rate_str = str('{:.2f}'.format(change_rate))
            print(change_rate_str)

            with open("225Price.csv", "a") as csvFile:
                writer = csv.writer(csvFile)
                writer.writerow([row_str, old_date, old_stock, now_date, now_stock, change_rate_str])

            count = count + 1
            time.sleep(1)

        except:
            print("try error:: データ取得できず....")
            with open("225Price.csv", "a") as csvFile:
                writer = csv.writer(csvFile)
                writer.writerow([row_str, "エラー"])
            csvFile.close()

            count = count + 1
            time.sleep(1)

225社を順番に Yahoo! Finance で調べていくと、ところどころデータの取得エラーが発生。調べてみると株価データ内に 「10 Dividend配当」 といった情報がありました。

この配当に関する項目のせいで XPath の値が一致せず、エラーが発生していると考えられます。 この問題を回避しようと思うと、株価一覧の上部に表示されている 「Download」 ボタンが良さそうです。

しかし、 Selenium でこの "ダウンロード" を実行しようと思うと...ムリでした。理由は、 <a>タグに script を埋め込んだり、複雑なクラス構造にしたり、 CSVファイルの URL を拡張子ナシに設定されているため。スクレイピング防止措置とも思えるコードの内容です。

Yahoo!Japanの株価時系列データならスクレイピングできるかもしれませんが、こちらはダウンロードが有償設定。公の場でスクレイプすると著作権侵害になるかもしれませんので、やめておきます。

結局スクレイピングで取得できたデータは、 46銘柄分で、プログラムの実行にかかった時間は約 2時間。 100回目のスクレイピングで Webページの読み込みに失敗して価格を参照できていないことが確認できます(上図参照)。 株価データの取得も容易でないことが確認できますね。逆にいえば、需要はありそうなので、 Bot に株価データを毎日記録させる、そしてそれを売る、というのも面白いかもしれませんね。


AIエンジニアに必要なスキルが身に付く

CodeCampの無料体験はこちら

まとめ

今回 2種類の API、 1種類の Webスクレイピングで株価データの取得に挑戦しました。残念ながら日経225社分の 10年前の株価を一気に取得することはできませんでしたが、 Yahoo Finance API が一番早く、一番多くのデータを取得できました。

自動で株価を取得しようとした結果
Yahoo Finance API Webスクレイピング Alpha Vantage(API)
自動取得データ 190 46 1
不備データ 35 53 -
未スクレイピングデータ 0 126 224

月50ドルなどお金を払えば "APIサービス:Alpha Vantage" も使いやすくなると思いますが、個人利用で 月50ドルは大きいと思います。

「株価分析やってみたいけど、プログラムのこと難しいな...」「スクレイピングや API 興味あるけど、難しいな...」 と、悩んでいるあなた。一度プログラミングスクールを検討してみるのはどうでしょうか?

なかなか前に進めずに悩んでいた方が、CodeCamp を受講し、前進している例はたくさんあります(一つの例)。 「お金がない!」「時間がない」 と決めつける前に、一度無料体験でレッスンの価値を体験してみませんか?

無料体験は、完全オンラインで実施しますので、予定さえ合えばどこからでもレッスンを体験できます。この機会に是非、 CodeCamp の受講、 無料体験、検討してみてください。

関連記事

オシママサラ
この記事を書いた人
オシママサラ
\ 無料体験開催中!/自分のペースで確実に習得!
オンライン・プログラミングレッスンNo.1のCodeCamp