HOME Python 書き込む

xml.dom.minidom を用いた XML の取り扱い


1. 初めに

XML は html のように、タグで文章の意味をあらわす形式で、 技術文書のように構造化された文書をあらわす 形式として最近広く用いられるようになっています。 たとえば、DocBook は XML を用いた技術文書のフォーマットであり、これを元に html, tex, pdf などの出力形式が生成されます。

Python には expat, dom, sax などの複数の XML パーサの ライブラリがあります。ここでは、比較的簡単で、機能も豊富な xml.dom.minidom を取り上げ、 どんな感じで XML をパースし、別形式で出力するか述べます。

2. 参考文献

この文書の種本は以下の2つです。 どちらも英語ですが、xml.dom.minidom について詳しく書かれています。

3. 例: XML から Tex を生成する。

早速例をあげて説明します。 XML から Tex のソースファイルを作ってみます。 まず、[code 1] のような入力ファイルを作ります。

まず、1行目で文字コードに UTF-8 を指定します。こうすると日本語も読んでくれます。 4, 9 行目でタグを入れ子にします。また、 10 — 23 行目で入れ子のリストを作ります。

[code 1] (party.xml)

001:   <?xml version="1.0" encoding="UTF-8"?>
002:   <article paper='a4paper' size='12pt' type='jarticle'>
003:   <document>
004:   <center><Large>お知らせ</Large></center>
005:   
006:   下記の通り飲み会を行います。
007:   <em>ふるって</em>ご参加ください。
008:   
009:   <center><large><bf>記</bf></large></center>
010:   <enumerate>
011:       <item>新年会
012:       <itemize>
013:           <item>日時:2010 年 1 月 1 日</item>
014:           <item>場所:新橋の飲み屋</item>
015:           <item>会費:5000 円</item>
016:       </itemize></item>
017:       <item>花見
018:       <itemize>
019:           <item>日時:2010 年 4 月 3 日</item>
020:           <item>場所:上野公園</item>
021:           <item>会費:3500 円</item>
022:      </itemize></item>
023:   </enumerate>
024:   <flushright>以上</flushright>
025:   <vspace value='2cm' />
026:   <center>
027:   <img file='december.bmp' height='5cm' width='4cm'/>
028:   </center>
029:   </document>
030:   </article>
[code 2] に [code 1] から tex ファイルを作る python コードを示します。
  1. (8 行目) XML ファイルのパースは簡単です。xml.dom.minidom の parse を使えば XML ファイルから木構造のオブジェクトを作ってくれます。
  2. (9 行目) getElementsByTagName で、今のノードの子ノードで article という名前のもののリストを返します。
  3. (73 行目) codecs.open を使って、文字コードを指定して出力ファイルを開きます。
  4. (74 行目) print_node を使って、木構造を出力ファイルに書き出していきます。
  5. (45 — 66 行目) print_node の定義です。この関数を再帰的に呼び出すことによって木構造を書き出していきます。 node.childNodes は node の子ノード全てのリストです。
  6. (50,51 行目) ノードがタグにはさまれたテキストの場合、それを出力します。
  7. (12 — 17 行目など) ノードの属性のハッシュテーブルは attributes に収められています。ただ、普通のハッシュ表と違い、 値を取り出すときは value をつける必要があります。また、ある属性があるかどうかは has_key() というメソッドで調べることができます。
  8. (29 行目など) ノードの名前は tagName で取り出します。
  9. (23 — 47 行目) いろいろなタグに分類していますが、基本的には同じです。
