HTMLの高度なパース

概要

IronPythonの例題のひとつとなります。

少々崩れたHTMLデータであってもDOMとして解析し、要素の抽出や置き換えが可能な HTML Aglity Pack を利用したサンプルです。

マネージドdll(.NET用DLL)だけ公開されているような「配布ライブラリ」を、
hmPyを経由して、いかに手軽に秀丸マクロから利用するか、といったサンプルでもあります。

PICTURE

ダウンロード

更新日 2017/12/15
HmHtmlAgility.zip v1.20

この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のライセンスを確認してください。