INVESTMENT 番外編

yfinanceでセクター情報を取得する(sector・industry)

Python経験者向けの投資分析シリーズ番外編。yfinance で銘柄のセクター(業種)を取得する方法をまとめます。info["sector"]ETFで KeyError になる理由、返ってくる分類名がGICSではないという事実、そしてETFの中身を比率で分解する正攻法まで、実行結果つきで扱います。

🎯 対象: Python経験者・投資分析 📦 yfinance 1.4.1 / pandas 3.0.3 🧪 2026-08-31 に実行して確認 ⏱️ 読了: 約14分

1. この記事のゴール

本記事は「Python経験者が、自分の投資ポートフォリオを管理・可視化するアプリを作る」連載の番外編です。連載第7回Matplotlibで円グラフを作る(ポートフォリオのセクター分散)で、保有銘柄をセクター別に集計した円グラフを描きました。その手前にある「そもそもセクター情報をどう取るのか」だけを、この記事に切り出しています。

切り出した理由は、ここが思った以上に落とし穴だらけだからです。Ticker.info["sector"] という1行で済みそうに見えて、実際には銘柄によっては例外で落ち、返ってくる分類名は多くの人が想定するものと違い、ETFでは別のAPIが必要になります。円グラフの描き方と混ぜて書くと、どちらも中途半端になります。

🎯
この記事のゴール

ティッカーを渡すと、個別株ならセクター名を、ETFなら中身のセクター比率を返す関数を書けるようになること。どちらでもない銘柄が来ても例外で止まらない形にします。完成コードは7章にあります。

🧪
この記事の検証環境

本文のコードと出力は、Windows 11 / Python 3.12.10 / yfinance 1.4.1 / pandas 3.0.3 の環境で 2026年8月31日に実際に実行して確認したものです。登場する銘柄コードは実在のものですが、掲載しているのは取得した時点の実行例であり、推奨銘柄ではありません。

執筆時点の yfinance の最新版は 1.7.0 で、検証環境の 1.4.1 とは差があります。info の中身はバージョンと時点で変わりますので、手元の結果が本記事と違っていても不思議ではありません。

2. info["sector"] でセクターを取得する

yfinance では Ticker.info という辞書に、社名・時価総額・指標などがまとめて入っています。セクターもそのひとつです。トヨタ自動車(7203.T)で確認します。

import yfinance as yf

t = yf.Ticker("7203.T")
info = t.info

print(len(info), "キー")
print(info["sector"], "/", info["industry"])
171 キー
Consumer Cyclical / Auto Manufacturers

英語で返ってきます。日本の証券会社の画面で見慣れた「輸送用機器」ではありません。日本語で表示したいなら、自分で対応表を持つことになります(4章で作ります)。

セクターに関係するキーは、実測した範囲では4つありました。

for k in ["sector", "sectorKey", "industry", "industryKey"]:
    print(f"{k:<12} {info.get(k)!r}")
sector       'Consumer Cyclical'
sectorKey    'consumer-cyclical'
industry     'Auto Manufacturers'
industryKey  'auto-manufacturers'
キー中身使いどころ
sector大分類(11種類)集計・グラフのカテゴリ。本記事の主役
industry細かい業種同じセクター内の内訳を見たいとき
sectorKey / industryKey小文字ハイフン形式の識別子表示用ではなく識別用。表記ゆれに強い

sectorKey が実在するのは覚えておく価値があります。表示名は表記が揺れうるのに対し、キーのほうは機械処理向けの安定した形だからです。辞書のキーとして使うなら、sectorKey を採用したほうが安全な場面があります。

なお Ticker.info の公式ドキュメントは「プロパティで dict を返す」以上の説明がなく、含まれるキーの一覧は公開されていません。本記事の内容はすべて、インストール済みパッケージのソースと実行結果に基づいています。Ticker から何が取れるのかを広く知りたい場合は、第1回yfinanceで株価データを取得するに取得できるデータのカテゴリ別一覧があります(info のキー一覧そのものではありません)。

3. 失敗実演:欠損は None ではなく KeyError

