Lua用の使いやすいHTML・XML処理ライブラリーを開発しました。 これは、クリアコードが株式会社セナネットワークス様からの発注を受けて開発したライブラリーです。 XMLua(えっくすえむえるあ)といいます。MITライセンスで公開しています。
Luaは、スクリプト言語の書きやすさとC言語に匹敵する速さを持っている言語です。 速度は欲しいがC言語を使っての開発が大変というような状況のときによく使われます。 XMLuaは速度を大事にしているので、Luaが使われるような高速な動作が必要な状況でHTML・XMLを処理したいという場面で有用です。
現状は、最低限必要と思われる機能しか実装していませんが、XMLuaはLuaでHTML・XMLを処理したいという人に広く使ってほしいので、他の言語で広く使われているHTML・XML処理ライブラリー(Pythonではlxml、RubyではNokogiriというライブラリーがあります。)を参考に、徐々に機能を拡張していく予定です。
インストール方法
ライブラリーは、LuaRocksで公開しており、以下のコマンドで簡単にインストールできます。
例えば、Debian GNU/Linuxでは以下のようにインストールします。
% sudo apt install -y -V libxml2
% sudo luarocks install xmlua
Debian GNU/Linux以外のOSでのインストールは、XMLua - インストールを参照してください。 XMLuaは、Debian GNU/Linuxの他に、Ubuntu、CentOS、macOSに対応しています。
また、XMLuaはLuaJITが提供するFFIライブラリーを使って、C言語の関数やデータ構造にアクセスしているため、XMLuaを使うには、LuaJITが必要になります。お使いのOSのパッケージ管理システムを使って、予めLuaJITもインストールしておいてください。
XMLuaの主な機能
XMLuaの主な機能を紹介します。 XMLuaを使うとLuaで以下のようなことができます。
HTML、XMLドキュメントのパース/シリアライズ
XMLuaを使ってHTML、XMLを操作するには、まず、xmlua.Documentオブジェクトを作る必要があります。 xmlua.Documentオブジェクトは、以下のように処理対象のHTMLまたは、XMLをパースして、取得します。
-- "xmlua"モジュールの読み込み
local xmlua = require("xmlua")
local html = [[
<html>
<head>
<title>Hello</title>
</head>
<body>
<p>World</p>
</body>
</html>
]]
-- HTMLをパース
local document = xmlua.HTML.parse(html)
パースする対象は、Luaの文字列型に格納されている必要がありますので、ファイルに保存されているHTMLやXMLをパースする場合は、以下のように事前にファイルから読み込んでからパースする必要があります。
-- "xmlua"モジュールの読み込み
local xmlua = require("xmlua")
local html_file = io.open("test.html")
local html = html_file:read("*all")
html_file:close()
local document = xmlua.HTML.parse(html)
また、以下のようにして、処理したHTML、XMLをxmlua.Documentから元のLuaの文字列に変換することもできます。
-- "xmlua"モジュールの読み込み
local xmlua = require("xmlua")
local html = [[
<html>
<head>
<title>Hello</title>
</head>
<body>
<p>World</p>
</body>
</html>
]]
-- HTMLをパース
local document = xmlua.HTML.parse(html)
-- HTMLへシリアライズ
print(document:to_html())
-- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
-- <html>
-- <head>
-- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-- <title>Hello</title>
-- </head>
-- <body>
-- <p>World</p>
-- </body>
-- </html>
XPathを使った、要素の検索と属性値の取得
XMLuaは以下のようにXPathを用いて、要素を検索できます。
local xmlua = require("xmlua")
local xml = [[
<root>
<sub>text1</sub>
<sub>text2</sub>
<sub>text3</sub>
</root>
]]
local document = xmlua.XML.parse(xml)
-- <root>要素配下の全ての<sub>要素を検索します
local all_subs = document:search("/root/sub")
-- "#"を使ってマッチしたノードの数を出力できます。
print(#all_subs) -- -> 3
-- "[]"を使って、N番目のノードにアクセスできます。
print(all_subs[1]:to_xml()) -- -> <sub1>text1</sub1>
print(all_subs[2]:to_xml()) -- -> <sub2>text2</sub2>
print(all_subs[3]:to_xml()) -- -> <sub3>text3</sub3>
上記の検索機能を使うことで、スクレイピングしたWebサイトから特定の要素のみを抜き出すことや、特定の要素が含まれるページを検索する等に利用できます。また、以下のように検索した結果に対して、さらに検索できます。
local xmlua = require("xmlua")
local xml = [[
<root>
<sub class="A"><subsub1/></sub>
<sub class="B"><subsub2/></sub>
<sub class="A"><subsub3/></sub>
</root>
]]
local document = xmlua.XML.parse(xml)
-- 全ての<sub class="A">要素を検索
local class_a_subs = document:search("//sub[@class='A']")
-- <sub class="A">配下の全ての要素を検索
local subsubs_in_class_a = class_a_subs:search("*")
print(#subsubs_in_class_a) -- -> 2
-- /root/sub[@class="A"]/subsub1
print(subsubs_in_class_a[1]:to_xml())
-- <subsub1/>
-- /root/sub[@class="A"]/subsub3
print(subsubs_in_class_a[2]:to_xml())
-- <subsub3/>
XMLuaには、属性値を取得する機能もあり、スクレイピングしたWebページから特定の要素の属性値を抜き出してまとめて処理するといったこともできます。 属性値の取得は以下のように行います。
local xmlua = require("xmlua")
local document = xmlua.XML.parse("<root class='A'/>")
local root = document:root()
-- ドットを使った属性値の取得
print(root.class)
-- -> A
-- []を使った属性値の取得
print(root["class"])
-- -> A
-- get_attributeメソッドを使った属性値の取得
print(root:get_attribute("class"))
-- -> A
マルチスレッドでの使用
XMLuaはマルチスレッドに対応しているため、複数のスレッドから呼び出すことができ、大量のHTMLやXMLを処理する際に効率的に処理することが出来るようになっています。 マルチスレッドで使用するには、幾つかの決まりごとがあるので、マルチスレッドで使用する場合は、XMLua - チュートリアルのマルチスレッドセッションを参照してください。
おわりに
XMLuaの主な機能を紹介しました。大量にXML・HTMLを操作する必要がある場合の選択肢として、XMLuaを是非使ってみて下さい。
XMLuaの機能についてより詳しく知りたい場合は、XMLua - リファレンスを参照してください。XMLuaの全ての機能の詳細を記載しています。
また、XMLuaをすぐに動かしてみたいという方は、XMLua - チュートリアルを参照してください。ここで紹介した主な機能をすぐに使えるようになるチュートリアルがあります。
開発した成果をフリーソフトウェアで公開するというのは、自社サービスを提供をしてる会社によく見られる光景ですが、クリアコードは自社サービスを持っておらず、このライブラリーは、株式会社セナネットワークス様からの依頼を受けて開発したライブラリーです。つまり受託開発の成果物です。 受託開発の成果物であっても、フリーソフトウェアとして公開することで様々なユーザーがライブラリーを使えます。様々な環境下で使われることにより、いままで発見出来なかったバグを発見出来たり、当初想定されていなかったニーズに気がつけたりして、ライブラリーの品質が高まります。これはお客さんにとってもメリットとなります。
この度、ご依頼主の株式会社セナネットワークス様に上記のようなメリットにご理解をいただき、成果を公開できました。ありがとうございます!