Python
 Computer >> コンピューター >  >> プログラミング >> Python

PythonでHTMLページを解析してHTMLテーブルを抽出する方法

課題

Webページ上のHTMLテーブルを抽出したい——そんな場面は、データ収集やスクレイピングの現場でよくあります。本記事では、Pythonを使ってHTMLページを解析し、ページ内に埋め込まれたテーブルを取り出す方法を解説します。

はじめに

インターネット、そしてWorld Wide Web(WWW)は、現代において最も重要な情報源です。膨大な情報が存在するため、その中から目的のコンテンツを選び出すのは決して簡単ではありません。こうした情報の大部分は、HTTPを通じて取得することができます。

しかし、これらの操作はプログラムから実行することも可能です。情報の取得と処理を自動化できれば、作業を大幅に効率化できます。

Pythonには標準ライブラリとしてHTTPクライアントが用意されていますが、requestsモジュールを使えば、Webページの情報取得はさらに簡単になります。

本記事では、HTMLページを解析し、ページ内に埋め込まれたHTMLテーブルを抽出する具体的な手順を見ていきます。

使用するパッケージ

今回は以下のパッケージを使用します。未インストールの場合はpipでインストールしてください。導入済みかどうかは pip freeze で確認できます。

  • requests:HTTPリクエストの送信
  • pandas:テーブルデータのDataFrame化
  • beautifulsoup4:HTMLの解析
  • tabulate:テーブルの整形表示
import requests
import pandas as pd
from tabulate import tabulate

実装手順

STEP 1:サイトURLの設定

今回はTutorialspointのPythonチュートリアルページを対象に、ページを解析して埋め込まれているHTMLテーブルをすべて出力します。

# サイトURLを設定
site_url = "https://www.tutorialspoint.com/python/python_basic_operators.htm"

STEP 2:サーバーへリクエストを送信

# サーバーにリクエストを送信
response = requests.get(site_url)

# レスポンスを確認
print(f"*** {site_url} へのレスポンスステータスは {response.status_code}")

STEP 3:ヘッダーとコンテンツの確認

ステータスコード200は、サーバーからの応答が成功したことを意味します。続いて、リクエストヘッダー・レスポンスヘッダー、およびサーバーから返された本文の先頭100文字を確認してみましょう。

# リクエストヘッダーの確認
print(f"*** リクエストヘッダー - \n {response.request.headers} ")

# レスポンスヘッダーの確認
print(f"*** レスポンスヘッダー - \n {response.headers} ")

# 取得結果の内容を確認
print(f"*** 先頭100/{len(response.text)}文字 - \n\n {response.text[:100]} ")

出力例

