PERや配当利回りもPythonで自動収集!yfinanceで財務データを取得する方法

Python実装・コード

※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。

株式投資の判断材料はテクニカル分析だけではありません。

PER(株価収益率)、PBR(株価純資産倍率)、配当利回り、売上高、営業利益といったファンダメンタルズ指標を定量的に把握することが、中長期投資の精度を大きく左右します。

しかし、これらの財務データを銘柄ごとに証券会社のサイトで手動確認していては、膨大な時間がかかります。

特に複数銘柄を横断比較したい場合、手作業では到底追いつきません。

この記事では、世界標準のオープンソースライブラリ「yfinance」を使って、PER・PBR・配当利回り・財務諸表などの財務データをPythonで自動取得する方法を解説します。

1銘柄の詳細取得から複数銘柄の一括比較まで、すべてコピペで動作するコードを提供します。

yfinanceで取得できる財務データの全体像

yfinanceは株価データだけでなく、企業の財務情報やバリュエーション指標も取得できます。

まずは、どのような種類のデータにアクセスできるのかを整理します。

取得可能なデータカテゴリ

yfinanceの Ticker オブジェクトには、以下のプロパティが用意されています。

プロパティ名 内容 戻り値の型
.info 企業概要・バリュエーション指標の辞書 dict
.financials 損益計算書(年次) DataFrame
.quarterly_financials 損益計算書(四半期) DataFrame
.balance_sheet 貸借対照表(年次) DataFrame
.quarterly_balance_sheet 貸借対照表(四半期) DataFrame
.cashflow キャッシュフロー計算書(年次) DataFrame
.quarterly_cashflow キャッシュフロー計算書(四半期) DataFrame
.dividends 配当履歴 Series
.earnings_dates 決算発表日スケジュール DataFrame

.info プロパティは非常に多くのキーを含む辞書ですが、日本株の場合は一部のキーが欠損していることがあります。データが取得できない場合は None が返されるため、必ず存在チェックを行ってください。

.infoで取得できる主要なバリュエーション指標

.info 辞書の中でも、投資判断に直結する重要なキーを以下にまとめます。

  • trailingPE:実績PER(直近12ヶ月の実績ベース)
  • forwardPE:予想PER(アナリスト予想ベース)
  • priceToBook:PBR(株価純資産倍率)
  • dividendYield:配当利回り(小数表記。0.03 = 3%)
  • trailingEps:実績EPS(1株当たり利益)
  • marketCap:時価総額
  • totalRevenue:直近の売上高
  • operatingMargins:営業利益率(小数表記)
  • returnOnEquity:ROE(自己資本利益率)
  • debtToEquity:自己資本比率の逆数(負債/自己資本比率)

これらのデータを自動取得するだけで、スクリーニングの大半を機械化できます。

【コピペOK】1銘柄の財務データを詳細取得するコード

まずは1銘柄を対象に、バリュエーション指標と財務諸表を一括取得するコードを紹介します。

yfinanceの各プロパティの使い方を網羅的に確認できる内容です。

事前準備:ライブラリのインストール


pip install yfinance pandas

【コピペOK】1銘柄の財務データ取得コード


import yfinance as yf
import pandas as pd

# ==============================
# 設定エリア
# ==============================
SYMBOL = "8058.T"  # 三菱商事

# ==============================
# 安全にinfoの値を取得するヘルパー関数
# ==============================
def safe_get(info: dict, key: str, default="N/A"):
    ""辞書から安全に値を取得します。Noneの場合はデフォルト値を返します。""
    value = info.get(key)
    return value if value is not None else default

# ==============================
# バリュエーション指標の取得
# ==============================
def get_valuation_summary(symbol: str) -> pd.DataFrame:
    ""主要なバリュエーション指標を取得してDataFrameで返します。""
    ticker = yf.Ticker(symbol)
    info = ticker.info

    if not info:
        raise ValueError(f"{symbol} の企業情報を取得できませんでした。")

    # 配当利回りはパーセント表記に変換
    div_yield_raw = safe_get(info, "dividendYield", 0)
    div_yield_pct = f"{div_yield_raw * 100:.2f}%" if isinstance(div_yield_raw, (int, float)) else "N/A"

    # 営業利益率もパーセント表記に変換
    op_margin_raw = safe_get(info, "operatingMargins", 0)
    op_margin_pct = f"{op_margin_raw * 100:.1f}%" if isinstance(op_margin_raw, (int, float)) else "N/A"

    # ROEもパーセント表記に変換
    roe_raw = safe_get(info, "returnOnEquity", 0)
    roe_pct = f"{roe_raw * 100:.1f}%" if isinstance(roe_raw, (int, float)) else "N/A"

    data = {
        "企業名": safe_get(info, "longName"),
        "セクター": safe_get(info, "sector"),
        "業種": safe_get(info, "industry"),
        "時価総額(億円)": round(safe_get(info, "marketCap", 0) / 1e8, 1),
        "実績PER": safe_get(info, "trailingPE"),
        "予想PER": safe_get(info, "forwardPE"),
        "PBR": safe_get(info, "priceToBook"),
        "配当利回り": div_yield_pct,
        "実績EPS": safe_get(info, "trailingEps"),
        "営業利益率": op_margin_pct,
        "ROE": roe_pct,
        "売上高(億円)": round(safe_get(info, "totalRevenue", 0) / 1e8, 1),
    }

    df = pd.DataFrame([data]).T
    df.columns = [symbol]
    return df