001:   #!/usr/bin/env python
002:   
003:   from xml.dom.minidom import parse
004:   import sys, codecs
005:   
006:   
007:   def get_article(fname):
008:       obj = parse(fname)
009:       return obj.getElementsByTagName('article')[0]
010:   
011:   def print_article(f, article):
012:       options=article.attributes
013:   
014:       print >>f, r'\documentclass[%s,%s]{%s}' % \
015:         ((options['paper'].value if options.has_key('paper') else 'a4paper'), \
016:          (options['size'].value if options.has_key('size') else '12pt'), \
017:          (options['type'].value if options.has_key('type') else 'jarticle'))
018:       print >>f, r'\usepackage{graphicx}'
019:   
020:       for node in article.childNodes:
021:           print_node(f, node)
022:   
023:   def print_img(f, node):
024:       attr=node.attributes
025:       print >>f, r'\includegraphics[height=%s, width=%s]{%s}' % \
026:                       (attr['height'].value, attr['width'].value, attr['file'].value)
027:           
028:   def print_env(f, node):
029:       print >>f, r'\begin{%s}' % (node.tagName,)
030:       for c in node.childNodes:
031:           print_node(f, c)
032:       print >>f, '\n\\end{%s}' % (node.tagName,)    
033:       
034:   def print_block(f, node):
035:       f.write(r'{\%s ' % (node.tagName))
036:       for c in node.childNodes:
037:           print_node(f, c)
038:       f.write('}')
039:   
040:   def print_parm(f, node):
041:       f.write(r'\%s{%s}' % (node.tagName, node.attributes['value'].value))
042:       
043:   
044:   def print_uni(f, node):
045:       f.write(r'\%s ' % (node.tagName))
046:       for child in node.childNodes:
047:           print_node(f, child)
048:       
049:   def print_node(f, node):
050:       if node.nodeType == node.TEXT_NODE:
051:           f.write(node.data)
052:       elif node.tagName=='article':
053:           print_article(f, node)
054:       elif node.tagName=='img':
055:           print_img(f, node)
056:       elif node.tagName in ('document', 'flushleft', 'flushright', 'center', 'enumerate', 'itemize'):
057:           print_env(f, node)
058:       elif node.tagName in ('hspace', 'vspace'):
059:           print_parm(f, node)
060:       elif node.tagName in ('large', 'Large', 'small', 'em', 'bf'):
061:           print_block(f, node)
062:       elif node.tagName in ('item', 'newpage'):
063:           print_uni(f, node)
064:       else:
065:           for child in node.childNodes:
066:               print_node(f, child)
067:       
068:   if __name__=='__main__':
069:       fname=sys.argv[1]
070:       article=get_article(fname)
071:       if article:
072:           pos=fname.find('.')
073:           f = codecs.open(fname[0:pos]+'.tex', 'w', 'shift_jis')
074:           print_node(f, article)
075:           f.close()
$ python xml2tex.python party.xml
とすると、[code 3] で示す tex ファイルができ、これをコンパイルすると 図 1 に示すような dvi ファイルが出来上がります。

[code 3] (party.tex)

001:   \documentclass[a4paper,12pt]{jarticle}
002:   \usepackage{graphicx}
003:   
004:   \begin{document}
005:   
006:   \begin{center}
007:   {\Large お知らせ}
008:   \end{center}
009:   
010:   
011:   下記の通り飲み会を行います。
012:   {\em ふるって}ご参加ください。
013:   
014:   \begin{center}
015:   {\large {\bf 記}}
016:   \end{center}
017:   
018:   \begin{enumerate}
019:   
020:       \item 新年会
021:       \begin{itemize}
022:   
023:           \item 日時:2010 年 1 月 1 日
024:           \item 場所:新橋の飲み屋
025:           \item 会費:5000 円
026:       
027:   \end{itemize}
028:   
029:       \item 花見
030:       \begin{itemize}
031:   
032:           \item 日時:2010 年 4 月 3 日
033:           \item 場所:上野公園
034:           \item 会費:3500 円
035:      
036:   \end{itemize}
037:   
038:   
039:   \end{enumerate}
040:   
041:   \begin{flushright}
042:   以上
043:   \end{flushright}
044:   
045:   \vspace{2cm}
046:   \begin{center}
047:   
048:   \includegraphics[height=5cm, width=4cm]{december.bmp}
049:   
050:   
051:   \end{center}
052:   
053:   
054:   \end{document}
[図 1]

4. 終わりに

簡単ですが、python での XML ファイルの取り扱いについて述べてみました。 今後少しずつ書き足していこうと思っています。