0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

直近1年の日経225の株価の動きが見たい

0
Posted at

はじめに

株式投資の分析やポートフォリオ作成において、「日経平均株価(日経225)の構成銘柄リストが欲しい!」と思う場面は多いですよね。

証券会社のサイトなど色々な場所に一覧はありますが、プログラムから直接扱いやすいデータフォーマット(CSVなど)でサクッと取得したい場合、Wikipediaの日経平均株価ページからスクレイピングしてくるのが一番手軽です。

今回は、Pythonを使って以下のステップを全自動化するスクリプトを作成しました。

  1. Wikipediaから「日経225構成銘柄」と「対象業界(セクター)」を取得
  2. 取得した銘柄リストを使って、Yahoo Finance(yfinance)から過去1年分の月次株価を一括取得
  3. 扱いやすいCSV形式で保存する

Wikipediaスクレイピングの注意点(マナー)
実装に入る前に、Wikipediaをスクレイピングする際の重要なマナーについて触れておきます。

Wikipediaの運営元(Wikimedia財団)は、自動化プログラムからのアクセスに対してUser-Agent を設定して誰がアクセスしているか明示すること」を求めています。
Pythonの requestspandas.read_html をデフォルトのまま使うと、403 Forbidden エラーで弾かれることがあります。そのため、リクエストヘッダに必ず User-Agent を設定しましょう。

また、今回は1ページのみのアクセスですが、ループで大量のページにアクセスする場合は time.sleep() などを入れてサーバーに負荷をかけない配慮が必要です。


準備と実行環境

以下のライブラリを使用します。インストールしていない場合は pip で入れておいてください。

pip install pandas requests beautifulsoup4 lxml yfinance
  • pandas, lxml: HTMLのテーブル解析やデータ整形用
  • requests, beautifulsoup4: HTML取得とDOM解析用
  • yfinance: Yahoo Financeから株価履歴を取得するため

実装コード

それでは、一気にデータを取得・整形してCSVに書き出すコードです。

import pandas as pd
import yfinance as yf
import requests
from bs4 import BeautifulSoup
import re
from io import StringIO

def get_nikkei_history():
    print("1. Wikipediaから日経225の構成銘柄と対象業界を取得中...")
    url = "https://ja.wikipedia.org/wiki/%E6%97%A5%E7%B5%8C%E5%B9%B3%E5%9D%87%E6%A0%AA%E4%BE%A1"
    
    # 403 Forbiddenエラー回避とマナーのためのUser-Agent設定
    headers = {'User-Agent': 'Mozilla/5.0'}
    
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.content, "html.parser")
    
    data = []
    current_industry = None
    
    # Wikipediaページ内の見出し(h3)とテーブル(table)を順番に解析
    # h3タグに「食品(11銘柄)」のように業界名が書かれているため、それを拾いながら下の表を見る
    for element in soup.find_all(['h3', 'table']):
        if element.name == 'h3':
            text = element.text.strip()
            if '銘柄)' in text:
                # "食品(10銘柄)" -> "食品" に整形
                current_industry = re.sub(r'(.*', '', text).strip()
        elif element.name == 'table' and current_industry:
            try:
                # StringIOでラップしてpandasの警告を回避
                dfs = pd.read_html(StringIO(str(element)))
                df = dfs[0]
                if '証券コード' in df.columns and '銘柄' in df.columns:
                    for _, row in df.iterrows():
                        data.append({
                            '証券コード': row['証券コード'],
                            '対象業界': current_industry,
                            '銘柄': row['銘柄']
                        })
            except Exception:
                pass
                
    result_df = pd.DataFrame(data)
    
    # 通し番号(1〜225)を先頭に追加
    result_df.insert(0, '通し番号', range(1, len(result_df) + 1))
    
    print(f"-> {len(result_df)} 銘柄情報を取得しました。\n")
    
    print("2. Yahoo Financeから過去1年の月次株価を取得中...")
    # 日本株はコード末尾に ".T" が必要
    tickers = [f"{code}.T" for code in result_df['証券コード']]
    ticker_str = " ".join(tickers)
    
    # period="1y", interval="1mo" で過去1年分の月次データを一括ダウンロード
    stock_data = yf.download(ticker_str, period="1y", interval="1mo", progress=False)
    open_prices = stock_data['Open']
    
    # 日付インデックスを "YYYY-MM" 形式にフォーマット
    months = [dt.strftime('%Y-%m') for dt in open_prices.index]
    open_prices.index = months
    
    # 各月のデータを列として追加
    for month in months:
        price_dict = open_prices.loc[month].to_dict()
        prices = []
        for code in result_df['証券コード']:
            ticker = f"{code}.T"
            price_val = price_dict.get(ticker, pd.NA)
            # データが存在しない場合はハイフンハイフン、ある場合は小数点1桁で丸める
            prices.append("-" if pd.isna(price_val) else round(price_val, 1))
        
        result_df[month] = prices
        
    print("-> 株価データの取得と結合が完了しました。\n")
    
    # UTF-8 with BOMでエクセルでも文字化けしないように保存
    output_filename = "nikkei225_1y_history.csv"
    result_df.to_csv(output_filename, index=False, encoding="utf-8-sig")
    print(f"{output_filename}』 として保存しました!")

if __name__ == "__main__":
    get_nikkei_history()

コードのポイント解説

1. BeautifulSouppandas の合わせ技

Wikipediaの日経225ページは、証券コードと銘柄名のテーブルが業種(セクター)ごとに分かれて配置されています。
単に pandas.read_html() ですべてのテーブルを取得してしまうと、「どの銘柄がどの業種なのか」という情報が抜け落ちてしまいます。

そこで、BeautifulSoup を使ってHTMLツリーを上から順に走査し、<h3>タグを見つけたら業種名を一時変数に保存、直後の <table>タグを見つけたらその業種名を紐付けながら pandas.read_html() でデータ化する、という少し工夫したロジックを入れています。

2. yfinance で一括ダウンロード

yfinance の素晴らしいところは、複数のティッカーシンボル(證券コード)をスペース区切りで渡すだけで、非同期的に一括でデータを取ってきてくれる点です。
forループで1銘柄ずつリクエストを送ると膨大な時間がかかってしまいますが、yf.download("7203.T 9984.T ...") のように渡せば数十秒で225銘柄の過去データが手に入ります。


実行結果 (CSVの出力イメージ)

スクリプトを実行すると、同階層に nikkei225_1y_history.csv が作成されます。Excelなどで開くと以下のようなデータ構造になります。

通し番号 証券コード 対象業界 銘柄 2025-04 2025-05 ... 2026-03
1 2002 食品 日清製粉グループ本社 2000.5 2050.0 ... 2100.0
2 2269 食品 明治ホールディングス 3500.0 3600.0 ... 3550.0
... ... ... ... ... ... ... ...

まとめ

Wikipediaのようなよく整理された公開情報と、yfinance のような強力なAPIを組み合わせることで、実用的な金融データセットを数秒で構築することができました。
スクレイピングのマナー(User-Agent)にだけ気をつければ、非常に強力な武器になるのでぜひ試してみてください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?