MSHTMLを使ってHTMLを解析してみる*3
URLを指定してHTMLを読み込みドキュメントツリーを見るテスト
#include "stdafx.h" #include <mshtml.h> class CMSHTMLParserTest : public CAtlExeModuleT<CMSHTMLParserTest> { public: HRESULT PreMessageLoop(int nShowCmd) throw() { HRESULT hr = CAtlExeModuleT<CMSHTMLParserTest>::PreMessageLoop(nShowCmd); if (FAILED(hr)) { return hr; } hr = pHTMLDocument_.CoCreateInstance(__uuidof(HTMLDocument)); ATLASSERT(SUCCEEDED(hr)); return hr; } void RunMessageLoop( ) throw() { HRESULT hr; CComPtr<IMoniker> pMoniker; hr = CreateURLMoniker(NULL, L"http://www.microsoft.com/japan/msdn/", &pMoniker); ATLASSERT(SUCCEEDED(hr)); CComPtr<IBindCtx> pBindCtx; hr = CreateBindCtx(0, &pBindCtx); ATLASSERT(SUCCEEDED(hr)); CComQIPtr<IPersistMoniker> pPersistMoniker(pHTMLDocument_); hr = pPersistMoniker->Load(FALSE, pMoniker, pBindCtx, STGM_READ); ATLASSERT(SUCCEEDED(hr)); hr = WaitForComplite(); ATLASSERT(SUCCEEDED(hr)); CComQIPtr<IHTMLDocument3> pDoc(pHTMLDocument_); CComPtr<IHTMLElement> pDocElement; hr = pDoc->get_documentElement(&pDocElement); ATLASSERT(SUCCEEDED(hr)); hr = WalkTree(pDocElement); ATLASSERT(SUCCEEDED(hr)); } private: HRESULT WalkTree(IHTMLElement* pElm) throw() { HRESULT hr; try { CComBSTR tagName; hr = pElm->get_tagName(&tagName); ATLASSERT(SUCCEEDED(hr)); OutputDebugStringW(tagName); OutputDebugStringW(L"\n"); CComPtr<IDispatch> pChildrenDisp; hr = pElm->get_children(&pChildrenDisp); ATLASSERT(SUCCEEDED(hr)); CComQIPtr<IHTMLElementCollection> pChildren(pChildrenDisp); long cnt; hr = pChildren->get_length(&cnt); ATLASSERT(SUCCEEDED(hr)); for (long idx = 0; idx < cnt; idx++) { CComVariant name(idx); CComVariant dmy; CComPtr<IDispatch> pChildDisp; hr = pChildren->item(name, dmy, &pChildDisp); ATLASSERT(SUCCEEDED(hr)); CComQIPtr<IHTMLElement> pChild(pChildDisp); hr = WalkTree(pChild); if (FAILED(hr)) { break; } } } catch (...) { return E_FAIL; } return hr; } HRESULT WaitForComplite() throw() { try { HRESULT hr = E_FAIL; int loop = 0; for (;;) { CComBSTR state; hr = pHTMLDocument_->get_readyState(&state); ATLASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { break; } OutputDebugStringW(state); OutputDebugStringW(L"\n"); if (state != L"uninitialized" && state != L"loading") { hr = S_OK; } else { if (loop < 1000) { Sleep(50); continue; } } break; } return hr; } catch (...) { return E_FAIL; } } private: CComPtr<IHTMLDocument2> pHTMLDocument_; }; CMSHTMLParserTest _atlModule; int _tmain(int argc, _TCHAR* argv[]) { return _atlModule.WinMain(SW_SHOWNORMAL); }
所感
上記コードに自信はないが、とりあえず、動いた。その程度のレベル。
モニカの仕組みが分かっていないのだが、CreateURLMonikerでURLを指定することはできても、CreateFileMonikerでファイルを指定しても正しく読んでくれなかったのだ。
どうやら、IPersistMonikerではなく、IPersistFileを使ってロードしなければダメらしい。
なんで、そんなことになっているのかはわからない。
また非同期的にHTMLを読み込んでくれるので、完了したかどうかをチェックしなければならないのは、単純なシングルスレッドなバッチツールのような用途では、めんどくさいような気がする。(もちろん、コールバックを登録できるようであるが。)
それに、ステートを文字列で比較する以外に方法はないのだろうか?
しかし、この方法を使えばWebBrowserコントロールなどを使わずとも、GUIなしでHTMLを読ませて解析することができるので、覚えておくと重宝するのかもしれない。
使い方は奥が深いというか、調べないと分からないことは多すぎる気がする。