Python Tips: BeatifulSoup で HTML を加工したい

Python のライブラリ BeautifulSoup を使って HTML を加工したいときの小ネタ集です。

以下すべてのサンプルコードでこの↓ import 文が省略されています。

from bs4 import BeautifulSoup

目次

  • タグの属性を追加する
  • タグの属性を削除する
  • タグの中身を削除する
  • 中身を残してタグを削除する
  • タグとその中身を削除する
  • タグの種類を変更する
  • タグを特定の文字列に置き換える
  • 空白文字だけのテキスト要素を削除する

タグの属性を追加する

属性を追加するには tag.attrs に要素を追加します。 tag.attrs はシンプルな dict です。

soup = BeautifulSoup('', 'html.parser')

tag = soup.new_tag('p')
print(tag)
# => <p></p>

tag.attrs['class'] = 'mx4'
print(tag)
# => <p class="mx4"></p>

タグの属性を削除する

特定の属性を削除するには tag.attrs[name] に対して del を使います。

del tag.attrs['style']

すべての属性を削除するには tag.attrs.clear() を使います。

tag.attrs.clear()

タグの中身を削除する

タグを残してその子要素だけ削除したい場合は tag.clear() を使います。

soup = BeautifulSoup('', 'html.parser')

a_tag = soup.new_tag('a')
a_tag.attrs['href'] = '/sample'
a_tag.string = 'Click here'
a_tag
# => <a href="/sample">Click here</a>

a_tag.clear()
print(a_tag)
# => <a href="/sample"></a>

中身を残してタグを削除する

逆に、タグの中身を残してタグだけ削除したい場合は tag.unwrap() を使います。

soup = BeautifulSoup('', 'html.parser')

# 子に `p` タグを持った `div` タグをツリーに追加する
div_tag = soup.new_tag('div')
p_tag = soup.new_tag('p')
p_tag.string = 'Hello'
div_tag.append(p_tag)
soup.append(div_tag)

print(div_tag)
# => <div><p>Hello</p></div>

# `div` タグを中身を残して削除する
div_tag.unwrap()
print(soup)
# => <p>Hello</p>

タグとその中身を削除する

タグをその子要素も含めてツリーから削除したい場合は tag.extract() を使います。

tag.extract()

タグの種類を変更する

タグの種類をするには wrap()unwrap() を使います。

def change_tag(soup, tag, new_tag_name: str, copy_attrs: bool = False):
    """タグを別のタグに置き換える"""
    new_tag = soup.new_tag(new_tag_name)
    new_tag.attrs = tag.attrs.copy()
    tag.wrap(new_tag)
    tag.unwrap()
    return new_tag

この change_tag() は次のように利用できます。

soup = BeautifulSoup('', 'html.parser')

# `p` タグをツリーに追加する
p_tag = soup.new_tag('p')
p_tag.string = 'Hello'
soup.append(p_tag)

# `p` タグを `div` タグに変換する
new_tag = change_tag(soup, p_tag, 'div')
print(new_tag)
# => '<div>Hello</div>'

もしタグの属性を持ち越したい場合は上の copy_attrsTrue のときの処理を行います。

タグを特定の文字列に置き換える

タグの特定の文字列に置き換えて削除するには tag.insert() tag.append() tag.unwrap() あたりを駆使します。

たとえば <pre> タグを Markdown のコードブロックに変換するには次のようにします。

def change_pre_to_markdown_codeblock(pre_tag):
    pre_tag.insert(0, '\n\n```')
    pre_tag.append('```\n\n')
    pre_tag.unwrap()

この change_pre_to_markdown_codeblock() は次のようして使います。

soup = BeautifulSoup('', 'html.parser')

pre_tag = soup.new_tag('pre')
pre_tag.string = '\nprint("Hello")\n'
soup.append(pre_tag)
print(soup)
# => <pre>
# => print("Hello")
# => </pre>

change_pre_to_markdown_codeblock(pre_tag)
print(soup)
# =>
# => ```
# => print("Hello")
# => ```
# =>

空白文字だけのテキスト要素を削除する

&nbsp; 等の空白文字だけからなるテキスト要素を削除するには replace_with() を使います( WYSIWYG 等を使って書いた HTML の中にこのような要素が混入することがあります)。

import re

for text_element in soup.find_all(string=re.compile(r'^\s+$')):
    text_element.replace_with('')

参考