Pynote

Python、機械学習、画像処理について

スクレイピング - Beautiful Soup の DOM ツリーのアクセス方法 まとめ

一覧表

elem が要素を表すオブジェクトとする。

名前 説明
elem.parent 親要素を参照する。
elem.parents 先祖要素を参照する。
elem.<element_name> (例: elem.h1) 指定した名前を持つ最初の要素を参照する。
elem.children 子要素をジェネレーターで参照する。
elem.descendants 子孫要素を参照する。
elem.previous_sibling 1つ前の兄弟要素を参照する。
elem.next_sibling 1つ後の兄弟要素を参照する。
elem.previous_siblings この要素より前のすべての兄弟要素を参照する。
elem.next_siblings この要素より後のすべての兄弟要素を参照する。
elem.string この要素の値を参照する。
elem.strings 子孫要素の値をジェネレーターで参照する。
elem.stripped_strings 子孫要素の値をジェネレーターで参照する。文字列の前後の空白は削除される。
elem.attrs この要素の属性一覧を参照する。
elem['<attribute_name>'] この要素の指定した属性の値を参照する。

ツリー構造の操作

以下の HTML をサンプルとして使用する。

sample.html

<html>
<head>
    <title>レストランメニュー</title>
</head>
<body>
    <h1>本日の<b>おすすめ</b>メニュー</h1>
    <ul class="menu">
        <li>さんまの塩焼き</li>
        <li>田舎風<i>ミネストローネ</i></li>
    </ul>
    <p>メニューは日替わりです。</p>
    <hr>
    <p>サンプル画像</p>
    <p>
        <img src="sample.png" alt="サンプル画像"></p>
    <p>お気に入りに<a href="sample.com">Web site</a>を登録してください。</p>
</body>
</html>
with open('sample.html', encoding='utf8') as f:
    html = f.read()
    # DOM ツリーを簡単にするため、行頭の空白と改行は削除する。
    html = re.sub(r'^\s+', '', html, flags = re.MULTILINE).replace('\n', '')

soup = BeautifulSoup(html, 'lxml')

HTML の DOM ツリーは以下のようになる。

root (BeautifulSoup)
└── html (Tag)
    ├── head (Tag)
    │   └── title (Tag)
    │       └── 'レストランメニュー' (NavigableString)
    └── body (Tag)
        ├── h1 (Tag)
        │   ├── '本日の' (NavigableString)
        │   ├── b (Tag)
        │   │   └── 'おすすめ' (NavigableString)
        │   └── 'メニュー' (NavigableString)
        ├── ul (Tag)
        │   ├── li (Tag)
        │   │   └── 'さんまの塩焼き' (NavigableString)
        │   └── li (Tag)
        │       ├── '田舎風' (NavigableString)
        │       └── i (Tag)
        │           └── 'ミネストローネ' (NavigableString)
        ├── p (Tag)
        │   └── 'メニューは日替わりです。' (NavigableString)
        ├── hr (Tag)
        ├── p (Tag)
        │   └── 'サンプル画像' (NavigableString)
        ├── p (Tag)
        │   └── img (Tag)
        └── p (Tag)
            ├── 'お気に入りに' (NavigableString)
            ├── a (Tag)
            │   └── 'Web site' (NavigableString)
            └── 'を登録してください。' (NavigableString)

要素の情報を表示するヘルパー関数を定義しておく。

def print_element(elem):
    if isinstance(elem, bs4.NavigableString):
        print(type(elem), elem.string)
    else:
        print(type(elem), elem.name)

親要素を参照する。

.parent で親要素を参照できる。

print_element(h1.parent)  # <class 'bs4.element.Tag'> body

# ルート要素 BeautifulSoup オブジェクトの親は None 
print(soup.parent)  # None

先祖要素を参照する。

.parents で先祖要素を参照できる。

# 先祖要素を参照する。
for anc in h1.parents:
    print_element(anc)
# <class 'bs4.element.Tag'> body
# <class 'bs4.element.Tag'> html
# <class 'bs4.BeautifulSoup'> [document]

子要素を参照する。

タグ名で子要素を参照する。

h1 = soup.html.body.h1
print_element(h1)  # <class 'bs4.element.Tag'> h1

該当する要素が複数ある場合は、最初の要素の参照になる。
例えば、body の子には、4つの p タグがあるが、その最初の p タグの参照となる。

print(soup.html.body.p)  # <p>メニューは日替わりです。</p>

子要素をジェネレーターで参照する。

h1 = soup.html.body.h1
for child in h1.children:
    print_element(child)
# <class 'bs4.element.NavigableString'> 本日の
# <class 'bs4.element.Tag'> b
# <class 'bs4.element.NavigableString'> メニュー

要素自体も子要素を取得するジェネレーターになる。

