質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
XML

XMLは仕様の1つで、マークアップ言語群を構築するために使われています。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

XPath(XML Path)

XML Path Language (XPath; XMLパス言語)は、マークアップ言語 XML に準拠した文書の特定の部分を指定する言語構文の事をいいます。XPathはXMLとは別の構文を使用します。XMLドキュメントの抽象、論理ストラクチャ上で動作します。

Q&A

解決済

4回答

21574閲覧

VBAでHTMLに対してXPathで取得する良い方法ないですか?

Mr_Roboto

総合スコア2208

XML

XMLは仕様の1つで、マークアップ言語群を構築するために使われています。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

XPath(XML Path)

XML Path Language (XPath; XMLパス言語)は、マークアップ言語 XML に準拠した文書の特定の部分を指定する言語構文の事をいいます。XPathはXMLとは別の構文を使用します。XMLドキュメントの抽象、論理ストラクチャ上で動作します。

0グッド

3クリップ

投稿2016/08/07 14:56

最近、スクレイピングなんてものが流行ってるようで、
私も簡単な処理を頼まれててVBAで作りました。

MSXMLというものがあるので、
これでXPAthで簡単に取れるだろうと思ったのですが、どうもうまくいきません。
XMLを前提にしているので、HTMLだとタグ等が壊れている場合も多く、エラーになります。
まぁ、当たり前と言えば当たり前ですが。

納期があるので、今回はDomでgetElementBy〜で処理したのですが、
なんとなく、XPathの方がオサレだよなぁと思うわけです。

今後も含めて何かいい方法があるに違いないと思って調べてみると
validateOnParse = False とすると良さ気なのでやってみても
警告レベルのエラーでもパース出来ず結局エレメントが取得できません。

Webのサンプルを参考にしながらテストでこんな風に書きました。

VBA

1Sub XPathTest() 2 3 Dim dom As DOMDocument30 4 Set dom = New DOMDocument30 5 dom.async = False 6 dom.validateOnParse = False 7 dom.setProperty "SelectionLanguage", "XPath" 8 9 Dim objXML As New MSXML2.XMLHTTP 10 Dim ret As Boolean 11 12 With objXML 13 .Open "GET", "http://www.example.com/", False 14 .send (Null) 15 ret = dom.LoadXML(.responseText) 16 End With 17 18 Dim nodelist As IXMLDOMNodeList 19 Set nodelist = dom.DocumentElement.SelectNodes("//tr") 20 21 Debug.Print nodelist.Item(0).nodeName 22 23End Sub

処理を渡す前にHTMLをある程度加工してあげればうまくいくかもとも思いましたが、
ちょっとやっぱり現実的ではないですよね。

調査する際は、ブラウザのコンソールで$x('//tr')とかやるとさっくり取ってくれます。
PHPでもダメなのかと思って書いてみたら、警告は出るもののエレメントの取得は出来ます。

なので、VBAでもなんとかうまいこと出来ないものかと思って調べてみても、
なかなかこれはという情報に行き当たりません。

MSXMLじゃなくてもいいので、XPathエンジン的なものでも
呼び出して処理するというような方法は無いものでしょうか?

何か情報がございましたら、ご回答よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ご質問の内容に対して直接的な回答ではないのですが。。。

まっとうにXMLドキュメントとして扱うのは困難なことが確かに多いですね。
しかも正規表現を使った方が圧倒的にパフォーマンスが上がってしまったので、正規表現を使うことにしたことがあります。

投稿2016/08/14 11:58

kaz.Suenaga

総合スコア2037

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mr_Roboto

2016/08/15 03:11

ありがとうございます。 なるほど正規表現ですかぁ、改行とかあっても大丈夫なのかなぁという心配がありますけどねぇ。 (1行にしちゃえばいいのかな) XPath使いたいなぁw
guest

0

ベストアンサー

MSXMLはXMLパーサーです.XMLを入力としてDOMを作りXPathを使えます.でもHTMLは入力にはなりえません.それを解決する手段が他の方も言及されているXHTMLとすることです.

もうご覧になっていらっしゃると思いますが、サンプルは以下です.

Using MSXML with HTML
https://msdn.microsoft.com/en-us/library/ms759123%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

引用テキストvalidateOnParse = False とすると良さ気なのでやってみても

これは単に入力XMLをスキーマやDTDで検証しないだけで、入力がwell-formedなXMLである条件には変わりはありません.

調査する際は、ブラウザのコンソールで$x('//tr')とかやるとさっくり取ってくれます。

