Python

【Python】lxmlライブラリを使ったHTML解析

この記事では、Python で lxmlライブラリ を使って HTML を解析する方法を解説します。

lxmlとは?

lxml とは、XML と HTML を処理するためのライブラリです。

Linklxml - Processing XML and HTML with Python

標準ライブラリではないので pip を使ってインストールする必要があります。

ターミナル、またはコマンドプロンプト

pip install lxml

Linkpipを使ってパッケージ管理する方法

lxml をインストールしたら次に使い方を見ていきましょう!

HTML文字列の読み込み

HTML文字列 を読み込むには、lxml.htmlfromstring() を使います。

import lxml.html

lxml.html.fromstring(html文字列)
【Python】任意のサイトのHTMLを文字列で取得する方法この記事では、Pythonで任意のURLからそのサイトのHTMLを文字列型で取得する方法を解説します。HTMLを文字列で取得することで ...

今回はわかりやすいように複数行文字列を簡単な HTML として読み込んでみます。

import lxml.html

# HTMLデータ
html = '''<!DOCTYPE html>
<html lang="ja">
 <head>
 <meta charset="utf-8">
 <title>サイトタイトル</title>
 </head>
 <body>

 <h1>タイトル</h1>

 <h2>見出し1</h2>
 <p>コンテンツの内容</p>

 </body>
</html>'''

root = lxml.html.fromstring(html)
print(root)

実行結果

<Element html at 0x10678ccc0>

root には、タグの情報がツリー構造で格納されています。

タグの取得

それでは、タグを取得する方法を見ていきましょう!

headタグとbodyタグ

head属性からheadタグ、body属性からbodyタグを取得できる。

root = lxml.html.fromstring(html)

print(root.head, root.body)

実行結果

<Element head at 0x10252a3b0> <Element body at 0x102536310>

headタグ以下、またはbodyタグ以下の情報が欲しいときに使います。

子要素の取得

リストに変換することで子要素をリストとしてまとめて取得できる。

root = lxml.html.fromstring(html)

print(list(root))

実行結果

[<Element head at 0x1012e95e0>, <Element body at 0x1012e97c0>]

インデックスを指定して取得することもできる。

root = lxml.html.fromstring(html)

print(root[0], root[1])

実行結果

 <Element head at 0x104a035e0> <Element body at 0x104a037c0>

親要素の取得

親要素を取得するには、getparent() を使います。

root = lxml.html.fromstring(html)

print(root.body.getparent())

実行結果

<Element html at 0x105705450>

前後のタグ

前のタグを getprevious()、次のタグを getnext() で取得できます。

root = lxml.html.fromstring(html)

# body の取得
body = root.body
# body の子要素
print(f'bodyの子要素: {list(body)}')

# body の中の h2
h2 = body[1]

# h2 の前のタグ
print(f'h2の前のタグ: {h2.getprevious()}')
# h2 の次のタグ
print(f'h2の次のタグ: {h2.getnext()}')

実行結果

bodyの子要素: [<Element h1 at 0x106a59680>, <Element h2 at 0x106a59860>, <Element p at 0x106a773b0>]
h2の前のタグ: <Element h1 at 0x106a59860>
h2の次のタグ: <Element p at 0x106a59860>

ルートの取得

ルートは xpath('/*') で取得できる。

root = lxml.html.fromstring(html)

# body の取得
body = root.body

# body の中の h2
h2 = body[1]

# h2 からルートの取得
print(h2.xpath('/*'))

実行結果

[<Element html at 0x105871f40>]

xpath() はリストでタグを取得するので注意。

タグを探す

find() を使うことでタグを探すことができる。

root = lxml.html.fromstring(html)

# body の取得
body = root.find('body')
print(body)

# h1 の取得
h1 = root.find('body/h1')
print(h1)

実行結果

<Element body at 0x100ba0400>
<Element h1 at 0x100bac770>

findall() を使うことで複数の要素を取得できる。

root = lxml.html.fromstring(html)

# body 内のタグの取得
in_body = root.findall('body/*')
print(in_body)

実行結果

[<Element h1 at 0x1020bf3b0>, <Element h2 at 0x1020bf7c0>, <Element p at 0x1020bf9a0>]

xpath() を使っても同様のことができる。

root = lxml.html.fromstring(html)

print(root.xpath('body/*'))

実行結果

[<Element h1 at 0x10c73a400>, <Element h2 at 0x10c7433b0>, <Element p at 0x10c7437c0>]

タグの指定方法は以下の記事を参考にしてください。

外部リンクロケーションパス(省略構文) - XPathのまとめ、要素の参照方法いろいろ │ Web備忘録

idで探す

タグに指定した ID から検索できる。

# HTMLデータ
html = '''<!DOCTYPE html>
<html lang="ja">
 <head>
 <meta charset="utf-8">
 <title>サイトタイトル</title>
 </head>
 <body>

 <h1 id="title">タイトル</h1>

 <h2>見出し1</h2>
 <p>コンテンツの内容</p>
 
 </body>
</html>'''

root = lxml.html.fromstring(html)

# title という ID を持つタグの取得
title = root.get_element_by_id('title')
print(title)

実行結果

<Element h1 at 0x10f7a6630>

タグの情報の取得

タグから属性やテキストを取得する方法を見ていきましょう!

タグ名の取得

tag 属性からタグ名を取得することができます。

root = lxml.html.fromstring(html)

# h1 の取得
h1 = root.find('body/h1')
# タグ名の取得
print(h1.tag)

実行結果

h1

属性の取得

attrib 属性からタグの属性を辞書として取得できる。

root = lxml.html.fromstring(html)

# h1 の取得
h1 = root.find('body/h1')

# 属性の取得
print(h1.attrib)

実行結果

{'id': 'title'}

get() で指定した属性のみを取得することもできる。

root = lxml.html.fromstring(html)

# h1 の取得
h1 = root.find('body/h1')

# 属性の取得
print(h1.get('id'))

実行結果

title

テキストの取得

text 属性からタグに囲まれているテキストを取得できる。

root = lxml.html.fromstring(html)

# h1 の取得
h1 = root.find('body/h1')

# 属性の取得
print(h1.text)

実行結果

タイトル

直下のテキストしか取得できないので <h1><a>タイトル</a></h1> のようになっている場合は注意。

インライン要素の後ろにあるテキストは tail 属性から取得する。

html = '<p>あいうえお<span>かきくけこ</span>さしすせそ</p>'

p = lxml.html.fromstring(html)

# span の取得
span = p.find('span')

# span の後ろのテキスト
print(span.tail)

実行結果

さしすせそ

簡単な実践

試しに適当なサイトの記事内の見出しを抜き出してみます。

import lxml.html
import requests

# 適当なURL
url = 'https://yumarublog.com/python/str'

# # URLを開く
r = requests.get(url)

# HTML文字列の読み込み
root = lxml.html.fromstring(r.text)

body = root.body

# 記事内の見出しのみを取得
elems = body.xpath('//div[@class="cps-post-main-box"]//h2')

for elem in elems:
    print(elem.text)

実行結果

大文字小文字に変換して比較する
casefold()メソッドを使う
正規表現を使う

まとめ

この記事では、lxmlライブラリを使ってHTML文字列を解析する方法を解説しました。

lxml はとても高速で簡単にスクレイピングすることができます。解析するサイトの迷惑にならないように高速でアクセスするのは避けましょう!

それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ

『DMM WEBCAMP COMMIT』