# 子要素をジェネレーターで参照する。
for sibling in soup.html.body.h1:
    print_element(sibling)
# <class 'bs4.element.NavigableString'> 本日の
# <class 'bs4.element.Tag'> b
# <class 'bs4.element.NavigableString'> メニュー

子孫要素を参照する。

.descendants で子孫要素を参照できる。

for des in h1.descendants:
    print_element(des)
# <class 'bs4.element.NavigableString'> 本日の
# <class 'bs4.element.Tag'> b
# <class 'bs4.element.NavigableString'> おすすめ
# <class 'bs4.element.NavigableString'> メニュー

兄弟要素を参照する。

.previous_sibling で1つ前の兄弟要素を参照できる。
.next_sibling で1つ後の兄弟要素を参照できる。

ul = soup.html.body.ul

# 1つ後の兄弟を取得要素を参照する。
print_element(ul.next_sibling)
# <class 'bs4.element.Tag'> p

# 1つ前の兄弟要素を参照する。
print_element(ul.previous_sibling)
# <class 'bs4.element.Tag'> h1

.previous_siblings でこの要素より前のすべての兄弟要素を参照できる。

ul = soup.html.body.ul

# これより前の兄弟要素を取得する。
for sibling in ul.previous_siblings:
    print_element(sibling)
# <class 'bs4.element.Tag'> h1

.next_siblings でこの要素より後のすべての兄弟要素を参照できる。

ul = soup.html.body.ul

# これより後の兄弟要素を取得する。
for sibling in ul.next_siblings:
    print_element(sibling)
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.Tag'> hr
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.Tag'> p

解析中に見つかった前後の要素を参照する。

HTML を解析する過程でこの要素の1つ前に見つかった要素を .previous_element、1つ後に見つかった要素を .next_element で参照できる。

# 解析中にこれより1つ後に見つかった要素を取得する。
print_element(ul.next_element)
# <class 'bs4.element.Tag'> li

# 解析中にこれより1つ前に見つかった要素を取得する。
print_element(ul.previous_element)
# <class 'bs4.element.NavigableString'> メニュー

.previous_elements で HTML を解析する過程でこの要素より前に見つかったすべての要素を参照できる。

# 解析中にこれより前に見つかった要素を順番に取得する。
for elem in ul.previous_elements:
    print_element(elem)
# <class 'bs4.element.NavigableString'> メニュー
# <class 'bs4.element.NavigableString'> おすすめ
# <class 'bs4.element.Tag'> b
# <class 'bs4.element.NavigableString'> 本日の
# <class 'bs4.element.Tag'> h1
# <class 'bs4.element.Tag'> body
# <class 'bs4.element.NavigableString'> レストランメニュー
# <class 'bs4.element.Tag'> title
# <class 'bs4.element.Tag'> head
# <class 'bs4.element.Tag'> html

.next_elements で HTML を解析する過程でこの要素より後に見つかったすべての要素を参照できる。

# 解析中にこれより後に見つかった要素を順番に取得する。
for elem in ul.next_elements:
    print_element(elem)
# <class 'bs4.element.Tag'> li
# <class 'bs4.element.NavigableString'> さんまの塩焼き
# <class 'bs4.element.Tag'> li
# <class 'bs4.element.NavigableString'> 田舎風
# <class 'bs4.element.Tag'> i
# <class 'bs4.element.NavigableString'> ミネストローネ
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.NavigableString'> メニューは日替わりです。
# <class 'bs4.element.Tag'> hr
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.NavigableString'> サンプル画像
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.Tag'> img
# <class 'bs4.element.Tag'> p
# <class 'bs4.element.NavigableString'> お気に入りに
# <class 'bs4.element.Tag'> a
# <class 'bs4.element.NavigableString'> Web site
# <class 'bs4.element.NavigableString'> を登録してください。

要素の値を参照する。

.string で要素の値を参照できる。

# タグオブジェクトが1つだけ子要素を持っていて、それが NavigableString の場合、
# NavigableString の持つ値を string で取得できる。
h1.b.string
# 'おすすめ'

h1.string
# None

.strings で子孫要素の値をジェネレーターで参照できる。

# NavigableString である子孫要素の値をジェネレーターで取得する。
for s in h1.strings:
    print(s)
# 本日の
# おすすめ
# メニュー

# NavigableString である子孫要素の値をジェネレーターで取得する。
# stripped_strings の場合、文字列の前後の空白は削除される。
for s in h1.stripped_strings:
    print(s)

属性を参照する。

.attrs で属性及びその値を dict で参照できる。
また element['name'] で name 属性の値を参照できる。

ul = soup.html.body.ul

# ul 要素の属性を参照する。
print(ul.attrs)  # {'class': ['menu']}

# ul 要素の属性 `class` の値を参照する。
print(ul['class'])  # ['menu']