概要(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))
PythonCSS セレクタでカード型を一気に拾う
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 連携や自動ブラウザも視野に。これらを体に入れれば、初心者でも短いコードで「必要な情報だけを正確に抜く」スクレイピングが安定して書けます。
