前の関連記事:LibreOffice(56)htmlをファイルに保存してブラウザに結果を出力する
メソッドや属性の名前をみても何をするものかよくわかりませんのでSDKのドキュメントから解説の部分だけ抽出してみます。まずはLibreOffice4.2.6のSDKをインストールしました。「Install」ボタンがクリックできるようになるまでに結構時間がかかりました。
ローカルディスクのhtmlファイルを読み込んで必要な部分を抽出する
SDKをインストールするとLibreOffice 4.2 SDK API ReferenceがC:\Program Files (x86)\LibreOffice 4\sdk\docs\idl\refフォルダにhtmlファイルで入っています。
とりあえずどれかhtmlファイルを開くとオンラインでみるのと同様のレイアウトのページが開きますので、右上の検索ボックスで目的のAPI(IDL)を探し出します。
メソッドを持つ例としてXComponentContext Interface Reference、XToolkit Interface Reference、サービス属性を持つものとしてGenericTextDocument Service Reference、インターフェイス属性を持つものといてXEmbeddedScripts Interface Referenceでそれぞれ考えてみます。
まずはXComponentContextのhtmlファイルからメソッドの解説の抽出をしてみます。
ファイル名はa02217.htmlでした。これを適当なフォルダへ移動させます。
C:/_LibreOffice_test/IDLへ移動させました、、、これが大きな間違いでした。
ファイルを移動させると画像へのリンクが切れないように自動的にhtmlが書き換えられてしまいます。
その書き換えられてしまった部分を含んだ正規表現パターンで以下の作業を行っていたので、あとでまたこの記事を一から作り直すことになってしまいました。
ということでC:\Program Files (x86)\LibreOffice 4\sdk\docs\idl\refフォルダに入れたままで以下の作業を行います。
a02217.htmlのソースをみながらLibreOffice(42)UNOオブジェクトの属性1:正規表現パターンを作成で学習した正規表現パターンを作っていきます。
Windowsのメモ帳にa02217.htmlをドロップしても改行記号が認識されず1行で表示されてしまうので、ワードパッドにドロップしてソースを見ます。
参照しているhtmlファイルのIDLのフルネームは上のパンくずリストから得ることにしました。
この部分は<div id="nav-path" class="navpath">になります。
XComponentContextのアンカータグからこのファイル名を得ます。
メソッドの情報はこの部分から得ることにします。
この部分は<table class="memberdecls">というテーブルになるのですが、後で出てくるサービスでは「Exported Interfaces」の部分がとてもたくさん含まれてしまうので「Public Member Functions」の直前にある<a name="pub-methods">から得ることにします。
ここからメソッド名とそのジャンプ先、メソッドの解説を抽出します。
libreoffice57.py
.uno.XComponentContext a02217.html .uno.XComponentContext::getValueByName a02217.html#aba4803c30336f63c0f548c5087cb8683 Gets a value from the context. .uno.XComponentContext::getServiceManager a02217.html#ab63afa2026d599602797552cacc07b73 Gets the service manager instance to be used from key <code>/singletons/com.sun.star.lang.theServiceManager</code>.これでこのファイルから必要な部分の抽出ができました。
XToolkitのa03524.htmlからもメソッドの情報が抽出できました。
今度はGenericTextDocumentのa00795.htmlからもサービス属性の情報を抽出してみます。
サービス属性は「Public Attributes」の項目になるので<a name="pub-attribs">を見ればよいことになります。
libreoffice57.py
他は変更しなくてもサービス属性の項目が得られました。
「Public Attributes inherited from OfficeDocument」以降は不要なのでそこでwhile文を抜けるようにしました。
インターフェイス属性をもつXEmbeddedScriptsのa02474.htmlです。
これは何も変更しなくてもインターフェイス属性の項目が得られました。
これですべてのパターンを網羅しているのかはやってみないとわかりません。
読み込んだデータを辞書に収納する
C:\Program Files (x86)\LibreOffice 4\sdk\docs\idl\refフォルダには17202個のhtmlファイルがあります。
このうち379個のファイルはサブフォルダのserchにあるものなので見に行かなくてよさそうです。
見に行かないといけないファイルはa+5桁の数字のファイル名のhtmlファイルと思われます。
どうも03687より大きな数字のものは見なくてよさそうです。
a00001.htmlからa03687.htmlのなかにはメンバーアイテムがないのもありますのでそれは出力しないようにします。
print()で出力しているものを辞書に収納していきます。
libreoffice57.py
dic_idl_html = dict() # キー:idl、値:idlのhtmlファイル名、の辞書。 dic_memItem_html = dict() # キー:メンバーアイテム、値:メンバーアイテムのhtml、の辞書。 dic_memItem_mdesc = dict() # キー:メンバーアイテム、値:メンバーアイテムの解説、の辞書。メソッドとかサービス属性とか分けて辞書を作らないといけないと思っていましたが、全部同じ条件で抽出できたので区別しないことにしました。
fileinputで複数ファイルを読み込む
結局パスに依存しない正規表現パターンにできたのでC:/_LibreOffice_test/IDLへファイルを移動させて複数ファイルの読み込みのテストをします。
複数ファイルの読み込みにはfileinputを使おうと思います。
まずはa02217.html、a00795.html、a03524.htmlの3つのファイルでテストです。
libreoffice57.py
with fileinput.input(files=('C:/_LibreOffice_test/IDL/a02217.html', 'C:/_LibreOffice_test/IDL/a00795.html', 'C:/_LibreOffice_test/IDL/a03524.html')) as f: # 複数ファイルの読み込み。 line = f.readline() # 1行ずつファイルを読み込む。FileInput クラスのインスタンスはTextFile クラスのメソッドを備えているようで11行目のwith文のところを書き換える以外はそのままf.readline()も動きました。
if '<a name="details" id="details">' in line: # 「Detailed Description」まできたとき f.nextfile() # 次のファイルにいく。単ファイルの処理のときは39行目でwhile文を抜けてプラグラムを終了していましたが、今回はwhile文は抜けずにf.nextfile()で次のファイルに行っています。
これで11行目で指定した3つのファイルから目的の情報を取り出せましたが今度はファイル指定にワイルドカードを使えるようにします。
ファイル名の指定にワイルドカードを使うにはglobモジュールを使います。
a00001.htmlからa03687.htmlのファイルを指定したいのでワイルドカードは'a0[0-3][0-9][0-9][0-9].html'にします。
このパターンですとa03688.htmlからa03999.htmlのファイルもマッチしてしまいます。
ファイル名の昇順に読み込まれるのならa03688.htmlで読み込まれたときに処理を終了すればよいのですが、昇順に読み込まれるかどうか不明なのでファイル名から数字を抜き出して比較することにしました。
fileinputで読み込むときのエンコードをutf-8に指定する
libreoffice57.py
4000個近いファイルを読み込むとなるとすると時間もかかる上にエラーがでてきます。
エラーが起きるファイル名はPyCharm(5)ブレークポイント設定いろいろの「ループ内の例外のデバッグは例外が出た状態でブレークすると便利」の方法で確認しました。
まず最初にひっかかったのはAmbiguous< T > Struct Referenceのa00096.htmlです。
原因は「Ambiguous< T >」のTの両側に含まれている空白と括弧です。
reg_idl_html = r'href="(\S+?.html)">\w+?</a></li>\s*?</ul>' # 参照しているhtmlファイル名を抽出する正規表現パターン。これの\wがマッチしません。
reg_idl_html = r'href="(\S+?.html)">.+?</a></li>\s*?</ul>'すべての文字にマッチするように変更すると最初に出てきたhtmlファイル名が抽出されてしまいます。
reg_idl_html = r'href="(a\d\d\d\d\d.html)">'結局その行のすべてのhtmlファイル名を取得してその最後の要素を得ることにしました。
ついでにファイル名の条件を厳しくしました。これは今回のエラーに関係ありませんけど。
libreoffice57.py
次に引っかかったのがUriSchemeParser_vndDOTsunDOTstarDOTexpand Service Referenceのa01912.htmlです。
このファイルをreadline()しようとするとUnicodeDecodeErrorがでてきます。
UnicodeDecodeError: 'cp932' codec can't decode byte 0x9d in position 2829: illegal multibyte sequence
別のフォルダに移してチェックしてみると次のa01913.htmlも同じエラーがでます。
a01914.htmlは大丈夫でした。
どうもこれはutf-8のファイルをcp932のエンコードで読み込もうとしているのが問題のようです。
エンコーディングを指定しなければutf-8で読み込まれるものと思っていましたがそうでもないようです。
with fileinput.input(openhook=fileinput.hook_encoded("utf-8"), files=(glob.glob('C:/Program Files (x86)/LibreOffice 4/sdk/docs/idl/ref/a0[0-3][0-9][0-9][0-9].html'))) as f: # 複数ファイルの読み込み。fileinputで読み込むときにutf-8で読み込むように指定しました。
libreoffice57.py
これでエラーが出なくなりました。
結果が出るまでそこそこ時間がかかります。
辞書をprint()しても大きすぎてConsoleにでてこないので51行目にブレークポイントを設定してデバッグモードで辞書を確認しました。
キー:idl、値:idlのhtmlファイル名、の辞書dic_idl_htmlは2577項目になりました。
これはAPIリファレンスでメンバーアイテムが取得できるAPIの数になります。
キー:メンバーアイテム、値:メンバーアイテムのhtml、の辞書dic_memItem_htmlは10883項目、キー:メンバーアイテム、値:メンバーアイテムの解説、の辞書dic_memItem_mdescは10068項目でした。
これで必要な情報すべてなのか、間違って取得しているものがないかはいまのところ私にはわかりません。
参考にしたサイト
7.2. ファイルを読み書きする
Pythonでファイルの読み書きをする方法。
10.22. distutils.text_file — TextFile クラス
ファイルを読み書きするときのファイルはこのTextFileクラスのインスタンスになります。
11.2. fileinput — 複数の入力ストリームをまたいだ行の繰り返し処理をサポートする。 — Python 3.3.5 ドキュメント
複数ファイルをまとめて読み込む方法。openhook=fileinput.hook_encoded("utf-8")が必要でした。
11.6. glob — Unix 形式のパス名のパターン展開 — Python 3.3.5 ドキュメント
ファイル名の指定にワイルドカードを使う方法。パスの区切り文字はOS固有のものになるようです。
11.7. fnmatch — Unix ファイル名のパターンマッチ — Python 3.3.5 ドキュメント
globはfnmatchを使ってパターンマッチしてしています。ワイルカードの特別な文字の解説。
PyRegex
Pythonの正規表現のチェックにすごく便利なツール。デフォルトでmatchであることに注意。
0 件のコメント:
コメントを投稿