同じ書き方をETF(1306.T・TOPIX連動)に対して実行します。

# ⚠️ 失敗例:ETFにはセクター情報が無く、キー自体が存在しない
etf = yf.Ticker("1306.T")
print(etf.info["sector"])
KeyError: 'sector'

ここが最初のつまずきどころです。「無いなら None が入っているはず」と考えて if info["sector"] is None: と書くと、その判定に到達する前に例外で落ちます

理由はライブラリの実装にあります。yfinance は info を組み立てるとき、値が None のキーを辞書に入れずに捨てています。つまり値の無い項目は「空で存在する」のではなく「存在しない」わけです。

einfo = etf.info
print("キー数:", len(einfo))
print("sector:", repr(einfo.get("sector")))
print("quoteType:", repr(einfo.get("quoteType")))
print("'sector' in info:", "sector" in einfo)
キー数: 91
sector: None
quoteType: 'ETF'
'sector' in info: False

対処は info.get("sector") で受けるだけです(KeyError そのものの読み方はPythonのよくあるエラーと対処法にまとめてあります)。あわせて2つの事実が読み取れます。

1つめ。個別株が171キー返すのに対し、このETFは91キーしかありません。第1回(2026年8月11日の実行)では同じETFが88キー、7203.T も168キーでした。3週間で個別株・ETFとも数キー増えています。キー数は時点やバージョンで変わるので、「n個ある前提」のコードを書かないでください。

2つめ。quoteType を見れば 'EQUITY''ETF' かが判別できます。取得ロジックを分岐させる材料になるので、7章の関数ではこれを使います。

⚠️
DataFrame に入れると None は NaN に変わる

.get() が返した None を辞書のリストに詰めて pd.DataFrame() に渡すと、その欄は NaN になります。「None のつもりで is None 判定を書いたのに引っかからない」という混乱はここから来ます。DataFrame にしたあとは isna()fillna() で扱ってください。連載第7回では、この NaN を放置したまま集計して合計が静かに狂うという事故を実演しています。

4. 返ってくる分類はGICSではない

ここが、この記事でいちばん伝えたい点です。「セクター分類といえばGICS(世界産業分類基準)」という知識でコードを書くと、辞書が一致せず全部その他行きになります

yfinance が返しうるセクター名は、インストール済みパッケージのソース(yfinance/const.py)に集合として直接書かれています。

import yfinance.const as C

print(sorted(C.SECTOR_INDUSTY_MAPPING.keys()))   # ※ typo も含めてこの名前です
['Basic Materials', 'Communication Services', 'Consumer Cyclical', 'Consumer Defensive',
 'Energy', 'Financial Services', 'Healthcare', 'Industrials', 'Real Estate',
 'Technology', 'Utilities']

11個です。GICSも11セクターなので数は同じですが、名前は半分ほど違います

GICS(MSCI・S&P Dow Jones Indices)yfinance が返す値一致
EnergyEnergy同じ
MaterialsBasic Materials違う
IndustrialsIndustrials同じ
Consumer DiscretionaryConsumer Cyclical違う
Consumer StaplesConsumer Defensive違う
Health CareHealthcare(スペース無し)違う
FinancialsFinancial Services違う
Information TechnologyTechnology違う
Communication ServicesCommunication Services同じ
UtilitiesUtilities同じ
Real EstateReal Estate同じ

左列のGICS名は一般に流通している英語表記を当サイトで整理したもので、MSCI公式ページの逐語確認までは行っていません。またこの表は「11個が1対1で対応する」という意味ではありません。GICSと、yfinance が返す名称が一致するMorningstar系の分類は、それぞれ独立した体系です。下位の業種の切り方も件数も違うため、名前が似ているものを機械的に読み替えるのは危険です。GICSの定義そのものはMSCIのGICS解説ページが一次情報になります。

ソースの表記と、実際の返り値も違う

もう1つ、手を動かして分かった細かい罠があります。const.py に書かれている業種名の区切り文字と、APIが実際に返す文字が違います。

