lxmlはXMLやHTMLファイルを簡単に扱えるPythonのライブラリで、Webスクレイピングにも利用できます。市販のXMLパーサーはたくさんありますが,よりよい結果を得るために,開発者は自分でXMLやHTMLのパーサーを書きたいと思うことがあります.このような場合に、lxmlライブラリが役に立ちます。このライブラリの主な利点は、使いやすく、大きなドキュメントをパースするときに非常に速く、非常によく文書化されており、データをPythonのデータ型に簡単に変換できるため、ファイル操作が容易になることです。
このチュートリアルでは、Pythonのlxmlライブラリを深く掘り下げ、異なるオペレーティングシステム用に設定する方法から始めて、その利点と提供する幅広い機能について説明します。
インストール
lxmlをシステムにインストールするには、複数の方法があります。以下にそのいくつかを紹介します。
Pipの使用
Pip は Python パッケージマネージャで、Python ライブラリをローカルシステムに簡単にダウンロードおよびインストールするために使用されます。
pipがシステムにインストールされている場合、ターミナルまたはコマンドプロンプトで次のコマンドを実行するだけです。
$ pip install lxml
apt-getの使用
MacOSやLinuxをお使いの方は、ターミナルで以下のコマンドを実行して、lxmlをインストールすることができます。
$ sudo apt-get install python-lxml
easy_installの使用
おそらくこの部分には到達しないと思いますが、もし何らかの理由で上記のコマンドのどれもがうまくいかない場合は、easy_install
を使用してみてください。
$ easy_install lxml
注意: 特定のバージョンの lxml をインストールしたい場合は、コマンドプロンプトまたはターミナルで lxml==3.x.y
のようにコマンドを実行するときに、そのバージョンを指定することができます。
ここまでで、あなたのローカルマシンには lxml ライブラリがインストールされているはずです。では、さっそくこのライブラリを使ってどんなことができるのか見てみましょう。
機能性
lxmlライブラリをプログラムで使用できるようにするには,まず,インポートする必要があります.これは以下のコマンドで行うことができる。
from lxml import etree as et
このコマンドは、lxmlライブラリの中から、今回対象とする etree
モジュールをインポートするものである。
HTML/XMLドキュメントの作成
etreeモジュールを使うと、XML/HTML の要素とその下位要素を作成することができます。これは、HTML や XML ファイルを作成したり操作したりする場合に、非常に便利なものです。ここでは、
etree` を用いて HTML ファイルの基本構造を作ってみよう。
root = et.Element('html', version="5.0")
# Pass the parent node, name of the child node,
# and any number of optional attributes
et.SubElement(root, 'head')
et.SubElement(root, 'title', bgcolor="red", fontsize='22')
et.SubElement(root, 'body', fontsize="15")
上のコードでは、 Element
関数は少なくとも1つのパラメータを必要とするのに対して、 SubElement
関数は少なくとも2つのパラメータを必要とすることを知っておく必要があります。これは、 Element
関数が作成する要素の名前のみを「要求」するのに対し、 SubElement
関数はルートノードと作成する子ノードの両方の名前を要求するためです。
また、これらの関数はどちらも受け付けられる引数の数に下限があるだけで、好きなだけ属性を関連付けることができるため上限がないことも知っておくとよいでしょう。要素に属性を追加するには、(Sub)Element関数に引数を追加して、 attributeName='attribute value'
という形で属性を指定するだけです。
これらの関数について、より直感的に理解するために、上で書いたコードを実行してみましょう。
# Use pretty_print=True to indent the HTML output
print (et.tostring(root, pretty_print=True).decode("utf-8"))
出力
出力:“`
もうひとつ、階層的に要素を作成し、整理する方法があります。こちらも見てみましょう。
root = et.Element(‘html’)
root.append(et.SubElement(‘head’))
root.append(et.SubElement(‘body’))
この場合、新しい要素を作成するたびに、それをルート/親ノードに単純に追加します。
#### HTML/XML 文書のパース
これまで、新しい要素を作成し、それに属性を割り当てることだけを考えてきました。ここでは、すでにHTMLやXMLのファイルがあり、それを解析して特定の情報を抽出する例を見てみましょう。最初の例で作成したHTMLファイルがあると仮定して、ある特定の要素のタグ名を取得し、次にすべての要素のタグ名を出力してみます。
print(root.tag)
出力します。
html
次に、`root` ノードにあるすべての子要素を繰り返し処理し、そのタグを表示します。
for e in root:
print(e.tag)
出力
head
title
body
#### アトリビュートの操作
では、既存の要素に属性を関連付ける方法と、与えられた要素に対して特定の属性の値を取得する方法を見てみましょう。
先ほどと同じ `root` 要素を使って、次のコードを試してみてください。
root.set(‘newAttribute’, ‘attributeValue’)
Print root again to see if the new attribute has been added
print(et.tostring(root, pretty_print=True).decode(“utf-8”))
出力
ここで、`newAttribute="attributeValue"` が確かにルート要素に追加されたことがわかります。
では、上のコードで設定した属性の値を取得してみましょう。ここでは、`root` 要素の配列インデックスを使って子要素にアクセスし、`get()` メソッドを使って属性を取得することにします。
print(root.get(‘newAttribute’))
print(root[1].get(‘alpha’)) # root[1] accesses the title
element
print(root[1].get(‘bgcolor’))
出力します。
出力:```
attributeValue
None
red
要素からテキストを取得する
さて、ここまででetree
モジュールの基本的な機能を見てきましたが、HTMLやXMLファイルを使ってもう少し面白いことをやってみましょう。ほとんどの場合、これらのファイルにはタグとタグの間にテキストがあります。では、どのようにして要素にテキストを追加するか見てみましょう。
# Copying the code from the very first example
root = et.Element('html', version="5.0")
et.SubElement(root, 'head')
et.SubElement(root, 'title', bgcolor="red", fontsize="22")
et.SubElement(root, 'body', fontsize="15")
# Add text to the Elements and SubElements
root.text = "This is an HTML file"
root[0].text = "This is the head of that file"
root[1].text = "This is the title of that file"
root[2].text = "This is the body of that file and would contain paragraphs etc"
print(et.tostring(root, pretty_print=True).decode("utf-8"))
出力
<html version="5.0"This is an HTML file<headThis is the head of that file</head<title bgcolor="red" fontsize="22"This is the title of that file</title<body fontsize="15"This is the body of that file and would contain paragraphs etc</body</html
要素が子供を持つかどうかを確認する
次に、例外処理のために多くのウェブスクレイピングアプリケーションで必要とされる、2つの非常に重要なことを確認する必要があります。まず、要素に子供がいるかどうか、そしてノードが Element
であるかどうかです。
上で作成したノードに対してそれを行ってみましょう。
if len(root) > 0:
print("True")
else:
print("False")
上のコードは、ルートノードが子ノードを持つので、「True」と出力されます。しかし、以下のコードのように、ルートの子ノードに対して同じことを確認すると、出力は「False」になります。
for i in range(len(root)):
if (len(root[i]) > 0):
print("True")
else:
print("False")
を出力する。
False
False
False
次に、各ノードが Element
であるかどうかを確認するために、同じことを行ってみましょう。
for i in range(len(root)):
print(et.iselement(root[i]))
出力
True
True
True
iselementメソッドは、有効な
Element` オブジェクトがあるかどうか、つまり、ここで紹介したメソッドを使ってトラバリングを続けられるかどうかを判断するのに役立ちます。
要素が親要素を持つかどうかを確認する
先程は、階層を下る方法、つまり、ある要素が子要素を持っているかどうかを調べる方法を示しましたが、このセクションでは、階層を上る方法、つまり、子ノードの親を調べて取得する方法を試してみましょう。
print(root.getparent())
print(root[0].getparent())
print(root[1].getparent())
最初の行は何も返しません(別名 None
)。他の2行は両方ともルート要素、つまりHTMLタグを指しているはずです。出力が期待通りのものであるかどうか、チェックしてみましょう。
出力です。
None
<element 0x1103c9688="" at="" html=""
<element 0x1103c9688="" at="" html=""
要素の兄弟姉妹を取得する
このセクションでは、階層を横方向に移動して、ツリー内の要素の兄弟を取得する方法を学びます。
ツリーを横方向に移動する方法は、縦方向に移動する方法とよく似ています。後者では、 getparent
と要素の長さを使用しましたが、前者では getnext
と getprevious
の関数を使用することになります。それでは、以前に作成したノードで試してみて、どのように動作するかを確認してみましょう。
# root[1] is the `title` tag
print(root[1].getnext()) # The tag after the `title` tag
print(root[1].getprevious()) # The tag before the `title` tag
出力します。
<element 0x10b5a75c8="" at="" body=""
<element 0x10b5a76c8="" at="" head=""
ここでは、root[1].getnext()
が次の要素である “body” タグを取得し、root[1].getprevious()
が “head” タグを取得していることが分かります。
同様に、もしルートに対して getprevious
関数を使用した場合は None
を返し、もしルート[2] に対して getnext
関数を使用した場合は None
を返したでしょう。
文字列から XML をパースする
次に、XMLやHTMLファイルから必要な情報を取得したり操作したりするために、生の文字列を解析したい場合は、以下の例に従って行います。
root = et.XML('<html version="5.0"This is an HTML file<headThis is the head of that file</head<title bgcolor="red" fontsize="22"This is the title of that file</title<body fontsize="15"This is the body of that file and would contain paragraphs etc</body</html')
root[1].text = "The title text has changed!"
print(et.tostring(root, xml_declaration=True).decode('utf-8'))
出力
<?xml version='1.0' encoding='ASCII'?
<html version="5.0"This is an HTML file<headThis is the head of that file</head<title bgcolor="red" fontsize="22"The title text has changed!</title<body fontsize="15"This is the body of that file and would contain paragraphs etc</body</html
ご覧のように、HTML文書内のテキストを変更することに成功しました。XML の doctype 宣言も、 tostring
関数に渡した xml_declaration
パラメータにより、自動的に追加されました。
エレメントを検索する
最後に紹介するのは、XML や HTML ファイルをパースするときにとても便利なものです。ある要素が特定のタイプの子要素を持つかどうか、そして持つならその子要素は何を含むかを確認する方法について説明します。
これは、特定のWebページ上のすべてのlink要素を見つけるような、多くの実用的なユースケースを持っています。
print(root.find('a')) # No <a tags exist, so this will be `None`
print(root.find('head').tag)
print(root.findtext('title')) # Directly retrieve the the title tag's text
出力
None
head
This is the title of that file
結論
In the above tutorial, we started with a basic introduction to what lxml library is and what it is used for. After that, we learned how to install it on different environments like Windows, Linux, etc. Moving on, we explored different functionalities that could help us in traversing through the HTML/XML tree vertically as well as sideways. In the end, we also discussed ways to find elements in our tree, and as well as obtain information from them.