Webサイトの表は、HTMLの<table>がそのまま読み込まれるページなら、pandasのread_htmlで取得できます。取得できない場合の原因は、表がJavaScriptで後から描画される、iframeの中にある、<table>ではなく<div>で組まれている、ページ内に表が複数ある、のいずれかであることが多く、原因ごとに対処が決まっています。JavaScriptやiframeが原因なら、Seleniumでページを開き、表が表示されるまで待ってからHTMLをpandasに渡します。複数サイトの表は、URLの一覧をループで処理し、encoding="utf-8-sig"でCSVにまとめるとExcelでもそのまま開けます。
本記事では、表が取れない原因の見分け方と対処、対象の表や要素を正しく選ぶ方法、複数サイトの表を1つのCSVにまとめるコードまでを解説します。 コードはすべてpandas 3.0、Selenium 4.51で動作を確認しています。
| 症状 | 主な原因 | 対処 |
|---|---|---|
ValueError: No tables found | 表がJavaScriptで後から描画される | Seleniumで表示を待ってからpandasに渡す |
ValueError: No tables found | 表が<div>で組まれている | Seleniumのfind_elementsで行ごとに取得する |
NoSuchElementException | 表がiframeの中にある | switch_to.frameで切り替えてから探す |
| 取得できたが別の表が入っている | ページ内に表が複数ある | matchやattrsで対象の表を指定する |
| 列名が「日/日/日」のように重なる | ヘッダーが多段になっている | 列名を1段にまとめる |
FileNotFoundError | pandas 3でHTML文字列をそのまま渡している | StringIOで包んでから渡す |
まずread_htmlで表が取れるか確認する
最初に試すべきなのは、pandasのread_htmlです。URLを渡すだけで、ページ内のすべての<table>がDataFrameのリストとして返ります。必要なライブラリは次のとおりです。
気象庁「過去の気象データ」の東京・2026年9月の日別値のページで試すと、次のようになります。
python
返ってくる表の数と順番はページの作りで決まり、データの表が先頭にあるとは限りません。ページのデザインが変わると順番も変わるため、dfs[0]のように番号で選ぶのではなく、次の方法で対象の表を指定します。
matchで表の中の文字を指定する
matchには、目的の表にだけ含まれる文字列を渡します。pandasの公式ドキュメントでも、単純なページ以外ではmatchを指定することが推奨されています。
python
na_values=["--"]は、データのない欄に入っている「–」を欠損値として読み込む指定です。指定しないと、その列全体が数値ではなく文字列として扱われ、集計や並べ替えができません。
attrsで表のidやclassを指定する
表にidやclassが付いている場合は、attrsで直接指定できます。ブラウザーで表の中の値を右クリックして「検証」を選ぶと、開発者ツールが開きます。選択された<td>から上の階層をたどり、<table>タグの行をクリックすると、ページ上で表全体がハイライトされ、属性を確認できます。
気象庁の日別値の表には、id="tablefix1"とclass="data2_s"が付いています。
python
なお、read_htmlは「1,200」のような桁区切りのカンマを自動で取り除き、数値として読み込みます。

*出典:気象庁ホームページ
表が取得できない・使えない6つの原因と対処
1.表がJavaScriptで後から描画されている
read_htmlはサーバーから返ってきたHTMLだけを読みます。ページを開いた後にJavaScriptで表を描画するサイトでは、その時点のHTMLに表がないためValueError: No tables foundになります。Seleniumでページを開いた直後にpage_sourceを渡しても、描画が終わる前なら同じエラーです。
表が表示されるまでWebDriverWaitで待ち、そのあとでHTMLをpandasに渡します。
python
time.sleepで固定の秒数を待つ方法は、待ち時間が足りなければ失敗し、長すぎれば処理が遅くなります。Seleniumの公式ドキュメントでも、条件を指定して待つ明示的な待機が推奨されています。待機の考え方とSeleniumの基本操作は、PythonでSeleniumを使ってスクレイピングする方法で詳しく解説しています。
2.表がiframeの中にある
iframeは、ページの中に別のページを埋め込む仕組みです。Seleniumは最初、外側のページの要素しか探せないため、iframe内の要素を探すとNoSuchElementExceptionになります。
Seleniumの公式ドキュメントにあるとおり、switch_to.frameでiframeに切り替えてから探します。
python
iframeの中にあるかどうかは、開発者ツールで確認できます。目的の要素を選択したとき、上の階層に<iframe>タグと#documentがあれば、iframeの中です。画面下部のパンくずリストにhtmlとbodyが2回出てくることでも見分けられます。

