Pythonで高配当銘柄を自動抽出する方法|利回りスクリーニング術

Python実装・コード

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

高配当銘柄を手作業でスクリーニングするのは、銘柄数が多いと現実的ではないです。yfinanceで配当利回りを自動取得して複合条件で絞り込む方が効率的です。ということで、Pythonで高配当銘柄を自動抽出するコードをまとめます。

📘 外部参考Python 公式(ダウンロード)Python 公式ドキュメント(日本語)

📘 外部参考yfinance 公式GitHubPyPI

多くの個人投資家は、手作業で一銘柄ずつ配当情報を調べています。日本の上場企業は約3,800社あり、すべてを目視で確認するのは難しいです。

原因は、財務データの一括取得方法とフィルタリングのコードを知らないことにあります。yfinanceは個別銘柄の配当情報を取得できますが、数百銘柄を効率的にループ処理するにはコツが必要です。

ということで、この記事では、日本株の主要銘柄から配当利回りが指定条件を満たす銘柄を自動抽出するPythonコードを提供します。さらに、配当性向(Payout Ratio)やPBRを加えた複合スクリーニングの応用コードも解説します。

コードはすべてコピペで動作します。SBI証券での長期保有候補のリストアップに、日々の銘柄選定ワークフローとして組み込んでください。

配当利回りの基本知識とスクリーニングの判断基準

配当利回りの計算式と目安

配当利回り(Dividend Yield)は、1株あたりの年間配当金を現在の株価で割った値です。計算式は以下のとおりです。

配当利回り(%)= 年間配当金 ÷ 株価 × 100

一般的に、配当利回り3%以上が「高配当」の目安とされます。4%以上であれば市場平均を大きく上回る水準です。ただし、利回りが極端に高い(8%以上など)銘柄は、株価急落による見かけ上の高利回りである可能性を疑ってください。

スクリーニングで併用すべき指標

配当利回りだけで銘柄を選ぶのは危険です。以下の指標を組み合わせることで、「減配リスクの低い真の高配当銘柄」を見極められます。

指標 意味 目安
配当性向(Payout Ratio) 利益のうち配当に回す割合 60%以下が安全圏
PBR(Price Book-value Ratio) 株価純資産倍率 1.0倍以下で割安
自己資本比率 総資産に対する自己資本の割合 40%以上が健全

配当性向が80%を超えている銘柄は、利益の大部分を配当に充てており、減配リスクが高いと判断してください。

【コピペOK】高配当銘柄を自動抽出するメインコード

まず、必要なライブラリをインストールしてください。


pip install yfinance pandas

以下がメインコードです。


import time
from typing import Any, Optional

import pandas as pd
import yfinance as yf

# ==============================
# 設定エリア
# ==============================
# スクリーニング対象の銘柄コードリスト(東証主要銘柄の例)
# 実運用では日経225構成銘柄等のリストに差し替えてください
TARGET_CODES: list[str] = [
    "1605", "1925", "1928", "2002", "2269", "2413", "2502", "2503",
    "2802", "2914", "3382", "3402", "3407", "4005", "4042", "4063",
    "4188", "4452", "4502", "4503", "4507", "4519", "4523", "4568",
    "4578", "4661", "4689", "4901", "4911", "5020", "5108", "5201",
    "5401", "5411", "5713", "5802", "6098", "6178", "6273", "6301",
    "6326", "6367", "6501", "6502", "6503", "6586", "6645", "6702",
    "6752", "6758", "6861", "6902", "6954", "6971", "6981", "7011",
    "7201", "7203", "7267", "7269", "7270", "7733", "7741", "7751",
    "7832", "7974", "8001", "8002", "8015", "8031", "8035", "8053",
    "8058", "8233", "8252", "8267", "8306", "8308", "8309", "8316",
    "8331", "8354", "8411", "8601", "8604", "8630", "8697", "8725",
    "8750", "8766", "8795", "8801", "8802", "8830", "9020", "9021",
    "9022", "9064", "9101", "9104", "9107", "9202", "9432", "9433",
    "9434", "9501", "9502", "9503", "9531", "9532", "9602", "9613",
    "9735", "9766", "9843", "9984",
]