世の中のブラウザはHTML DOM+XPathをサポートしているので、ブラウザコンソールではできちゃいますね~.

という訳でMSXMLは入力がXHTMLでない限りあきらめられた方が良いと思います.

以上 少しでもお役にたてば.

[追記]
コメントに書きましたプログラムです.

vb

1Imports System.Xml 2Imports System.Xml.XPath 3Imports System.Text.Encoding 4Imports Sgml 5Module HtmlXPathModule 6 Sub Main() 7 Dim xw As XmlWriter = CreateXmlWriter("maxTemp.xml") 8 xw.WriteStartElement("tempDataRoot", "") 9 Dim sgml As SgmlReader = New SgmlReader() 10 sgml.DocType = "HTML" 11 sgml.Href = "http://www.data.jma.go.jp/obd/stats/data/mdrr/synopday/data1s.html" 12 sgml.IgnoreDtd = True 13 Dim htmlDoc As XDocument = XDocument.Load(sgml) 14 Dim nsTable As NameTable = New NameTable 15 Dim nsMgr As XmlNamespaceManager = New XmlNamespaceManager(nsTable) 16 nsMgr.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml") 17 Dim targetTrs As IEnumerable(Of XElement) = htmlDoc.XPathSelectElements("//xhtml:table[@class = 'o1']//xhtml:tr[@class != 'o1h']", nsMgr) 18 For Each tr As XElement In targetTrs 19 Dim targetTds As IEnumerable(Of XElement) = tr.Elements 20 Dim tdArray As XElement() = targetTds.ToArray 21 Dim region As XElement = tdArray(0) 22 Dim maxTemp As XElement = tdArray(6) 23 xw.WriteStartElement("tempData") 24 xw.WriteAttributeString("region", "", region.Value) 25 xw.WriteAttributeString("maxTemp", "", maxTemp.Value) 26 xw.WriteEndElement() 27 Next 28 xw.WriteEndElement() 29 xw.Close() 30 End Sub 31 32 Function CreateXmlWriter(outputPath As String) As XmlWriter 33 Dim settings As XmlWriterSettings = New XmlWriterSettings() 34 settings.CloseOutput = True 35 settings.ConformanceLevel = ConformanceLevel.Document 36 settings.Encoding = UTF8 37 settings.Indent = False 38 settings.NewLineChars = vbCrLf 39 settings.NewLineHandling = NewLineHandling.None 40 settings.OmitXmlDeclaration = False 41 settings.WriteEndDocumentOnClose = False 42 Dim xw As XmlWriter = XmlWriter.Create(outputPath, settings) 43 Return xw 44 End Function 45 46End Module

こんなXMLが出ます.(maxTemp.xml)今日は日本列島暑いです.37℃越え(!)のところもあります.

XML

1<?xml version="1.0" encoding="UTF-8"?> 2<tempDataRoot> 3 <tempData maxTemp="27.1]" region="札幌"/> 4 <tempData maxTemp="26.9]" region="稚内"/> 5 <tempData maxTemp="26.0]" region="北見枝幸"/> 6 ... 7 <tempData maxTemp="37.1]" region="鹿児島"/> 8 ... 9 <tempData maxTemp="31.3]" region="西表島"/> 10 <tempData maxTemp="33.1]" region="石垣島"/> 11 <tempData maxTemp="" region="昭和"/> 12</tempDataRoot>

入力はたまたまXHTMLでした.他のダーティーなのでも試した方が良いのでしょうけれども、私の家は冷房なしでこれ以上気力が持ちません... あと普段まずVisual Studioを立ち上げる機会はゼロです.おかしな点があるかもしれません、ご容赦ください.

[追記]

HtmlAgilityPackを使ってみましたが結果は散々でした.

HtmlAgilityPackでもいろいろやった末、以下のコードで動きました.エンコーディングの指定が要るようです.

vb

