Python | Web / API:HTML の構造

Python Python
スポンサーリンク

概要(HTML の「骨組み」を理解すると、スクレイピングが一気に楽になる)

HTML はタグで構造化されたテキストです。ページは head(設定やメタ情報)と body(画面に見える内容)に分かれ、要素は親子関係で木構造を作ります。スクレイピングでは、この木構造を意識して「どの親の、どの子を、どの属性で」取るかを決めるのが核心です。まずは基本構造、タグと属性、入れ子(ネスト)とセレクタの考え方を掴み、次に表・リスト・カード・フォームなど定番レイアウトの取り方を型で覚えましょう。


HTML の基本構造(head と body、タグと属性、親子関係)

ドキュメントの枠組み(head と body)

HTML ファイルは、宣言(DOCTYPE)→ html → head → body の順に並びます。head はタイトルや文字コード、外部 CSS/JS の参照など画面には直接出ない情報。body が実際の見た目を構成します。スクレイピングで可視データを取りたいなら、基本的には body 以下を対象にします。

タグ・属性・テキストの三点セット

要素はタグ名(例 h1, p, a, div, span)、属性(class, id, href, data-* など)、中身のテキストで構成されます。抽出精度を上げたいときは、クラス名や id、特定の属性値を手掛かりにすると短いコードで狙い撃ちできます。

親子関係とネスト(木構造として考える)

HTML は入れ子の木構造です。親の中に子があり、子のさらに子が続きます。スクレイピングでは「まず親コンテナで範囲を絞る→その中の子要素を取得」という二段構えが誤抽出を減らし、速度も上がります。


セレクタと検索の考え方(タグ名・class・id・CSS セレクタ)

タグ名と class・id での絞り込み

タグ名だけだと広すぎるため、class_ や id を併用して「記事カード」「タイトル」「価格」など役割ごとに絞ります。id は基本一意なので単一要素の特定に向き、class は複数要素のグループ化に向きます。

CSS セレクタは構造指定に強い

CSS セレクタは「親子(>)」「子孫(空白)」「#id」「.class」による絞り込みが直感的です。フロント側の知識があるなら select/select_one が最短ルートです。例えば ul#news li > a.title のように書くと、ニュース一覧のタイトルリンクだけを一気に拾えます。

属性存在・値指定・部分一致の扱い

href がある a 要素の抽出は href=True の指定が速く、特定の data-* 属性や role で業務システム系の要素を正確に特定できます。部分一致は最終的に Python 側で文字列条件をかけると安全です。


定番レイアウトの取り方(表・リスト・カード・フォーム)

表(table)の構造と抽出

表は table の中に thead(ヘッダ)と tbody(本体)があり、行 tr、セル th/td が入ります。実務では thead を除外して tbody の tr を順に読み、td のテキストを整形して二次利用(CSV/JSON)に回します。直下要素だけを取りたいときは recursive=False で制御すると崩れにくくなります。

箇条書き(ul/ol)と一覧の抽出

一覧は ul/ol の中に li がぶら下がります。各 li の中に a.title や span.date がセットで入っていることが多く、「親の li を並べて、子要素の特定パーツを取る」流れにすると誤抽出が激減します。

カード型レイアウト(div.item/article)

最近のサイトはカード型レイアウトが主流です。div.item や article を親として、その中の h3(見出し)、a[href](リンク)、span.price(価格)、img[src](画像)などをひとまとめに抽出します。親を find_all→子を find の二段構えが基礎です。

フォーム要素(input/select/textarea)

フォームはユーザー入力用ですが、プレースホルダや初期値が意味を持つ場合があります。name、value、placeholder、selected/checked などの属性から必要な情報を拾えます。POST 先の action、method は連携時の手掛かりです。


Python 実例(requests + BeautifulSoup で構造に沿って抜く)

基本の取得と解析

# pip install requests beautifulsoup4 lxml
import requests
from bs4 import BeautifulSoup

url = "https://httpbin.org/html"
resp = requests.get(url, timeout=5)
resp.raise_for_status()
resp.encoding = resp.apparent_encoding  # 文字化け対策(推定で上書き)

soup = BeautifulSoup(resp.text, "lxml")
title = soup.title.get_text(strip=True)
print("タイトル:", title)
Python

リスト構造からタイトルとリンクを抽出

from bs4 import BeautifulSoup

html = """
<ul id="news">
  <li><a class="title" href="/a">A News</a><span class="date">2025-12-01</span></li>
  <li><a class="title" href="/b">B News</a><span class="date">2025-12-02</span></li>
</ul>
"""
soup = BeautifulSoup(html, "lxml")
items = soup.find("ul", id="news").find_all("li", recursive=False)

records = []
for it in items:
    a = it.find("a", class_="title")
    date = it.find("span", class_="date")
    records.append({
        "title": a.get_text(strip=True) if a else "",
        "href": a.get("href", "") if a else "",
        "date": date.get_text(strip=True) if date else ""
    })
print(records)
Python

テーブルの行を整形して CSV 向けに

import csv
from bs4 import BeautifulSoup

html = """
<table class="data">
  <tr><th>名前</th><th>点数</th></tr>
  <tr><td>A</td><td>90</td></tr>
  <tr><td>B</td><td>80</td></tr>
</table>
"""
soup = BeautifulSoup(html, "lxml")
rows = soup.find("table", class_="data").find_all("tr", recursive=False)[1:]

data = [[td.get_text(strip=True) for td in r.find_all("td", recursive=False)] for r in rows]
with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    csv.writer(f).writerows(data)
print("書き出し件数:", len(data))
Python

CSS セレクタでカード型を一気に拾う

from bs4 import BeautifulSoup

html = """
<section id="products">
  <div class="item"><h3>Alpha</h3><span class="price">¥1,200</span></div>
  <div class="item"><h3>Beta</h3><span class="price">¥980</span></div>
</section>
"""
soup = BeautifulSoup(html, "lxml")
for card in soup.select("section#products div.item"):
    name = card.select_one("h3").get_text(strip=True)
    price = card.select_one("span.price").get_text(strip=True)
    print(name, price)
Python

よくある落とし穴と回避策(文字化け・誤抽出・動的生成)

文字化けを防ぐ

HTTP ヘッダーや meta charset が適切でないサイトでは、resp.encoding を apparent_encoding で推定上書きすると読めるようになります。JSON や CSV の保存時は UTF-8 を明示して整形すると後工程が安定します。

誤抽出を減らす

ページ全体からいきなり検索すると、似た構造の別ブロックを拾いがちです。先に親コンテナ(section や ul、table)でスコープを絞り、その中で子要素を探す流れにすると精度が上がります。属性は .get(“attr”, “”) のように安全に取り出し、None チェックを癖にすると落ちません。

JavaScript で動的生成されるページ

BeautifulSoup は取得時点の静的 HTML しか見えません。表示後に JS で差し込まれた要素は見えないので、ネットワークタブで裏の JSON API を探す、あるいは必要に応じて自動ブラウザ(Selenium/Playwright)を使う選択が必要です。


まとめ

HTML の構造は「タグ・属性・テキスト」と「親子の木構造」です。まず head と body を区別し、狙う領域の親コンテナでスコープを絞り、class・id・属性で精度を上げ、CSS セレクタで構造を短く表現する。表・リスト・カード・フォームなど定番レイアウトは、それぞれの骨組みに沿って取り出す型を作る。文字化けや誤抽出への備えを入れ、動的生成は API 連携や自動ブラウザも視野に。これらを体に入れれば、初心者でも短いコードで「必要な情報だけを正確に抜く」スクレイピングが安定して書けます。

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