print(repr(C.SECTOR_INDUSTY_MAPPING["Financial Services"] & {"Banks—Diversified"}))  # ソース側
print(repr(yf.Ticker("8306.T").info["industry"]))                                    # 実際の返り値
{'Banks—Diversified'}
'Banks - Diversified'

ソース側はemダッシュ(—)でつないだ Banks—Diversified、実際に返ってきたのは半角ハイフンとスペースの Banks - Diversified でした。const.py の集合を「許可リスト」にして文字列一致で照合すると、正しい値なのに弾かれます。判定に使うなら実際の返り値を基準にしてください。

日本語の対応表を作る

グラフや表のラベルを日本語にしたいなら、yfinance が返す11個に対して辞書を用意します。GICS用の対応表を持ってきても値が一致しないので、必ずこちらの11個で作ります。

SECTOR_JA = {
    "Basic Materials": "素材",
    "Communication Services": "通信サービス",
    "Consumer Cyclical": "一般消費財",
    "Consumer Defensive": "生活必需品",
    "Energy": "エネルギー",
    "Financial Services": "金融",
    "Healthcare": "ヘルスケア",
    "Industrials": "資本財・産業",
    "Real Estate": "不動産",
    "Technology": "テクノロジー",
    "Utilities": "公益",
}

この日本語は当サイトが表示用に決めた訳であって、分類の提供元が公表している公式の日本語名ではありません。人に見せる資料に使うなら、英語のまま出すか、訳語の出どころを添えるほうが安全です。

11個すべてを辞書に入れておくのがポイントです。自分の保有銘柄に出てこないセクターも、ETFを分解すると出てきます(6章)。手持ちの銘柄だけ見て辞書を作ると、あとで穴が空きます。

5. 東証の業種分類は、また別の体系

日本株を扱っていると、ここで違和感が出ます。証券会社の画面に出る「輸送用機器」「電気機器」といった業種名が、yfinance からは一切返ってこないからです。

これらは東京証券取引所で使われている業種別分類(いわゆる「東証33業種」)のもので、今回扱っている分類とは別の体系です。info に東証の業種を示すキーはありません。実際、トヨタは東証の分類では輸送用機器の側に入りますが、yfinance が返すのは Consumer Cyclical(一般消費財)です。自動車メーカーを「消費財」と捉えるか「輸送用機器」と捉えるかという、分類思想そのものの違いだと考えると腑に落ちます。

整理すると、混同しやすい体系が3つあります。

体系言語・粒度yfinanceから取れるか
yfinance が返すセクター英語・11分類取れるinfo["sector"]
GICS英語・11セクター取れない(名前が似ているだけの別体系)
東証の業種別分類日本語取れない

当サイトでは東証側の分類について一次情報を確認できていませんので、正確な定義や分類数はJPXの公式ページでご確認ください。日本の業種名で集計したいなら、銘柄コードと業種の対応表を自分で持つのが結局いちばん確実です。保有銘柄が数十件なら、CSVを1枚作って merge するだけで済みます。

📜
yfinanceの利用条件を確認してから使う

yfinance はPyPIの説明で「Yahoo, Inc. とは無関係のオープンソースであり、調査・教育目的を意図している」「Yahoo! finance の API は個人利用のみを意図している」と明示しています。業務利用や再配布を考えているなら、必ず自分で最新の利用条件を確認してください。取得元の規約をコードより先に確認するという考え方は、第2回Pythonで株価をスクレイピングする前にで扱っています。

6. ETFの中身を分解する(sector_weightings)

ETFにセクターが無いのは、ETF自体が「器」だからです。中身は複数の銘柄で、ひとつの業種には収まりません。ただし中身の比率は、別のAPIから取れます。

fd = yf.Ticker("1306.T").funds_data
print("quote_type():", fd.quote_type())      # ← quote_type だけメソッドです

weights = fd.sector_weightings
for k, v in weights.items():
    print(f"  {k:<22} {v}")
print("合計:", sum(weights.values()))
quote_type(): ETF
  realestate             0.0188
  consumer_cyclical      0.1265
  basic_materials        0.043
  consumer_defensive     0.0456
  technology             0.1702
  communication_services 0.0775
  financial_services     0.1867
  utilities              0.0123000005
  industrials            0.2546
  energy                 0.0089
  healthcare             0.0558