SUFFIX: str = ".T"                   # 東証サフィックス
MIN_DIVIDEND_YIELD: float = 3.5      # 最低配当利回り(%)
MAX_PAYOUT_RATIO: float = 70.0       # 最大配当性向(%)
REQUEST_INTERVAL: float = 0.8        # APIリクエスト間隔(秒)
OUTPUT_CSV: str = "high_dividend_screening.csv"


# ==============================
# 単一銘柄の財務情報取得
# ==============================
def fetch_dividend_info(ticker_code: str) -> Optional[dict[str, Any]]:
    'yfinanceから配当関連の財務情報を取得する'
    ticker_str: str = ticker_code + SUFFIX
    try:
        tk: yf.Ticker = yf.Ticker(ticker_str)
        info: dict = tk.info

        dividend_yield: Optional[float] = info.get("dividendYield")
        if dividend_yield is None:
            return None

        return {
            "銘柄コード": ticker_code,
            "ticker": ticker_str,
            "銘柄名": info.get("shortName", "不明"),
            "株価": info.get("currentPrice", info.get("regularMarketPrice")),
            "配当利回り(%)": round(dividend_yield * 100, 2),
            "年間配当金": info.get("dividendRate"),
            "配当性向(%)": _safe_round(info.get("payoutRatio"), 100),
            "PBR": _safe_round(info.get("priceToBook"), 1),
            "自己資本比率(%)": _calc_equity_ratio(info),
            "セクター": info.get("sector", "不明"),
        }
    except Exception:
        return None


# ==============================
# ユーティリティ関数群
# ==============================
def _safe_round(
    value: Optional[float],
    multiplier: float,
) -> Optional[float]:
    'None安全な四捨五入処理'
    if value is None:
        return None
    return round(value * multiplier, 2)


def _calc_equity_ratio(info: dict) -> Optional[float]:
    '総資産と自己資本から自己資本比率を算出する'
    total_assets: Optional[float] = info.get("totalAssets")
    equity: Optional[float] = info.get("totalStockholderEquity")
    if total_assets and equity and total_assets > 0:
        return round(equity / total_assets * 100, 1)
    return None


# ==============================
# スクリーニング実行
# ==============================
def run_screening(
    codes: list[str],
    min_yield: float,
    max_payout: float,
    interval: float,
) -> pd.DataFrame:
    '全銘柄をスキャンし条件を満たす銘柄をDataFrameで返す'
    results: list[dict] = []
    total: int = len(codes)

    for i, code in enumerate(codes, start=1):
        print(f"  [{i}/{total}] {code}.T を取得中...")
        info: Optional[dict] = fetch_dividend_info(code)

        if info is None:
            continue

        # 配当利回りフィルタ
        if info["配当利回り(%)"] < min_yield:
            continue

        # 配当性向フィルタ(データがある場合のみ)
        payout: Optional[float] = info["配当性向(%)"]
        if payout is not None and payout > max_payout:
            continue

        results.append(info)
        time.sleep(interval)

    df: pd.DataFrame = pd.DataFrame(results)
    if not df.empty:
        df = df.sort_values("配当利回り(%)", ascending=False)
    return df


# ==============================
# 結果出力
# ==============================
def export_results(df: pd.DataFrame, filepath: str) -> None:
    'スクリーニング結果をCSVとコンソールに出力する'
    if df.empty:
        print("条件に合致する銘柄が見つかりませんでした。閾値を調整してください。")
        return

    print("n" + "=" * 60)
    print(f"スクリーニング結果: {len(df)}銘柄が条件に合致")
    print("=" * 60)
    display_cols: list[str] = [
        "銘柄コード", "銘柄名", "株価", "配当利回り(%)",
        "配当性向(%)", "PBR", "セクター",
    ]
    print(df[display_cols].to_string(index=False))

    df.to_csv(filepath, index=False, encoding="utf-8-sig")
    print(f"nCSVファイルを保存しました: {filepath}")


# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    print("高配当銘柄スクリーニングを開始します...")
    print(f"条件: 配当利回り {MIN_DIVIDEND_YIELD}%以上 / 配当性向 {MAX_PAYOUT_RATIO}%以下")
    print(f"対象: {len(TARGET_CODES)}銘柄n")

    screening_df: pd.DataFrame = run_screening(
        TARGET_CODES,
        MIN_DIVIDEND_YIELD,
        MAX_PAYOUT_RATIO,
        REQUEST_INTERVAL,
    )

    export_results(screening_df, OUTPUT_CSV)