# ==============================
# 財務諸表の取得
# ==============================
def get_financial_statements(symbol: str):
    ""損益計算書・貸借対照表・キャッシュフロー計算書を取得します。""
    ticker = yf.Ticker(symbol)

    print(f"n{'='*50}")
    print(f"  {symbol} - 損益計算書(年次・直近4期)")
    print(f"{'='*50}")
    financials = ticker.financials
    if financials is not None and not financials.empty:
        print(financials.to_string())
    else:
        print("データなし")

    print(f"n{'='*50}")
    print(f"  {symbol} - 貸借対照表(年次・直近4期)")
    print(f"{'='*50}")
    bs = ticker.balance_sheet
    if bs is not None and not bs.empty:
        print(bs.to_string())
    else:
        print("データなし")

    print(f"n{'='*50}")
    print(f"  {symbol} - キャッシュフロー計算書(年次・直近4期)")
    print(f"{'='*50}")
    cf = ticker.cashflow
    if cf is not None and not cf.empty:
        print(cf.to_string())
    else:
        print("データなし")

# ==============================
# 配当履歴の取得
# ==============================
def get_dividend_history(symbol: str) -> pd.Series:
    ""配当履歴を取得します。""
    ticker = yf.Ticker(symbol)
    dividends = ticker.dividends

    if dividends is not None and not dividends.empty:
        print(f"n{'='*50}")
        print(f"  {symbol} - 配当履歴(直近10件)")
        print(f"{'='*50}")
        print(dividends.tail(10))
    else:
        print("配当データなし")

    return dividends

# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    # 1. バリュエーション指標
    summary_df = get_valuation_summary(SYMBOL)
    print(f"n{'='*50}")
    print(f"  {SYMBOL} - バリュエーション指標")
    print(f"{'='*50}")
    print(summary_df.to_string())

    # 2. 財務諸表
    get_financial_statements(SYMBOL)

    # 3. 配当履歴
    get_dividend_history(SYMBOL)

実行すると、三菱商事のPER・PBR・配当利回りなどのバリュエーション指標、損益計算書・貸借対照表・キャッシュフロー計算書の年次データ、そして配当履歴がコンソールに出力されます。

.infoの戻り値が空になる場合の対処

日本株の場合、銘柄によっては .info の一部キーが None で返されることがあります。

上記コードの safe_get() 関数がこの問題に対応しています。

取得できないケースが多い項目は以下の通りです。

  • forwardPE(予想PER):アナリストカバレッジがない中小型株で欠損しやすい
  • returnOnEquity(ROE):会計基準の違いにより取得できない場合がある
  • operatingMargins(営業利益率):金融セクターでは算出方法が異なるため欠損することがある

欠損データがある場合でも、コードがエラーで停止しないように必ずデフォルト値を設定してください。

【コピペOK】複数銘柄の財務指標を一括比較するコード

1銘柄の詳細分析ができたら、次は複数銘柄を横断比較する実装に進みます。

スクリーニングやポートフォリオ分析に不可欠な機能です。

【コピペOK】複数銘柄の一括比較コード


import yfinance as yf
import pandas as pd
import time

# ==============================
# 設定エリア
# ==============================
SYMBOLS = {
    "8058.T": "三菱商事",
    "8031.T": "三井物産",
    "8001.T": "伊藤忠商事",
    "8002.T": "丸紅",
    "8053.T": "住友商事",
}
SLEEP_SEC = 1.0

# ==============================
# 安全取得ヘルパー
# ==============================
def safe_get(info: dict, key: str, default=None):
    value = info.get(key)
    return value if value is not None else default

def format_pct(value, decimals=2):
    ""小数をパーセント文字列に変換します。""
    if isinstance(value, (int, float)):
        return f"{value * 100:.{decimals}f}%"
    return "N/A"