*** リクエストヘッダー -
{'User-Agent': 'python-requests/2.24.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
*** レスポンスヘッダー -
{'Content-Encoding': 'gzip', 'Content-Type': 'text/html; charset=UTF-8', 'Date': 'Tue, 20 Oct 2020 09:45:18 GMT', 'Server': 'ECS (meb/A77C)', 'X-Cache': 'HIT', ...}
*** 先頭100/37624文字 -

<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Python - Basic Operators - Tutorialspoint</title>

STEP 4:BeautifulSoupでHTMLを解析

# HTMLページを解析
from bs4 import BeautifulSoup
tutorialpoints_page = BeautifulSoup(response.text, 'html.parser')

print(f"*** ページのタイトル - {tutorialpoints_page.title}")

# ページタイトルを文字列として取得することも可能
print(f"*** ページのタイトル - {tutorialpoints_page.title.string}")

STEP 5:見出しタグをもとにテーブルを特定

多くのWebページでは、テーブルの直前に h2h6 タグによる見出しが配置されています。そこで、まずこれらの見出しタグを特定し、その直後に続く <table> 要素を取得する方針で処理を行います。要素の検索には find、隣接要素の走査には find_next_siblings を使用します。

# h2〜h6の見出し要素を検索
tags = tutorialpoints_page.find(
    lambda elm: elm.name in ("h2", "h3", "h4", "h5", "h6")
)

for sibling in tags.find_next_siblings():
    if sibling.name == "table":
        my_table = sibling
        df = pd.read_html(str(my_table))
        print(tabulate(df[0], headers='keys', tablefmt='psql'))

コード全体

ここまでの手順をすべてまとめると、以下のようになります。

# STEP 1:必要なページをダウンロード
import requests
import pandas as pd

# サイトURLを設定
site_url = "https://www.tutorialspoint.com/python/python_basic_operators.htm"

# サーバーにリクエストを送信
response = requests.get(site_url)

# レスポンスを確認
print(f"*** {site_url} へのレスポンスステータスは {response.status_code}")

# リクエストヘッダーの確認
print(f"*** リクエストヘッダー - \n {response.request.headers} ")

# レスポンスヘッダーの確認
print(f"*** レスポンスヘッダー - \n {response.headers} ")

# 取得結果の内容を確認
print(f"*** 先頭100/{len(response.text)}文字 - \n\n {response.text[:100]} ")

# HTMLページを解析
from bs4 import BeautifulSoup
tutorialpoints_page = BeautifulSoup(response.text, 'html.parser')

print(f"*** ページのタイトル - {tutorialpoints_page.title}")
print(f"*** ページのタイトル - {tutorialpoints_page.title.string}")

# h2〜h6の見出しに隣接するテーブルを抽出
tags = tutorialpoints_page.find(
    lambda elm: elm.name in ("h2", "h3", "h4", "h5", "h6")
)
for sibling in tags.find_next_siblings():
    if sibling.name == "table":
        my_table = sibling
        df = pd.read_html(str(my_table))
        print(df)

実行結果

*** https://www.tutorialspoint.com/python/python_basic_operators.htm へのレスポンスステータスは 200
*** ページのタイトル - <title>Python - Basic Operators - Tutorialspoint</title>
*** ページのタイトル - Python - Basic Operators - Tutorialspoint
[<h2>Types of Operator</h2>, <h2>Python Arithmetic Operators</h2>, <h2>Python Comparison Operators</h2>, <h2>Python Assignment Operators</h2>, <h2>Python Bitwise Operators</h2>, <h2>Python Logical Operators</h2>, <h2>Python Membership Operators</h2>, <h2>Python Identity Operators</h2>, <h2>Python Operators Precedence</h2>]
[ Operator Description
0 + Addition Adds values on either side of the operator.
1 - Subtraction Subtracts right hand operand from left hand operand.
2 * Multiplication Multiplies values on either side of the operator
3 / Division Divides left hand operand by right hand operand
4 % Modulus Divides left hand operand by right hand operand
5 ** Exponent Performs exponential (power) calculation on operators
6 // Floor Division The division of operands where the result is the quotient...]

同様に、比較演算子・代入演算子・ビット演算子・論理演算子・メンバーシップ演算子・同一性演算子・演算子の優先順位に関するテーブルが、ページ内の見出しの順に従って順番に出力されます。

補足のポイント

  • pd.read_html() を利用するには、内部で使われるパーサー(lxml または html5lib)が必要です。あらかじめインストールしておきましょう。
  • サイトによっては、デフォルトのUser-Agent(python-requests/x.x)だとアクセスを拒否されることがあります。その場合は requests.get(site_url, headers={'User-Agent': 'Mozilla/5.0'}) のようにヘッダーを指定すると回避できる場合があります。
  • 見出しに依存せず、単純にページ内のすべてのテーブルを取得したい場合は、tutorialpoints_page.find_all('table') を使う方法もあります。
  1. PythonのBokehで画像を操作・表示する方法

    Pythonの可視化ライブラリBokehで画像を扱うには、image_url()メソッドを使い、画像URLのリストを渡すのが基本です。このメソッドを使うことで、ローカルやWeb上にある画像をプロット領域内に簡単に配置できます。実装の手順output_file()でデフォルトの出力先を設定し、show()を呼び出したときに結果がHTMLファイルとして保存されるようにします。figure()でプロット用の新しいFigure(図)オブジェクトを作成します。image_url()メソッドで、指定したURLから読み込んだ画像を描画します。show()を呼び出して、Bokehオブジェクトやアプリケーション

  2. PythonでAPIの結果を可視化する方法|GitHub APIとPlotlyを使った実践チュートリアル

    はじめにAPIを活用する最大のメリットの一つは、常に最新のライブデータを取得できる点です。データが頻繁に更新される場合でも、API経由なら最新の状態を確実に取得できます。APIプログラムは、特定の情報をリクエストするために専用のURLを使用します。たとえば「SpotifyやYouTube Musicで2020年に最も再生された楽曲トップ100」といった具合です。リクエストされたデータは、JSONやCSVなど、プログラムで扱いやすい形式で返却されます。Pythonを使えば、ほぼあらゆるURLに対してAPI呼び出しを記述できます。本記事では、GitHubのAPIからデータを抽出し、その結果をグラフ