1Imports System.Text.Encoding 2Imports System.Net 3Imports HtmlAgilityPack 4 5... 6 7 Sub Main() 8 Dim xw As XmlWriter = CreateXmlWriter("maxTemp.xml") 9 xw.WriteStartElement("tempDataRoot", "") 10 Dim wc As WebClient = New WebClient() 11 wc.Encoding = UTF8 12 Dim htmlSource As String = wc.DownloadString("http://www.data.jma.go.jp/obd/stats/data/mdrr/synopday/data1s.html") 13 Dim doc As HtmlDocument = New HtmlDocument() 14 doc.LoadHtml(htmlSource) 15 Dim trs = doc.DocumentNode.SelectNodes("//table[@class = 'o1']//tr[@class != 'o1h']") 16 For Each tr In trs 17 Dim tds As IEnumerable(Of HtmlNode) = tr.Elements("td") 18 Dim tdArray As HtmlNode() = tds.ToArray 19 Dim regionTd As HtmlNode = tdArray(0) 20 Dim maxTempTd As HtmlNode = tdArray(6) 21 xw.WriteStartElement("tempData") 22 xw.WriteAttributeString("region", regionTd.InnerText) 23 xw.WriteAttributeString("maxTemp", maxTempTd.InnerText) 24 Debug.WriteLine("region={0} max-temp={1}", regionTd.InnerText, maxTempTd.InnerText) 25 xw.WriteEndElement() 26 Next 27 xw.WriteEndElement() 28 xw.Close() 29 End Sub 30

投稿2016/08/14 11:54

編集2016/08/20 11:17
tmakita

総合スコア69

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mr_Roboto

2016/08/15 03:10

ありがとうございます。 MSXMLじゃなくても良いのですが
tmakita

2016/08/15 05:07

> MSXMLじゃなくても良いのですが ごもっともです.単なる否定ではソリューションになっていませんね. ご希望の最低要件は「VBAを使用する」でしょうか?? 以上
Mr_Roboto

2016/08/15 11:05

そうですね、Excelでごにょごにょやる案件が今後もありそうなので、VBAでできれば一番です。 あとはそこそこメジャーなブラウザのエンジンとかなら許容範囲というか。 クリップもたくさん(3つ)ついてるので、気になる人も多いのかとw そういうプロジェクトがGitHubなりSourceForgeなりにありそうなものですけどねぇ
tmakita

2016/08/16 05:57

VBAで使えるという事は、要するにCOMオブジェクトでなければいけないですね.WEBで.NETのDLLをVBAから参照可能にする(つまりCOM化する)方法というのは書いてあったので、たぶんできるのでしょう.その前の段階ですけれど、.NET + VB でどこまでできるかやってみました.他の方の投稿にもありましたので、HtmlAgilityPackを使ってみましたが結果は散々でした.属性だったらまだ拾えるのですが、要素のテキストをどうこうするというのが初心者の私ではどうにもなりません.かわりに、SgmlReader(https://github.com/MindTouch/SGMLReader) + XPathでごにょごにょやって、ようやく気象庁の天気データ(http://www.data.jma.go.jp/obd/stats/data/mdrr/synopday/data1s.html)から地点と最高気温を抜き出してXMLに落とすプログラムを作ってみました.それなりに動いているみたいです.でもこのURL用なので汎用とは言えません.私はブラウザをごちょごちょいじるのはキライなので、こんなプログラムでもよろしければ回答に参考として書きますが、要りますか?
Mr_Roboto

2016/08/16 06:09

ありがとうございます。 いろいろ書いていただいていただいたようで恐縮です。 他の方にも何かしらの参考になるとは思いますので、是非掲載をお願いします。 長ければ抜粋していただいて、全体はGistとかでもいいと思います。
Mr_Roboto

2016/08/17 10:43

追記ありがとうございます、必要になった時に参考にしたいと思います。
guest

0

.NETのHtmlAgilityPack.dllを、なんとかVBAから参照してXHTML変換できればもしや・・と思ったのですが、、参照できるところまでたどり着かず挫折。。
お力になれず申し訳ないす(T-T

投稿2016/08/10 11:16

jawa

総合スコア3013

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shouyu

2016/08/10 12:44

C#でHtmlAgilityPack.dllを使ったXHTML変換をするメソッドを作成して、 COMコンポーネントに登録すれば、VBAでも使えるようになりますよ。 ただRegAsm.exeを使ってレジストリに書き込みが必要になります。
Mr_Roboto

2016/08/15 03:08

ありがとうございます。 ちょっとめんどくさげですね、納品するのはちょっと厳しいような ^_^;
guest

0

MSXMLでXPathを使って・・って思った時期が私にもありましたが、
対象のページのHTMLがそもそもXMLに準拠してない書き方だと
MSXMLは食あたりしてしまうのでダメなんだと早々に諦めた記憶があります。
ダーティーなHTMLは無理です。
HTML-lintなどのサービスでエラーが出ないほどXML準拠していたら、もしかすると。

投稿2016/08/09 12:25

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mr_Roboto

2016/08/09 12:46

ありがとうございます。 駄目くさいですねぇ、teratailで回答が出ないということは厳しいかなぁ
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問