合計: 0.9999000004999999

中身の構成比が取れました。この短い出力に、踏みやすい罠が3つ詰まっています。

罠1:quote_type だけがメソッド

funds_data のメンバーは sector_weightingstop_holdingsasset_classes などプロパティで統一されているのですが、quote_type だけメソッドです。カッコを忘れると、こうなります。

>>> fd.quote_type
<bound method FundsData.quote_type of <yfinance.scrapers.funds.FundsData object at 0x...>>

メソッドオブジェクトがそのまま返るため、if fd.quote_type == "ETF":エラーにならず、永遠に偽になります。静かに間違うタイプのバグです。

罠2:キーの表記が info["sector"] と別物

片方は Financial Services、もう片方は financial_services です。同じ集計に混ぜるなら正規化が要ります。ところが素直な変換だと1件だけ失敗します。

# ⚠️ 失敗例:11件中10件は通るが、1件だけ一致しない
for k in weights:
    print(f"{k:<22} -> {k.replace('_', ' ').title()}")
realestate             -> Realestate          ← Real Estate にならない
consumer_cyclical      -> Consumer Cyclical
basic_materials        -> Basic Materials
(以下略)

realestate だけアンダースコアが入っていません。real_estate だと思い込んで変換すると、不動産だけ別カテゴリとして分裂します。しかも10件は成功するので、テストデータに不動産が無ければ気づけません。11件しかないのだから、推測変換ではなく対応表を直書きするのが安全です。

SECTOR_KEY_TO_NAME = {
    "basic_materials": "Basic Materials", "communication_services": "Communication Services",
    "consumer_cyclical": "Consumer Cyclical", "consumer_defensive": "Consumer Defensive",
    "energy": "Energy", "financial_services": "Financial Services",
    "healthcare": "Healthcare", "industrials": "Industrials",
    "realestate": "Real Estate", "technology": "Technology", "utilities": "Utilities",
}

罠3:比率の合計が 1.0 にならない

合計は 0.9999 でした。ETFの評価額に掛け戻して金額に直すと、元の金額よりわずかに小さくなります。たとえば216,750円のETFを分解すると、合計は216,728円です。

個人の資産管理では無視できる差ですが、「なぜ合計が1円単位で合わないのか」を知らないまま検算すると、原因不明の不一致として時間を溶かします。合計を厳密に保ちたいなら、比率を合計で割って正規化してから掛けてください。

total_w = sum(weights.values())
normalized = {k: v / total_w for k, v in weights.items()}   # 合計を 1.0 に揃える

個別株に使うと例外になる

funds_data はファンド向けのAPIなので、個別株に対して呼ぶと落ちます。

yf.Ticker("7203.T").funds_data.sector_weightings
YFDataException: 7203.T: No Fund data found.

したがって「セクターが取れなかったら無条件に funds_data を見る」という書き方は危険です。次章では、この例外も含めて安全に分岐させます。

7. 銘柄タイプで分岐する取得関数にまとめる

ここまでの判断を1枚に整理すると、こうなります。

これをそのままコードにします。

"""銘柄のセクター情報を取得する。
検証環境: Python 3.12.10 / yfinance 1.4.1(2026-08-31 実行)
"""
import yfinance as yf

SECTOR_KEY_TO_NAME = {
    "basic_materials": "Basic Materials", "communication_services": "Communication Services",
    "consumer_cyclical": "Consumer Cyclical", "consumer_defensive": "Consumer Defensive",
    "energy": "Energy", "financial_services": "Financial Services",
    "healthcare": "Healthcare", "industrials": "Industrials",
    "realestate": "Real Estate", "technology": "Technology", "utilities": "Utilities",
}


