lxmlを使ったxmlのパース方法
lxmlというライブラリを使ってxmlをパースしたときのメモを記事にしています。
lxmlはBeautifulSoupというスクレイピングのライブラリでも使われたりしなかったりします。
xmlファイルを取得する
pythonでファイルを読み込む方法はいろいろありますが、再帰的にファイルを取得しつつ、指定したディレクトリ以下のxmlファイルをすべて取得します。
from lxml import etree from pathlib import Path def get_xml_files(target): files = list(Path(target).glob('**/*.xml')) return files
get_xml_files
を実行すると、指定したディレクトリ以下のxmlファイルのパスがすべて取得できます。
このパスをlxmlで読み込んで、利用します。
lxmlでxmlをパースする
get_xml_files
を使って、ファイルまでのパスを取得後にパース処理をします。
xml_files = get_xml_files(xml_target) for xml_file in xml_files: xml_file_name = str(xml_file) tree = etree.parse(xml_file_name)
xml_file
はPosixPoth
クラスのインスタンスなので、そのままetree.parse
に渡せません。
一度strで文字列にしてからparseします。
コメントの削除方法
xmlのコメントが残っていると、forでループする際にcommentが引っかかります。
これを解消するために、removeしようと考えたのですが、remove系の関数がありませんでした。
理想ではtree.rm_comments()
とかetree.rm_comments()
があったらよかったです。
というわけで、対処方法が以下のプログラムです。
def remove_comments(tree): comments = tree.xpath('//comment()') for comment in comments: parent = comment.getparent() parent.remove(comment)
tagを判定する
以下のプログラムは、mybatisというjavaのormで使うxmlをパースする際に使った関数です。
xmlの中身はこのようなものです。
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="jp.hoge.com"> <select id="selectId" resultClass="Sample" parameterClass="string"> SELECT id FROM sample WHERE id = 1 </select> </sqlMap>
このxmlをパースしてステートメントごとに配列にしています。
def get_statements(tree): statement_elements = [] for element in tree.iter(): if element.tag in ['select', 'insert', 'update', 'delete', 'procedure']: statement_elements.append(element) return statement_elements
属性を取得する際はelement.get('id')
のようにします。
tree.iter()
でelementを取得できますが、少し癖があります。
深さ優先探索のようにタグを取得していくので、書くのが難しかったです。
一度に欲しい情報を取得するのではなく、個別に取得する方法を採用しました。
例えば、以下のようなxmlがあった場合、sqlをdictに格納して、selectをパースしている最中にincludeを見つけたらdictを参照して、中身を置き換えます。
<sqlMap namespace="jp.hoge.com"> <sql id="sampleInclude"> where id = 2 </sql> <select id="selectId" resultClass="Sample" parameterClass="string"> SELECT id FROM sample <include refid="sampleInclude" /> </select> </sqlMap>