Yahooファイナンスのスクレイピングを行います。
今回は、過去の株価を取得します。
いわゆる時系列データというモノです。
時系列データがあれば、チャートも描くことができます。
本記事の内容
- ここまでの流れ【Yahooファイナンスのスクレイピング】
- 時系列ページのスクレイピング仕様
- 時系列ページから株価時系列データを抽出
それでは、上記に沿って解説していきます。
ここまでの流れ【Yahooファイナンスのスクレイピング】
Yahooファイナンスのスクレイピングに関しては、段階を踏んで解説しています。
今回は、時系列ページのスクレイピングがメインです。
下記で過去の同シリーズを記載します。
第1弾
第2弾
第3弾
第1弾の記事は、スクレイピングをする上では必読の内容です。
その内容を理解していないと、犯罪者になってしまうかもしれません。
第2弾の記事は、銘柄コードのリストの作成方法を解説しています。
全部で3849件の銘柄リストを取得できました。
このようなリストを作成するのが、スクレイピングの基本中の基本です。
そして、このリストをもとに取得したいデータのページにアクセスが可能となります。
Yahooファイナンスなら、そのためのベースが銘柄コードなのです。
第3弾の記事では、銘柄コードをもとに企業情報ページへアクセスしています。
そして、企業情報ページから企業情報をスクレイピングしています。
第4弾となる今回は、時系列ページから情報を抽出していきます。
では、時系列ページからデータを抽出するための仕様を確認していきます。
時系列ページのスクレイピング仕様
まずは、時系列ページの確認からです。

上記を見ればわかるように、時系列データには次の二つが存在します。
- 株価時系列データ
- 信用残時系列データ
本記事では、株価時系列データを対象とします。
おそらく、時系列データと言えば、株価時系列データという認識の人がほとんどでしょう。
信用残データも株価との相関関係があるはずです。
その意味では、取得する価値はあるのかもしれません。
もちろん、他にも用途はあるでしょう。
しかし、今回は株価時系列データをターゲットにします。
株価時系列データをターゲットにする場合、考えるべきポイントは以下。
- 株価時系列データページのURL作成
- データ表示条件の決定
- 改ページ対応
- 株価時系列データの抽出
それぞれを下記で説明します。
株価時系列データページのURL作成
株価時系列データページのURLについて説明しておきます。
銘柄コードのリストから、自動的に各銘柄ごとの株価時系列データページのURLを作成します。
そのときの形式は、以下となります。
「https://stocks.finance.yahoo.co.jp/stocks/history/?code=●」
●は、銘柄コードです。
上記形式のURLで株価時系列データページにアクセス可能となります。
例えば、株式会社 極洋(1301)の場合は次のURLです。
https://stocks.finance.yahoo.co.jp/stocks/history/?code=1301
ただ、上記URLは利用しません。
次の「データ表示条件の決定」でその理由がわかります。
結論は、「まとめ」で説明します。
データ表示条件の決定
表示条件とは、以下のことです。

デフォルトだと、デイリーで1ヵ月がデータ表示の条件となっています。
この条件で「表示」ボタンをクリックすると、次のURLページへ遷移します。
「https://info.finance.yahoo.co.jp/history/?code=1301.T&sy=2021&sm=1&sd=14&ey=2021&em=2&ed=13&tm=d」
上記URLは、以下の2点で注目ポイントがあります。
- ドメイン
- クエリパラメータ
下記で説明します。
ドメイン
「株価時系列データページのURL作成」で確認したURLは、以下。
https://stocks.finance.yahoo.co.jp/stocks/history/?code=1301
「表示」ボタンをクリックして遷移した先のURLは、以下。
https://info.finance.yahoo.co.jp/history/?code=1301.T&sy=2021&sm=1&sd=14&ey=2021&em=2&ed=13&tm=d
それぞれのドメインを抽出すると以下となります。
- stocks.finance.yahoo.co.jp
- info.finance.yahoo.co.jp
正直、これには自分の目を疑いました。
でも、この事実を受け入れていきましょう。
あと、何気にドメイン以降のパスも異なります。
- /stocks/history/
- /history/
せめて、ここは同じにしましょうよ・・・
まあ、スクレイピング対策になっていると言えば、なっていますけどね。
クエリパラメータ
クエリパラメータは、以下。
パラメータ | 値 |
sy | 2021 |
sm | 1 |
sd | 14 |
ey | 2021 |
em | 2 |
ed | 13 |
tm | d |
説明するまでもありませんね。
ただ、tmだけは確認しておきましょう。
tmには、以下の値が設定可能です。
d | デイリー |
w | 週刊 |
m | 月間 |
わかりやすいですね。
その意味では、Yahooファイナンスはスクレイピングが容易と言えます。
改ページ対応
データ表示条件を2020年の1年間でデイリーとした場合、件数部分が次のように表示されます。