def fetch_sector_info(ticker):
    """銘柄のセクター情報を取得する。

    戻り値の dict:
      quote_type ... 'EQUITY' / 'ETF' など
      sector     ... 個別株なら分類名、取れなければ None
      weights    ... ETF等なら {セクター名: 比率}、無ければ None
    """
    info = yf.Ticker(ticker).info
    quote_type = info.get("quoteType")
    sector = info.get("sector")          # 無いキーなので [] ではなく get で受ける

    weights = None
    if sector is None:
        try:
            raw = yf.Ticker(ticker).funds_data.sector_weightings
            weights = {SECTOR_KEY_TO_NAME.get(k, k): v for k, v in raw.items()}
        except Exception as e:           # 個別株に使うと YFDataException
            print(f"  ({ticker}: {type(e).__name__} → 手動マッピングに回す)")

    return {"ticker": ticker, "quote_type": quote_type,
            "sector": sector, "weights": weights}


for t in ["7203.T", "1306.T"]:
    r = fetch_sector_info(t)
    print(f"{r['ticker']}  quote_type={r['quote_type']}  sector={r['sector']}")
    if r["weights"]:
        top = sorted(r["weights"].items(), key=lambda kv: kv[1], reverse=True)[:3]
        print("  weights 上位3:", [(k, round(v, 4)) for k, v in top])
        print("  weights 件数:", len(r["weights"]), "/合計:", round(sum(r["weights"].values()), 6))
7203.T  quote_type=EQUITY  sector=Consumer Cyclical
1306.T  quote_type=ETF  sector=None
  weights 上位3: [('Industrials', 0.2546), ('Financial Services', 0.1867), ('Technology', 0.1702)]
  weights 件数: 11 /合計: 0.9999

設計上のポイントは3つです。

1つめ、判定に quote_type ではなく sector の有無を使っています。quoteType の値は 'EQUITY''ETF''MUTUALFUND' など複数あり、将来増える可能性もあります。「セクターが取れたかどうか」で分けるほうが、値の増減に強い設計です。

2つめ、funds_datatry で囲んでいます。セクターが無い銘柄がすべてETFとは限りません(現金や分類外の銘柄もあります)。例外を握って3つめの出口に流すことで、1銘柄の失敗が全体の処理を止めません

3つめ、戻り値の形を1つに統一しています。個別株でもETFでも同じキーの dict が返るので、呼び出し側は分岐を意識せずリスト内包表記で回せます。この形にしておくと、そのまま pd.DataFrame() に渡せます——連載第7回の円グラフは、この DataFrame を材料にしています。

📖
次に読むなら:戻り値と例外の設計を体系立てて

この章でやったこと——例外を握って3つ目の出口に流す分岐しても戻り値の形を1つに揃える——は、このAPIに固有の話ではなく、どのコードにも効く設計の型です。型として身につけると、次に別のライブラリで同じ壁に当たったときも自分で同じ判断ができます。Effective Python 第3版はその判断基準を125項目にまとめた定番で、当サイトのおすすめ本ランキングでは総合4位として紹介しています。リンク先では対象レベル・全572ページというボリューム・出版社の公式ページを確認でき、そこから楽天の価格・在庫ページへ進めます。なお、リンク先は当サイトの書籍紹介ページで、アフィリエイト広告を含みますのでご了承ください。

🥧
次に読むなら:取れたセクターをグラフにする

セクター情報が取れたら、次は「自分の資産がどう配分されているか」を1枚の図にする段階です。連載第7回Matplotlibで円グラフを作る(ポートフォリオのセクター分散)では、この記事で作った情報を評価額と突き合わせ、ドーナツグラフにするところまでを扱います。3章で触れた NaN を放置すると、円グラフの合計が静かに狂う——その実演も第7回にあります。連載の全体像と公開済みの記事は投資×Python シリーズ一覧から辿れ、更新は新着記事のRSSでも配信しています。

📌
この連載のスタンス

本連載は「自分の資産を自分で管理・可視化するツールを、Pythonの学習題材として自作する」ことが目的です。特定銘柄の売買や投資手法を勧めるものではなく、登場する銘柄はデータ取得の実行例として挙げているに過ぎません。当サイトは投資助言業(金融商品取引業)の登録を行っておりません。セクター分類の解釈や個別の投資判断についてのご質問にはお答えできません。投資判断はご自身の責任で行ってください(詳細は免責事項をご覧ください)。