コードの処理フロー解説

上記のコードは、以下の5ステップで構成されています。

* ステップ1 銘柄リスト定義TARGET_CODESに東証主要銘柄のコードを列挙する。日経225全銘柄やTOPIX100銘柄に差し替えることで対象を拡大できる

* ステップ2 個別取得yf.Ticker().infoから配当利回り・配当性向・PBR等の財務情報を取得する。API負荷を抑えるため0.8秒間隔でリクエストする

* ステップ3 フィルタリング:配当利回りが閾値以上かつ配当性向が閾値以下の銘柄だけを抽出する

* ステップ4 ソート:配当利回りの降順でソートし、最も利回りの高い銘柄を先頭に配置する

* ステップ5 出力:結果をコンソールに表示し、CSVファイルとしても保存する

MIN_DIVIDEND_YIELD4.0に変更すれば、より厳選された高配当銘柄のみを抽出できます。

スクリーニング結果の読み解き方と実践的な銘柄評価

配当利回りが高すぎる銘柄の危険性

スクリーニング結果の上位に利回り7%や8%の銘柄が並んだ場合、無条件に飛びついてはいけません。異常に高い利回りは、株価の急落によって「分母が小さくなった」結果であるケースがほとんどです。

株価が下落した原因を必ず確認してください。業績悪化や減配発表が理由であれば、今後さらに配当が減る可能性が高いです。直近3年間の配当推移が安定または増加傾向にあるかを、追加の判断材料にしてください。

SBI証券での活用フロー

毎月1回、このコードを実行してCSVを出力してください。前月のCSVと比較することで、新たにスクリーニング条件を満たした銘柄や、条件から外れた銘柄を把握できます。

SBI証券の「銘柄スクリーナー」と併用し、Pythonの出力結果をSBI側のウォッチリストに登録するのが効率的です。最終的な投資判断は、決算短信やIR資料も確認したうえで行ってください。

【コピペOK】複合条件スクリーニングの応用コード


import time
from typing import Any, Optional

import pandas as pd
import yfinance as yf

# ==============================
# 設定エリア
# ==============================
TARGET_CODES: list[str] = [
    "2914", "4502", "4503", "5020", "5401", "6301", "7203",
    "8001", "8031", "8053", "8058", "8306", "8316", "8411",
    "8766", "9432", "9433", "9434", "9101", "9104", "9107",
]
SUFFIX: str = ".T"
REQUEST_INTERVAL: float = 0.8

# 複合スクリーニング条件
FILTER_MIN_YIELD: float = 3.0        # 配当利回り下限(%)
FILTER_MAX_PAYOUT: float = 60.0      # 配当性向上限(%)
FILTER_MAX_PBR: float = 1.5          # PBR上限(倍)
OUTPUT_CSV: str = "advanced_screening.csv"


# ==============================
# 財務情報取得(メインコードと共通)
# ==============================
def fetch_info(code: str) -> Optional[dict[str, Any]]:
    '銘柄の財務情報を辞書で返す'
    try:
        info: dict = yf.Ticker(code + SUFFIX).info
        dy: Optional[float] = info.get("dividendYield")
        if dy is None:
            return None
        pr: Optional[float] = info.get("payoutRatio")
        pbr: Optional[float] = info.get("priceToBook")
        return {
            "銘柄コード": code,
            "銘柄名": info.get("shortName", "不明"),
            "配当利回り(%)": round(dy * 100, 2),
            "配当性向(%)": round(pr * 100, 2) if pr else None,
            "PBR": round(pbr, 2) if pbr else None,
            "セクター": info.get("sector", "不明"),
        }
    except Exception:
        return None


# ==============================
# 複合フィルタ
# ==============================
def apply_filters(
    records: list[dict],
    min_yield: float,
    max_payout: float,
    max_pbr: float,
) -> pd.DataFrame:
    '複合条件でフィルタリングしDataFrameを返す'
    df: pd.DataFrame = pd.DataFrame(records)
    if df.empty:
        return df

    df = df[df["配当利回り(%)"] >= min_yield]
    df = df[(df["配当性向(%)"].isna()) | (df["配当性向(%)"] <= max_payout)]
    df = df[(df["PBR"].isna()) | (df["PBR"] <= max_pbr)]

    return df.sort_values("配当利回り(%)", ascending=False)


# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    print("複合条件スクリーニングを開始します...")
    print(f"条件: 利回り>={FILTER_MIN_YIELD}% / 配当性向<={FILTER_MAX_PAYOUT}% / PBR<={FILTER_MAX_PBR}")

    all_records: list[dict] = []
    for i, code in enumerate(TARGET_CODES, start=1):
        print(f"  [{i}/{len(TARGET_CODES)}] {code}.T")
        result: Optional[dict] = fetch_info(code)
        if result:
            all_records.append(result)
        time.sleep(REQUEST_INTERVAL)

    filtered: pd.DataFrame = apply_filters(
        all_records, FILTER_MIN_YIELD, FILTER_MAX_PAYOUT, FILTER_MAX_PBR,
    )

    if filtered.empty:
        print("条件に合致する銘柄はありませんでした。")
    else:
        print(f"n{len(filtered)}銘柄が条件に合致:")
        print(filtered.to_string(index=False))
        filtered.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
        print(f"CSVを保存しました: {OUTPUT_CSV}")

コードの処理フロー解説

上記のコードは、以下の3ステップで構成されています。

* ステップ1 全銘柄取得:対象銘柄すべての財務情報を取得し、リストに蓄積する。フィルタリング前に全データを揃えることで条件変更時の再取得を不要にする

* ステップ2 複合フィルタ:配当利回り・配当性向・PBRの3条件をAND条件で適用する。データがNullの項目はフィルタをスキップし、不必要な除外を防ぐ

* ステップ3 結果出力:条件を満たした銘柄を利回り降順で表示し、CSVに保存する

FILTER_MAX_PBR1.0に変更すれば、PBR1倍割れの割安高配当銘柄のみを抽出できます。

よくあるエラーと対処法

dividendYieldがNoneで大半の銘柄がスキップされる

yfinanceのバックエンドであるYahoo Financeが、日本株の配当情報を返さないケースがあります。特にマイナー銘柄や新興市場銘柄で発生しやすい現象です。

以下を試してください。

* yf.Ticker("7203.T").dividendsで直接配当履歴を取得し、直近1年分の合計から利回りを手動計算する

* yfinanceを最新版に更新する(pip install --upgrade yfinance

* 主要銘柄(日経225構成銘柄等)に対象を絞ることで取得率を上げる

実行に時間がかかりすぎる

120銘柄を0.8秒間隔で処理すると、最低でも約100秒かかります。対象銘柄数が多いほど実行時間は線形に増加します。

以下を試してください。

* REQUEST_INTERVAL0.5に短縮する(ただしレート制限のリスクが上がる)

* 対象リストを業種別に分割し、複数回に分けて実行する

* 一度取得した結果をCSVに保存し、2回目以降はCSVからの読み込みとフィルタ条件変更のみで済ませる

配当性向が100%を大幅に超える値が返される

特別配当や記念配当を含む年度では、配当性向が100%を超えることがあります。これはデータの異常ではなく、一時的な特殊要因です。

このような銘柄はMAX_PAYOUT_RATIOのフィルタで自動的に除外されます。ただし、特別配当を除いた「普通配当ベースの配当性向」を確認するには、決算短信の原データを別途参照してください。yfinanceの数値だけで判断するのは不十分です。

まとめ

この記事では、Pythonとyfinanceを使って高配当銘柄を自動抽出するスクリーニング手法を解説しました。

実際に使ってみた要点をまとめます。

* 配当利回りは「年間配当金÷株価×100」で算出する。3.5%以上を高配当の基準とし、8%超の異常値は株価急落を疑う

* 配当性向60%以下・PBR1.5倍以下の複合条件を加えることで、減配リスクの低い銘柄に絞り込める

* yfinanceのTicker().infoから配当利回り・配当性向・PBR等を一括取得できるが、日本株では情報がNullになるケースがあるため代替計算を用意する

* APIリクエスト間隔を0.8秒以上に設定し、レート制限を回避する

* 毎月定期実行して前月比較を行うことで、新たな高配当候補の早期発見につながる

また、スクリーニング結果に「連続増配年数」のフィルタを追加してください。yf.Ticker().dividendsから年ごとの配当金を集計し、増配が連続している年数を算出する関数を追加すれば、より信頼性の高い銘柄リストになるかと思います。

🔗 関連記事

Pythonで高配当株をスクリーニングする方法【yfinance・配当利回り自動取得】

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