HTMLの高度なパース
概要
IronPythonの例題のひとつとなります。
少々崩れたHTMLデータであってもDOMとして解析し、要素の抽出や置き換えが可能な HTML Aglity Pack を利用したサンプルです。
マネージドdll(.NET用DLL)だけ公開されているような「配布ライブラリ」を、
hmPyを経由して、いかに手軽に秀丸マクロから利用するか、といったサンプルでもあります。

ダウンロード
このhmHtmlAgility.zipには変換元のHTMLとなるサンプルファイルa.htmlが含まれています。
説明
HTMLの要素を解析し、その中身を取り出したい、といったことは珍しいことではありません。
しかし、「フォーマット的に正しいことが期待できるXMLやRSS」とは異なり、
HTMLは人の手が多分に混じっていることが多いため、不正なフォーマット(崩れた状態の)ものが多いの特徴です。
(一つも誤りがないHTMLの方が珍しいことでしょう)
IronPythonの部分
IronPythonの部分では、HTML Agility Packを利用しつつ、
「HTMLのロード」「toInnerText」「toInnerHtml」「新たなHTMLへのセーブ」という4つの関数に分けて制作してみました。 秀丸マクロから呼び出されることを意識したインターフェイスと言えるでしょう。
#coding: cp932
DEBUG = 1
import clr
import sys
sys.path.append(hm.Macro.Var["currentmacrodirectory"])
import System
import System.IO
import System.Text
import System.Collections.Generic
clr.AddReferenceByPartialName( "HtmlAgilityPack")
clr.AddReferenceByPartialName( "System.Windows.Forms" )
from HtmlAgilityPack import *
from System.Collections.Generic import *
load_html_filename = hm.Macro.Var["$load_html_filename"]
save_html_filename = hm.Macro.Var["$save_html_filename"]
file_encode = hm.Macro.Var["#encode"]
doc = ""
# htmlのロード。秀丸から操作しやすくするためにこのような単純なインターフェイスで
def LoadHTML():
global doc
si = None
try:
si = System.IO.StreamReader( load_html_filename, System.Text.Encoding.GetEncoding(file_encode) )
except:
System.Windows.Forms.MessageBox.Show("load_html_filename中にエラーが発生しました")
if si:
si.Close()
raise IOError
doc = HtmlDocument()
doc.LoadHtml(si.ReadToEnd())
si.Close()
# 対象のタグの中身はテキストだけになる(タグも除去される)
def toInnerText(tag):
global doc
q = doc.DocumentNode.Descendants(tag)
l = List[HtmlNode](q)
for item in l:
newNodeStr = item.InnerText
newNode = HtmlNode.CreateNode(newNodeStr)
item.ParentNode.ReplaceChild(newNode, item)
# 対象のタグは除去するが、他のタグは残る
def toInnerHtml(tag):
global doc
q = doc.DocumentNode.Descendants(tag)
l = List[HtmlNode](q)
for item in l:
newNodeStr = item.InnerHtml
newNode = HtmlNode.CreateNode(newNodeStr)
item.ParentNode.ReplaceChild(newNode, item)
# htmlのセーブ。秀丸から操作しやすくするためにこのような単純なインターフェイスで
def SaveHTML():
global doc
try:
so = System.IO.StreamWriter( save_html_filename, False, System.Text.Encoding.GetEncoding(file_encode) )
so.Write(doc.DocumentNode.InnerHtml)
so.Close()
except:
System.Windows.Forms.MessageBox.Show("save_html_filename中にエラーが発生しました")
if so:
so.Close()
raise IOError
マクロ側の処理
マクロ側では「ロードファイル」や「セーブファイル」を指定することとなるでしょう。
今回はエンコーディングについては、「UTF-8」固定としました。
ローカルなどの作業用テキストなどとは異なり、
Web用のHTMLについては、近年では様々な都合により、ほとんどのケースでUTF-8で記述されているようです。
マクロとして開いている html ファイルに対して…
- titleタグについては、中身のテキストだけに置き換える(タグを除去)
- blockquoteタグについては、中身のHTMLだけに置き換える(一番外のblockquoteタグだけを除去)
それを 元のファイル名 + 「_up.html」という形にして秀丸で開いています。
#PY = loaddll( hidemarudir + "\\hmPY.dll" );
if (!#PY) {
message("hmPYが未導入");
}
$load_html_filename = filename2;
$save_html_filename = directory2 + "\\" + basename2 + "_up.html";
#encode = codepage;
#_ = dllfuncw( #PY, "DoFile", currentmacrodirectory + "\\hmHtmlAgility.py" );
#_ = dllfuncw( #PY, "DoString", R"IPY(
LoadHTML()
toInnerText('title')
toInnerHtml('blockquote')
SaveHTML()
)IPY"
);
freedll( #PY );
openfile $save_html_filename;
ライセンス
-
HmHtmlAgility.pyとHmHtmlAgility.macについて
パブリックドメインとします。(即ち利用は自由)
-
Html Agility Packについて
Simon Mourierの著作物であり、Microsoft Public License(Ms-PL) ライセンスとなります。
詳細はHtml Agility Packのライセンスを確認してください。