# ==============================
# 複数銘柄の財務指標を一括取得
# ==============================
def compare_fundamentals(symbols: dict) -> pd.DataFrame:
    ""複数銘柄のバリュエーション指標を比較用DataFrameとして返します。""
    rows = []

    for i, (symbol, name) in enumerate(symbols.items(), start=1):
        print(f"[{i}/{len(symbols)}] {name}({symbol})を取得中...")

        try:
            ticker = yf.Ticker(symbol)
            info = ticker.info

            if not info:
                print(f"  ⚠ {symbol}: 情報が取得できません。スキップします。")
                continue

            row = {
                "銘柄コード": symbol,
                "企業名": name,
                "時価総額(億円)": round(safe_get(info, "marketCap", 0) / 1e8, 1),
                "実績PER": round(safe_get(info, "trailingPE", 0), 2),
                "予想PER": round(safe_get(info, "forwardPE", 0), 2) if safe_get(info, "forwardPE") else "N/A",
                "PBR": round(safe_get(info, "priceToBook", 0), 2),
                "配当利回り": format_pct(safe_get(info, "dividendYield")),
                "ROE": format_pct(safe_get(info, "returnOnEquity")),
                "営業利益率": format_pct(safe_get(info, "operatingMargins")),
                "売上高(億円)": round(safe_get(info, "totalRevenue", 0) / 1e8, 1),
            }
            rows.append(row)
            print(f"  ✔ 取得完了")

        except Exception as e:
            print(f"  ✖ {symbol}: エラー → {e}")

        if i < len(symbols):
            time.sleep(SLEEP_SEC)

    df = pd.DataFrame(rows)
    return df

# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    print("=== 総合商社5社 ファンダメンタルズ比較 ===n")
    comparison_df = compare_fundamentals(SYMBOLS)

    print("n" + "=" * 80)
    print(comparison_df.to_string(index=False))
    print("=" * 80)

    # CSV保存
    comparison_df.to_csv("fundamentals_comparison.csv", index=False, encoding="utf-8-sig")
    print("n✔ 保存完了: fundamentals_comparison.csv")

実行すると、総合商社5社(三菱商事・三井物産・伊藤忠商事・丸紅・住友商事)のバリュエーション指標が一覧テーブルで出力されます。

encoding="utf-8-sig" を指定しているため、CSVファイルはExcelで開いても文字化けしません。

カスタマイズのヒント:セクターを変えて比較する

SYMBOLS 辞書を書き換えるだけで、任意のセクターの比較が可能です。


# メガバンク3行の比較例
SYMBOLS = {
    "8306.T": "三菱UFJ",
    "8316.T": "三井住友FG",
    "8411.T": "みずほFG",
}

同一セクター内での比較は、割安・割高の判断において最も有効なアプローチです。

PERやPBRの絶対値だけでなく、「セクター内での相対的な位置づけ」を確認する習慣をつけてください。

【コピペOK】財務データの時系列推移を可視化するコード

バリュエーション指標のスナップショットだけでなく、財務諸表の推移をグラフ化することで、業績トレンドが一目でわかるようになります。

【コピペOK】売上高・営業利益の推移グラフ描画コード


import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
import os

# ==============================
# 設定エリア
# ==============================
SYMBOL = "6758.T"    # ソニーグループ
OUTPUT_DIR = "charts"
os.makedirs(OUTPUT_DIR, exist_ok=True)

# ==============================
# 財務データ取得+グラフ保存
# ==============================
def plot_financial_trend(symbol: str):
    ""損益計算書から売上高と営業利益の推移をグラフ化します。""
    ticker = yf.Ticker(symbol)
    financials = ticker.financials

    if financials is None or financials.empty:
        print(f"{symbol} の損益計算書を取得できませんでした。")
        return

    # 行名の確認(yfinanceのバージョンにより異なる場合あり)
    print("--- 取得可能な項目一覧 ---")
    for idx in financials.index:
        print(f"  {idx}")

    # 売上高と営業利益を抽出(キー名はバージョンにより異なる可能性あり)
    revenue_keys = ["Total Revenue", "Operating Revenue"]
    income_keys = ["Operating Income", "EBIT"]

    revenue = None
    for key in revenue_keys:
        if key in financials.index:
            revenue = financials.loc[key].sort_index()
            break

    operating_income = None
    for key in income_keys:
        if key in financials.index:
            operating_income = financials.loc[key].sort_index()
            break

    if revenue is None:
        print("売上高データが見つかりませんでした。")
        return

    # --- グラフ描画 ---
    fig, ax1 = plt.subplots(figsize=(10, 6))

    # 売上高(棒グラフ)
    years = [d.strftime("%Y") for d in revenue.index]
    revenue_billion = revenue.values / 1e9  # 10億円単位
    bars = ax1.bar(years, revenue_billion, color="steelblue", alpha=0.7, label="Revenue (B JPY)")
    ax1.set_ylabel("Revenue (Billion JPY)", fontsize=11)
    ax1.set_xlabel("Fiscal Year", fontsize=11)

    # 営業利益(折れ線グラフ)
    if operating_income is not None:
        ax2 = ax1.twinx()
        income_billion = operating_income.values / 1e9
        ax2.plot(years, income_billion, color="red", marker="o", linewidth=2, label="Operating Income (B JPY)")
        ax2.set_ylabel("Operating Income (Billion JPY)", fontsize=11, color="red")
        ax2.legend(loc="upper left")

    ax1.set_title(f"{symbol} - Revenue & Operating Income Trend", fontsize=14)
    ax1.legend(loc="upper right")
    ax1.grid(True, alpha=0.3, axis="y")

    filepath = os.path.join(OUTPUT_DIR, f"{symbol.replace('.', '_')}_financials.png")
    fig.savefig(filepath, dpi=150, bbox_inches="tight", facecolor="white")
    plt.close(fig)
    print(f"n✔ グラフ保存完了: {filepath}")

# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    plot_financial_trend(SYMBOL)

このコードを実行すると、ソニーグループの売上高(棒グラフ)と営業利益(折れ線グラフ)の推移が1枚のチャートに描画され、PNG画像として保存されます。

2軸グラフを使うことで、スケールの異なる2つの指標を同時に比較できます。

財務諸表の項目名がバージョンによって異なる問題

yfinanceが返す財務諸表のインデックス名(行名)は、ライブラリのバージョンやYahoo Financeのデータ仕様変更によって異なることがあります。

上記コードでは、売上高のキーとして "Total Revenue""Operating Revenue" の両方を候補リストで管理し、存在するほうを使用する実装にしています。

新しい項目名に対応する際は、まず financials.index の一覧を出力して確認してください。

よくあるエラーと対処法

財務データの取得は株価データと比べてハマりやすいポイントが多くあります。

ここでは頻出のトラブルとその対処法を解説します。

.infoにアクセスすると「JSONDecodeError」が発生します

yfinanceはYahoo Financeのウェブページからデータをスクレイピングしています。

Yahoo Finance側の仕様変更やサーバー障害時に、JSON解析エラーが発生することがあります。

対処法は以下の通りです。

  • yfinanceを最新バージョンに更新してください:pip install --upgrade yfinance
  • 時間を置いて再実行してください
  • 特定の銘柄だけで発生する場合は、その銘柄のYahoo Financeページ自体がエラーになっている可能性があります

日本株の財務諸表が空(Empty DataFrame)で返されます

日本株の一部銘柄では、Yahoo Finance上に財務諸表データが登録されていないことがあります。

特に以下のケースで空データになりやすい傾向です。

  • REIT(不動産投資信託)やETF
  • 上場間もない新規IPO銘柄
  • 小型株やマイナーな銘柄

この場合、有料の財務データベース(Refinitiv、Bloomberg等)を利用するか、EDINETのXBRLデータを直接解析する方法が代替手段となります。

配当利回りが実際と大きくズレています

yfinanceの dividendYield はYahoo Financeの算出ロジックに依存しており、証券会社の表示値と異なる場合があります。

主な原因は以下の通りです。

  • 算出基準日の違い:直近12ヶ月の実績配当で計算している場合と、予想配当で計算している場合があります
  • 特別配当の扱い:記念配当や特別配当が含まれている場合、利回りが一時的に跳ね上がります
  • 為替の影響:ADR銘柄では為替換算により数値がズレることがあります

yfinanceの財務データはあくまで「スクリーニングの初期段階」として活用し、最終的な投資判断は証券会社の公式データや有価証券報告書で裏取りすることを強く推奨します。

まとめ

この記事では、yfinanceを使ってPER・PBR・配当利回りなどのバリュエーション指標や、損益計算書・貸借対照表・キャッシュフロー計算書といった財務諸表をPythonで自動取得する方法を解説しました。

要点を整理します。

  • .info プロパティからPER・PBR・配当利回り・ROEなどの主要指標を辞書形式で取得できます
  • .financials.balance_sheet.cashflow で年次・四半期の財務諸表をDataFrame形式で取得できます
  • 日本株では一部データが欠損することがあるため、safe_get() のような安全な取得関数を必ず実装してください
  • 複数銘柄を一括取得して横断比較することで、セクター内のスクリーニングが自動化できます
  • 財務データの時系列をグラフ化すれば、業績トレンドを視覚的に把握できます

次のステップとして、今回の財務指標データとテクニカル指標(RSI・MACDなど)を組み合わせた「ファンダ×テクニカル複合スクリーニング」の構築に挑戦してみてください。

「割安かつ上昇シグナルが出ている銘柄」を自動検出できれば、投資の効率と精度が飛躍的に向上します。

タイトルとURLをコピーしました