*出典:Selenium公式のテスト用ページ
3.表が<table>ではなく<div>で組まれている
見た目は表でも、HTMLが<div>や<span>の組み合わせで作られている場合があります。read_htmlは<table>タグしか探さないため、ValueError: No tables foundになります。
この場合は、Seleniumで行ごとに要素を取り出し、DataFrameに組み立てます。
python
セレクターには、行や列に付いている意味のあるclass名を使います。「css-1x2y3z」のように自動生成されたclass名は、サイトの更新で変わりやすいため避けてください。XPathで指定する方法は、XPathの書き方を参考にしてください。
4.ページ内に表が複数ある
前章のとおり、read_htmlはページ内のすべての表を返します。ナビゲーションやフッターが<table>で作られているページでは、データの表より先にそれらが入ることがあります。matchかattrsで対象を指定すれば、ページの構成が変わっても目的の表を取得できます。
5.ヘッダーが多段になっている
気象庁の日別値の表は、「降水量(mm)」の下に「合計」「最大」、その下に「1時間」「10分間」と、ヘッダーが4段に分かれています。そのまま読み込むと、列名は('降水量(mm)', '降水量(mm)', '最大', '1時間')のようなタプルになり、扱いにくくなります。
次の関数で、重複を除いて「上段_下段」の1段にまとめます。
python

*出典:気象庁ホームページのデータをもとに作成(列名を加工)
6.pandas 3でHTML文字列をそのまま渡している
以前の解説記事では、pd.read_html(driver.page_source)のようにHTMLの文字列を直接渡すコードがよく使われていました。pandas 3.0では、この書き方は文字列をファイルのパスとして扱うため、FileNotFoundErrorになります。
文字列はStringIOで包んでから渡してください。
python
対象要素を正しく選択するためのチェックリスト
取得したい表や要素が見つからないときは、開発者ツールで次の順に確認すると、原因をすばやく絞り込めます。
- 「ページのソースを表示」で、表の中の文字が見つかるか。見つからなければJavaScriptで描画されている
- 開発者ツールで、表の上の階層に
<iframe>があるか(パンくずリストにhtmlとbodyが2回出てこないか) - 表のタグが
<table>か、<div>か - 同じページに
<table>がいくつあるか、目的の表にだけ含まれる文字やidは何か - ヘッダーの行が何段あるか、空欄に「–」などの記号が入っていないか
複数サイトの表をまとめてCSVに出力する方法
対象のサイトが増えたら、URLの一覧をファイルにまとめ、ループで処理します。
Step 1:URLの一覧をCSVで用意する
検索や社内の資料から対象のページを集め、urls.csvに次の形で書き出します。「キーワード」には、目的の表にだけ含まれる文字を入れます。
Step 2:表を取得してまとめるスクリプトを実行する
次のスクリプトは、まずpandasで取得を試し、表が見つからない場合だけSeleniumに切り替えます。取得できなかったページは処理を止めずに飛ばし、errors.csvに記録します。
python
実行すると、サイトごとに取得方法と行数が表示されます。静的な表、JavaScriptで描画される表、存在しないページの3件を処理した場合の出力は次のとおりです。
Step 3:出力されたCSVを確認する
tables.csvには「取得元」と「取得日」の列が先頭に付くため、どのサイトのいつのデータかを後から確認できます。サイトごとに列が異なる場合、concatは存在しない列を空欄で埋めて1つの表にまとめます。
encoding="utf-8-sig"は、ファイルの先頭にBOMを付けてUTF-8で保存する指定です。Microsoftのサポート情報のとおり、BOM付きのUTF-8で保存したCSVは、ダブルクリックでExcelを開いても日本語が正しく表示されます。
気象庁の東京と大阪の2026年9月の日別値をurls.csvに登録して実行すると、2ページ分の60行が1つのファイルにまとまりました。「–」だった雪の列は、na_valuesの指定により空欄(欠損値)になっています。

*出典:気象庁ホームページのデータをもとに作成(列名を加工)
コードを書かずに表を取得する方法との使い分け
| 状況 | 向いている方法 | 注意点 |
|---|---|---|
| 1〜2ページの静的な表をExcelに取り込みたい | ExcelのPower Query(Webから) | JavaScriptで描画される表は取得できない |
| Googleスプレッドシートで表を参照したい | IMPORTHTML関数 | 対象は静的な<table>に限られる |
| 表の構造や取得手順を細かく制御したい | Python(pandas、Selenium) | サイトの変更に合わせてコードを保守する |
| 構造の違う多数のサイトから定期的に取得したい | Octoparse | 多段ヘッダーの表は列名を整えてから出力する |
| 対象サイトがAPIやCSVダウンロードを提供している | 公式のAPI・ダウンロード | 利用条件を確認する |
Excelでの取り込み方はExcelでWebデータを自動取得する方法、スプレッドシートでの方法はスプレッドシートでWebスクレイピングする方法で解説しています。
Octoparseで表を取得する場合
Octoparse(オクトパース・オクトパス)は、ページを内蔵ブラウザーで開き、クリック操作で取得する項目を選ぶノーコードの収集ツールです。JavaScriptで描画される表も、ブラウザーで表示された状態から取得できます。コードを保守する担当者がいない場合や、対象のサイトが多く構造がばらばらな場合に向いています。
Step 1:URLを入力して自動検出を実行する
ホーム画面の入力欄に表のあるページのURLを貼り付け、「スタート」を押します。自動検出機能がページを解析し、表の行と列を項目として提案します。
前章と同じ気象庁の日別値のページで試すと、ページ内のデータ表が選択され、1日〜30日の30行がデータプレビューに並びました。各日の「日」のリンク先URLも、列として取得されます。