8. よくある質問(FAQ)

Q. info["sector"] が KeyError になります。

その銘柄にセクター情報が無いためです。yfinance は値が None のキーを info に入れないので、無い項目は空ではなく不在になります。info.get("sector") で受ければ None が返り、例外にはなりません。ETF・投資信託・指数などで起きやすく、実測ではETF(1306.T)の info は91キーで 'sector' in infoFalse でした(個別株は171キー)。

Q. セクター名が英語で返ります。日本語にできますか?

yfinance 側に日本語の分類名は無いため、自分で対応表を作ります。返りうる値は11個だけなので、辞書1つで済みます(4章に一覧があります)。注意点として、GICS用の対応表を流用すると値が一致しませんConsumer Discretionary ではなく Consumer CyclicalFinancials ではなく Financial Services が返ってきます。

Q. 「電気機器」「輸送用機器」のような東証の業種名は取れますか?

取れません。東京証券取引所で使われている業種別分類はyfinanceが返す分類とは別の体系で、info に該当するキーがありません。トヨタは東証の分類では輸送用機器の側ですが、yfinance が返すのは Consumer Cyclical です。日本の業種名で集計したいなら、銘柄コードと業種の対応表を自分で用意して結合してください。分類の定義はJPXの公式ページが一次情報です(当サイトでは逐語の確認ができていません)。

Q. 日本株でも sector は取得できますか?

検証した銘柄では取得できました。2026年8月31日に実行した範囲では、7203.T が Consumer Cyclical、8306.T が Financial Services、9432.T が Communication Services のように返っています。ただしすべての日本株で取れると確認したわけではありません。銘柄によっては欠ける場合があるので、.get() で受けて欠損に備える設計にしてください。

Q. sectorKeysector はどう違いますか?

sector が表示用の名前(Consumer Cyclical)、sectorKey が識別用の小文字ハイフン形式(consumer-cyclical)です。実測では industry / industryKey も同様のペアで存在しました。表示名は表記が揺れうる(本文4章のハイフンの例)ため、辞書のキーや条件分岐に使うなら sectorKey のほうが安定します。ただし公式ドキュメントに info のキー一覧は無いので、使う前に自分の環境で存在を確認してください

Q. ETFのセクター比率はどうやって取りますか?

Ticker("1306.T").funds_data.sector_weightings で、セクターをキーにした比率の辞書が返ります。実測では11件、値は小数(比率)でした。3点だけ注意してください。キーは financial_services のような小文字表記info["sector"] とは別物、realestate だけアンダースコアが無い、そして合計が 1.0 ではなく 0.9999 です。また同じオブジェクトの quote_typeプロパティではなくメソッドなので、カッコを付けないと比較が常に偽になります。

Q. 個別株に funds_data を使うとどうなりますか?

YFDataException: 7203.T: No Fund data found. という例外になります。「セクターが取れなければ funds_data を見る」と無条件に書くと、分類が付いていないだけの個別株で処理が止まります。try で囲んで、取れなかったときは自分の対応表に回すのが安全です(7章の関数がその形です)。

Q. 投資信託(非上場)のセクターは取得できますか?

日本の非上場の投資信託はyfinanceの対象外であることがほとんどです。市場で値が付く銘柄(株・ETF・為替・指数など)が対象という前提があるためで、この点は第1回yfinanceで株価データを取得するでも触れています。本連載では投信は手入力で割り切り、自動取得はETFに寄せる方針を採っています。保有本数が多くないなら、「投資信託」という自分用のカテゴリにまとめてしまうのが現実的です。

本記事は、生成AIを活用して下書きし、運営者が内容を確認・編集したうえで公開しています。掲載しているコードと出力は Python 3.12.10 / yfinance 1.4.1 / pandas 3.0.3 で実際に実行して確認したものですが、取得結果は2026年8月31日時点の実行例であり、ライブラリのバージョンやデータ提供元の都合で変わることがあります。登場する銘柄コードは実在のものですが、特定の銘柄の売買・投資手法を推奨するものではありません。投資判断はご自身の責任で行ってください。AIの利用方針は免責事項をご覧ください。