株価時系列データページでは、1ページに20件しか表示しません。
そのため、改ページへの対応が必要となります。
対応方法は、二つあります。
- クエリパラメータに「&p=●」を付ける
- 「次へ」リンクをクリックする
※●はページ数(1,2,3・・・)
Yahooファイナンスでは、両方の対応を取ることができます。
今回は、「次へ」リンクをクリックする対応を採用します。
クエリパラメータの方式は、対応できないサイトも存在します。
汎用性を考えたら、「次へ」リンクをクリックする方式がベターです。
それにSeleniumを利用している以上は、できる限り利用して身に付けていきましょう。
株価時系列データの抽出
各データ行のhtmlタグは、以下。

class名指定ではスクレイピングはできませんね。
各tr毎に存在するtd要素の順番により、データ項目を決定できそうです。
順番(0スタート) | データ項目 |
0 | 日付 |
1 | 始値 |
2 | 高値 |
3 | 安値 |
4 | 終値 |
5 | 出来高 |
6 | 調整後終値* |
念のため、「class=boardFin」のtable要素以下で対象となるtr・tdを絞り込みます。
まとめ
改ページ処理は、「次へ」リンクをクリックしていく形式を採用します。
そのため、スクレイピングする上でアクセスするURLは最初の一つだけです。
銘柄コード1301の2020年におけるデイリーの株価時系列データを得る場合のURLは、以下。
https://info.finance.yahoo.co.jp/history/?code=1301&sy=2021&sm=1&sd=14&ey=2021&em=2&ed=13&tm=d
上記URLにアクセスして、あとは「次へ」リンクをクリックしていくことになります。
もちろん、各ページ最大20件の株価時系列データを抽出しながらです。
以上より、スクレイピングの仕様が決まりました。
次は、実際に株価時系列データをスクレイピングしていきましょう。
時系列ページから株価時系列データを抽出
時系列ページから株価時系列データを抽出するコードは、以下。
現時点(2021年2月13日)では元気モリモリ動いています。
サンプルコード
import sys import bs4 import traceback import re import time from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains # ドライバーのフルパス CHROMEDRIVER = "chromedriver.exeのパス" # 改ページ(最大) PAGE_MAX = 2 # 遷移間隔(秒) INTERVAL_TIME = 3 # 開始年・月・日 SY = 2020 SM = 1 SD = 1 # 終了年・月・日 EY = 2020 EM = 12 ED = 31 # タイプ(d:デイリー、w:週刊、m:月間) TM = "d" # ドライバー準備 def get_driver(): # ヘッドレスモードでブラウザを起動 options = Options() options.add_argument( '--headless' ) # ブラウザーを起動 driver = webdriver.Chrome(CHROMEDRIVER, options = options) return driver # 対象ページのソース取得 def get_source_from_page(driver, page): try : # ターゲット driver.get(page) driver.implicitly_wait( 10 ) # 見つからないときは、10秒まで待つ page_source = driver.page_source return page_source except Exception as e: print ( "Exception\n" + traceback.format_exc()) return None # ソースからスクレイピングする def get_data_from_source(src): # スクレイピングする soup = bs4.BeautifulSoup(src, features = 'lxml' ) # print(soup) try : info = [] table = soup.find( "table" , class_ = "boardFin" ) if table: elems = table.find_all( "tr" ) for elem in elems: td_tags = elem.find_all( "td" ) if len (td_tags) > 0 : row_info = [] tmp_counter = 0 for td_tag in td_tags: tmp_text = td_tag.text if tmp_counter = = 0 : # 年月日 tmp_text = tmp_text else : tmp_text = extract_num(tmp_text) row_info.append(tmp_text) tmp_counter = tmp_counter + 1 info.append(row_info) return info except Exception as e: print ( "Exception\n" + traceback.format_exc()) return None # 次のページへ遷移 def next_btn_click(driver): try : # 次へボタン elem_btn = WebDriverWait(driver, 10 ).until( EC.visibility_of_element_located((By.LINK_TEXT, '次へ' )) ) # クリック処理 actions = ActionChains(driver) actions.move_to_element(elem_btn) actions.click(elem_btn) actions.perform() # 間隔を設ける(秒単位) time.sleep(INTERVAL_TIME) return True except Exception as e: print ( "Exception\n" + traceback.format_exc()) return False # 数値だけ抽出 def extract_num(val): num = None if val: match = re.findall( "\d+\.\d+" , val) if len (match) > 0 : num = match[ 0 ] else : num = re.sub( "\\D" , "", val) if not num: num = 0 return num if __name__ = = "__main__" : # 引数 args = sys.argv # 銘柄コード code = "1301" if len (args) = = 2 : # 引数があれば、それを使う code = args[ 1 ] # 対象ページURL page = "https://info.finance.yahoo.co.jp/history/?code=" + code page = page + "&sy=" + str (SY) + "&sm=" + str (SM) + "&sd=" + str (SD) page = page + "&ey=" + str (EY) + "&em=" + str (EM) + "&ed=" + str (ED) page = page + "&tm=" + TM # ブラウザのdriver取得 driver = get_driver() # ページのソース取得 source = get_source_from_page(driver, page) result_flg = True # ページカウンター制御 page_counter = 0 while result_flg: page_counter = page_counter + 1 # ソースからデータ抽出 data = get_data_from_source(source) # データ保存 print (data) # 改ページ処理を抜ける if page_counter = = PAGE_MAX: break # 改ページ処理 result_flg = next_btn_click(driver) source = driver.page_source # 閉じる driver.quit() |
プログラム詳細は、「時系列ページのスクレイピング仕様」とコード上のコメントをご覧ください。
不明な点がある場合は、同シリーズの過去記事を確認してください。
ただ、次の「対象ページURL」に関しては説明しておきます。
page = "https://info.finance.yahoo.co.jp/history/?code=" + code page = page + "&sy=" + str (SY) + "&sm=" + str (SM) + "&sd=" + str (SD) page = page + "&ey=" + str (EY) + "&em=" + str (EM) + "&ed=" + str (ED) page = page + "&tm=" + TM |
クエリパラメータを指定したURLを作成しています。
そして、ここで用いられる定数は以下の部分です。
# 開始年・月・日 SY = 2020 SM = 1 SD = 1 # 終了年・月・日 EY = 2020 EM = 12 ED = 31 # タイプ(d:デイリー、w:週刊、m:月間) TM = "d" |
特に問題はありませんね。
ここの値を変更すれば、取得したい条件でスクレイピングが可能です。
実行結果
サンプルコードを実行した結果は、以下。
[[ '2020年12月30日' , '2947' , '2960' , '2923' , '2951' , '11100' , '2951' ], [ '2020年12月29日' , '2948' , '2963' , '2945' , '2961' , '15100' , '2961' ], [ '2020年12月28日' , '2929' , '2950' , '2910' , '2950' , '22900' , '2950' ], [ '2020年12月25日' , '2903' , '2930' , '2903' , '2916' , '8300' , '2916' ], [ '2020年12月24日' , '2913' , '2937' , '2909' , '2917' , '13900' , '2917' ], [ '2020年12月23日' , '2913' , '2920' , '2906' , '2913' , '6300' , '2913' ], [ '2020年12月22日' , '2947' , '2947' , '2907' , '2913' , '18000' , '2913' ], [ '2020年12月21日' , '2939' , '2947' , '2912' , '2947' , '11300' , '2947' ], [ '2020年12月18日' , '2935' , '2938' , '2907' , '2937' , '15800' , '2937' ], [ '2020年12月17日' , '2872' , '2920' , '2870' , '2920' , '18900' , '2920' ], [ '2020年12月16日' , '2882' , '2882' , '2871' , '2871' , '8900' , '2871' ], [ '2020年12月15日' , '2880' , '2897' , '2874' , '2881' , '12900' , '2881' ], [ '2020年12月14日' , '2830' , '2888' , '2830' , '2888' , '31200' , '2888' ], [ '2020年12月11日' , '2810' , '2846' , '2810' , '2841' , '18400' , '2841' ], [ '2020年12月10日' , '2812' , '2828' , '2806' , '2815' , '14900' , '2815' ], [ '2020年12月9日' , '2816' , '2827' , '2807' , '2820' , '6600' , '2820' ], [ '2020年12月8日' , '2807' , '2824' , '2805' , '2816' , '10400' , '2816' ], [ '2020年12月7日' , '2820' , '2830' , '2806' , '2811' , '12900' , '2811' ], [ '2020年12月4日' , '2819' , '2819' , '2800' , '2811' , '9200' , '2811' ], [ '2020年12月3日' , '2810' , '2816' , '2796' , '2807' , '9400' , '2807' ]][[ '2020年12月2日' , '2806' , '2817' , '2789' , '2811' , '25700' , '2811' ], [ '2020年12月1日' , '2824' , '2824' , '2780' , '2787' , '17900' , '2787' ], [ '2020年11月30日' , '2816' , '2825' , '2795' , '2795' , '18900' , '2795' ], [ '2020年11月27日' , '2807' , '2825' , '2803' , '2819' , '22000' , '2819' ], [ '2020年11月26日' , '2820' , '2826' , '2801' , '2809' , '8700' , '2809' ], [ '2020年11月25日' , '2839' , '2846' , '2795' , '2831' , '27800' , '2831' ], [ '2020年11月24日' , '2850' , '2860' , '2806' , '2807' , '20100' , '2807' ], [ '2020年11月20日' , '2848' , '2848' , '2820' , '2832' , '10200' , '2832' ], [ '2020年11月19日' , '2834' , '2846' , '2812' , '2838' , '11600' , '2838' ], [ '2020年11月18日' , '2813' , '2838' , '2790' , '2838' , '14400' , '2838' ], [ '2020年11月17日' , '2800' , '2813' , '2785' , '2813' , '13100' , '2813' ], [ '2020年11月16日' , '2820' , '2820' , '2788' , '2804' , '15800' , '2804' ], [ '2020年11月13日' , '2821' , '2821' , '2771' , '2784' , '10500' , '2784' ], [ '2020年11月12日' , '2807' , '2839' , '2805' , '2814' , '13900' , '2814' ], [ '2020年11月11日' , '2810' , '2850' , '2796' , '2850' , '27300' , '2850' ], [ '2020年11月10日' , '2795' , '2819' , '2772' , '2793' , '27000' , '2793' ], [ '2020年11月9日' , '2800' , '2800' , '2757' , '2795' , '15800' , '2795' ], [ '2020年11月6日' , '2771' , '2786' , '2671' , '2782' , '30900' , '2782' ], [ '2020年11月5日' , '2745' , '2790' , '2671' , '2671' , '31400' , '2671' ], [ '2020年11月4日' , '2763' , '2768' , '2744' , '2746' , '12000' , '2746' ]] |
最初の2ページ分の40件だけです。
それは、以下のように「2」と設定しているからです。
# 改ページ(最大) PAGE_MAX = 2 |
適当にここを「99999」などにすれば、全ページ分をスクレイピングします。
もしくは、PAGE_MAXの制御を無効にするかです。
まとめ
以下は、本ブログにおけるスクレイピングでは当たり前の定数です。
# ドライバーのフルパス CHROMEDRIVER = "chromedriver.exeのパス" # 改ページ(最大) PAGE_MAX = 2 # 遷移間隔(秒) INTERVAL_TIME = 3 |
この定数に関して、わからないところがある場合は過去記事をご覧ください。
メルカリのスクレイピングシリーズが、特に参考になります。
メルカリのスクレイピング第1弾
メルカリのスクレイピング第2弾
メルカリのスクレイピング第3弾