Step 2:列名を整える
データプレビューには、表の値が1日〜30日の30行そろって表示されます。自動検出では、ヘッダー1段目の「気圧(hPa)」「降水量(mm)」「気温(℃)」などをもとに仮の列名が付くため、多段ヘッダーの表では、2段目以降の「現地」「海面」「合計」「最大」といった区分を列名に加えておくと、出力後のデータが読みやすくなります。
データプレビューの列名をクリックし、元の表の見出しと照らし合わせながら「気圧現地平均」「気圧海面平均」「降水量_合計」のように名前を付けていきます。Pythonのflatten_columns関数で行った処理を、画面上の操作で行うイメージです。
Step 3:実行してCSVで出力する
「ワークフローを生成」を押して実行し、終わったら「エクスポート」からCSVかExcelで保存します。エクスポートしたCSVは、ダブルクリックでExcelを開いても日本語が正しく表示されます。

Octoparseは無料プランから利用できます。プランごとの機能と最新の料金は料金ページで確認してください。
失敗しやすいポイントと運用の注意
ブラウザーとドライバーのバージョンが合わない
Seleniumを起動したときにSessionNotCreatedException: This version of ChromeDriver only supports Chrome version …と表示される場合は、Chromeとドライバーのバージョンが一致していません。Selenium 4.6以降は、Selenium Managerがインストール済みのChromeに合うドライバーを自動で用意します。古いchromedriverを手動でPATHに置いている場合は、それを削除して再実行してください。初回の起動はドライバーのダウンロードを含むため、数十秒かかることがあります。
取得した値をそのまま集計する
「–」「×」「―」のような記号や、「1,200円」のような単位付きの値が残っていると、数値として集計できません。na_valuesで記号を欠損値にし、単位は読み込み後に取り除いてから集計します。
アクセスの間隔と利用条件
- 対象サイトの利用規約と
robots.txtを確認し、自動取得を禁止しているページは対象から外す - 1ページごとに数秒の間隔を空け、必要なページだけを取得する
- ログインが必要なページや個人情報を含むページは取得しない
- 取得したデータを公開する場合は、出典の表示など、各サイトの利用条件に従う
今回例に使った気象庁のデータは、気象庁ホームページの利用規約に従い、出典を記載すれば利用できます。加工した場合は、加工したことも明記します。
よくある質問
質問1:ValueError: No tables foundと表示されるのはなぜですか?
読み込んだHTMLに<table>タグがないためです。表がJavaScriptで後から描画されているか、<div>で組まれていることが多いです。ブラウザーの「ページのソースを表示」で表の中の文字を検索し、見つからなければSeleniumで表示を待ってから取得してください。
質問2:なぜStringIOで包む必要があるのですか?
pandas 3.0のread_htmlは、渡された文字列をファイルのパスかURLとして扱います。HTMLの文字列をそのまま渡すとFileNotFoundErrorになるため、StringIOでファイルのように扱える形にしてから渡します。
質問3:chromedriverを手動でダウンロードする必要はありますか?
Selenium 4.6以降では不要です。webdriver.Chrome()を実行すると、Selenium Managerがインストール済みのChromeに合うドライバーを自動で用意します。
質問4:iframeの中の表を取得したあと、元のページの要素を操作するには?
driver.switch_to.default_content()で外側のページに戻ります。戻らずに外側の要素を探すと、NoSuchElementExceptionになります。
質問5:ヘッダーが複数行ある表はどう扱えばよいですか?
read_htmlは多段のヘッダーを複数レベルの列名として読み込みます。本文のflatten_columns関数のように、各レベルを「_」でつないで1段にまとめると、CSVやExcelで扱いやすくなります。
質問6:Seleniumは処理が遅いのですが、速くする方法は?
まずpandasのread_htmlだけで取得できないかを試し、表が見つからないページだけSeleniumを使います。本文のスクリプトもこの順で処理しています。ブラウザーはサイトごとに起動し直さず、1つを使い回してください。
まとめ
この記事では、Pythonでwebサイトの表を取得する方法と、取得できないときの対処を紹介しました。静的な<table>ならpandasのread_htmlだけで取得でき、matchやattrsで対象の表を指定すれば、ページ内に表が複数あっても目的の表を選べます。JavaScriptで描画される表はWebDriverWaitで表示を待ち、iframeの中の表はswitch_to.frameで切り替え、<div>で組まれた表はfind_elementsで行ごとに取り出します。
複数サイトの表は、URLの一覧をループで処理してutf-8-sigでCSVに出力すれば、Excelでそのまま開ける1つのファイルにまとまります。コードの保守が難しい場合や対象サイトが多い場合は、Octoparseのようなノーコードツールも選択肢になります。



