tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルWebbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2381
投稿日: 2005/10/10(Mon) 12:07
投稿者YAS
[OSのVer]:Windows XP  [VBのVer]:VB.NET 2005 beta2
VB.NET2005Beta2(ベータで恐縮です)のWebbrowserコントロールに次のようなHtmlを読み込ませ,
Documentプロパティから各エレメントのテキスト(innerText)にアクセスしたいのですが,タグの
ないテキストを選択できず,困っています。
下のHtmlの"いつも参考に"の部分は
me.Webbrowser.Document.body.children(0).innerText
で選択できますが,
"VBレスキューを" や "させていただいています。"という部分をDOMツリーか
ら選択するには
どうしたらよいのでしょうか。bodyエレメントのinnerTextでは
"VBレスキューをいつも参考にさせていただいています。"
となってしまいますし,bodyの子エレメントにタグのない部分は含まれません。
(bodyの子エレメント数は1で<b>〜</b>の部分です。)
MSHTMLについても検索したのですが,どうしてもわかりません。

なお,目的は教育目的でWebページの漢字にかなをふることです。ボタンや図表などの漢字に
も<ruby>タグをつけてしますとおかしなことになるので,<a>タグや本文だけを対象に処
理を
したいと考えています。正規表現で処理しようとしていたのですが,例外的な処理に悩み
DOMでアクセスしたいと考えました。

長文・乱文で失礼いたしました。
どなたかご教授をお願いいたします。

<html>
<body>
VBレスキューを<b>いつも参考に</b>させていただいています。
</body>
</html>  

[ツリー表示へ]
タイトルRe: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2383
投稿日: 2005/10/11(Tue) 09:55
投稿者なおこ(・∀・)
お世話になります。

VB.NET2005Beta2は手元にないので、試していないのですが、
こんな感じでしょうか。
Dim xmldoc As System.Xml.XmlTextReader
Dim stream As System.IO.StringReader
Try
  stream = New System.IO.StringReader("<html>" _
                    + "<body>" _
                    + "VBレスキューを" _
                    + "<b>" _
                    + "いつも参考に" _
                    + "</b>" _
                    + "させていただいています。" _
                    + "</body>" _
                    + "</html>")
  xmldoc = New System.Xml.XmlTextReader(stream)
  While (xmldoc.Read)
    If xmldoc.NodeType = Xml.XmlNodeType.Text Then
      Console.WriteLine(xmldoc.Value)
    End If
  End While
Finally
  If Not xmldoc Is Nothing Then xmldoc.Close()
  If Not stream Is Nothing Then stream.Close()
End Try

[ツリー表示へ]
タイトルRe: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2396
投稿日: 2005/10/12(Wed) 09:45
投稿者ふもふも
me.Webbrowser.Document.body.innerTextで取得できると思います。

[ツリー表示へ]
タイトルRe: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2398
投稿日: 2005/10/12(Wed) 12:34
投稿者YAS
なおこ(・∀・) さんありがとうございます。
なおこ(・∀・) さんのコードで私のhtmlの例は処理できました。
ところが実際に処理したいwebページのhtmlを処理させると
xmlの「予期されていないトークン」のエラーがでてしまいました。
これは対象のhtmlがxmlとみなせるよう整形されていないためだと
思います。(htmlはルールが相当ゆるいようです。)
.netにはhtmlreaderはないようなので、htmlをxhtmlに変換しようと
思いましたが、正規表現で簡単に置換ともいかないようです。

ふもふもさんありがとうございます。
確かに
me.webbrowser.document.body.innerText
でbodyの中のTextは得られるのですが、一部を置換してもとのhtmlに
埋め戻すことができません。body.innerTextもしくはinnerHtmlを変更
してしまうと子エレメントの<b></b>がなくなってしまいます。
目的はwebページの中のリンクや本文の中から漢字を見つけ、IMEの逆変換
やkakasiを使って読みがなを得て<ruby><rb>漢字<rt>かんじ</ruby>と
置換したいのです。
body全体を一気にkakasiにかけ、正規表現で置換をかけることもできますが、
ボタンやイメージのキャプションにもルビタグが入ってしまうのです。
そこでDOMツリーを再帰で検索しながら本文と特定のタグのテキストだけに
処理をしようと思ったのですが...なかなかうまくできません。

検索してみるとJAVAではノードという概念ですべての要素にアクセスできる
ようです。
mshtmlや.netの2003のwebbrowserコントロールで処理できないかさらに調
べてみます。

長文・乱文失礼いたしました。

[ツリー表示へ]
タイトルRe^2: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2399
投稿日: 2005/10/12(Wed) 12:58
投稿者魔界の仮面弁士
標準のHTMLパーサだと、テキストノードは扱い難いのですが、とりあえずは
  a = .getAdjacentText("BeforeBegin")
  b = .getAdjacentText("AfterBegin")
  c = .getAdjacentText("BeforeEnd")
  d = .getAdjacentText("AfterEnd")
あたりが使えるのではないかと。


それと……WebBrowser.document が返すオブジェクトは、COMコンポーネントですよね。
.NETからの利用であれば、その配下のオブジェクトも含め、解放処理に気を配る必要があるかも。

[ツリー表示へ]
タイトルRe^3: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2438
投稿日: 2005/10/19(Wed) 12:03
投稿者YAS
[OSのVer]:Windows    [VBのVer]:VB.NET  
魔界の仮面弁士さんありがとうございます。

お礼と返信が遅くなりまして申し訳ありませんでした。
.getAdjacentText等のメソッドについて調べていたのですが,
VB.NET2005β2には実装されていないようです。
.NET2003についても見つけることができませんでした。
JAVA等のDOMにはあるようでした。

仕方がないので正規表現で検索して置換するように考えてみます。

WebBrowserコントロールですが,.NET2003ではCOMコンポーネント
のようですが,.NET2005β2ではWindowsForm2.0のようです。
後ろにCOMコンポーネントがあるのでしょうが,オブジェクトの開放は
たぶんガベージコレクション等がよろしくやってくれるのでは
ないでしょうか。(勝手な推測ですが。)

PS早く.NET2005の正式版でませんかねぇ。もう2003には戻れません。

[ツリー表示へ]
タイトルRe^4: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2439
投稿日: 2005/10/19(Wed) 13:05
投稿者魔界の仮面弁士
> VB.NET2005β2には実装されていないようです。
あー。そういえば、.NET 2.0 を使っているのでしたっけ。

すみません、手元に 2.0 の環境が無いので確認できないのですが、
HtmlDocument.DomDocument プロパティ経由でも無理ですか?

[ツリー表示へ]
タイトルRe^5: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2441
投稿日: 2005/10/19(Wed) 21:32
投稿者YAS
[OSのVer]:Windows    [VBのVer]:VB.NET  

> HtmlDocument.DomDocument プロパティ経由でも無理ですか?
ヘルプを見ましたところ,DomDocumentはCOMへアンマネージのポインタを
渡せるようです。MSHTMLオブジェクトライブラリのIHTMLDocument2インター
フェイスを使うことができるようになるようです。
早速MSHTMLへの参照を追加して挑戦してみます。
ご助言ありがとうございます。

(心配なのはMSHTMLの配布条件ですが,よく調べてみます。)

[ツリー表示へ]
タイトルRe^5: Webbrowser.DocumentからHTMLでタグのないテキストを取得したい
記事No2442
投稿日: 2005/10/19(Wed) 23:36
投稿者YAS
[OSのVer]:Windows    [VBのVer]:VB.NET  
魔界の仮面弁士さんのヒントからMSHTMLをさらに調べ,以下のコードで
希望の結果を得ることができました!!


(プロジェクトにMSHTMLオブジェクトライブラリの参照を追加する)
Imports MSHTML

Public Class Form1

    Dim Idt As Integer

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
                Handles Me.Load
        Me.WebBrowser1.Navigate(Application.StartupPath & "\test.html")
    End Sub

    Private Sub WebBrowser1_DocumentCompleted(ByVal sender As Object, _
            ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _
            Handles WebBrowser1.DocumentCompleted
        Dim myDoc As HTMLDocument = Me.WebBrowser1.Document.DomDocument
        PrintNodeTree(myDoc.body)
        myDoc.close()
    End Sub

    Private Sub PrintNodeTree(ByVal Node As IHTMLDOMNode)
        For Each cNode As IHTMLDOMNode In Node.childNodes
            Debug.Print(Space(Idt) & "<" & cNode.nodeName & ">")
            If Not cNode.nodeValue Is DBNull.Value Then
                Debug.Print(Space(Idt) & cNode.nodeValue)
            End If
            If cNode.hasChildNodes Then
                Idt += 4
                PrintNodeTree(cNode)
            End If
        Next
        Idt -= 4
    End Sub

End Class

ご助言いただいたみなさんのおかげで(たぶん)解決いたしました。
ありがとうございました!!

P.S.コードに(開放忘れ等の)間違いや,よりシンプルに書ける部分がありましたら
  引き続きご教授お願いいたします!

[ツリー表示へ]
タイトルRe^6: Webbrowser.Documentから...
記事No2443
投稿日: 2005/10/20(Thu) 11:53
投稿者魔界の仮面弁士
> P.S.コードに(開放忘れ等の)間違いや,
DomDocument は、unmanaged な COM インターフェイスを返すようなので、
おそらくは、Marshal.ReleaseComObject が必要になるかと思います。

ただ、手元に .NET 2.0 の環境が無いのでチェックはできません。m(_ _)m
.NET 1.1 時代の知識を基にした机上デバッグなので、
あまり保証はできませんが、それでもよければ、多分――


Imports System.Runtime.InteropServices

>     Private Sub WebBrowser1_DocumentCompleted(ByVal sender As Object, _
>             ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _
>             Handles WebBrowser1.DocumentCompleted
'>        Dim myDoc As HTMLDocument = Me.WebBrowser1.Document.DomDocument
          Dim myDoc As HTMLDocument
          Try
            myDoc = Me.WebBrowser1.Document.DomDocument
            Try
              Dim body As IHTMLDOMNode = myDoc.body
>             PrintNodeTree(body)
            Finally
              If Not body Is Nothing AndAlso Marshal.IsComObject(body) Then
                Marshal.ReleaseComObject(body)
              End If
            End Try
>           myDoc.close()
          Finally
           If Not myDoc Is Nothing AndAlso Marshal.IsComObject(myDoc) Then
             Marshal.ReleaseComObject(myDoc)
           End If
          End Try
>     End Sub
>
>     Private Sub PrintNodeTree(ByVal Node As IHTMLDOMNode)
         Dim childNodes As Object
         Try
           childNodes = Node.childNodes
'>         For Each cNode As IHTMLDOMNode In Node.childNodes
           For Each cNode As IHTMLDOMNode In childNodes
>            Debug.Print(Space(Idt) & "<" & cNode.nodeName & ">")
>            If Not cNode.nodeValue Is DBNull.Value Then
>              Debug.Print(Space(Idt) & cNode.nodeValue)
>            End If
>            If cNode.hasChildNodes Then
>              Idt += 4
>              PrintNodeTree(cNode)
>            End If
             '---> 一応書いてみましたが、この部分の必要性は不明……。
             If Not cNode Is Nothing AndAlso Marshal.IsComObject(cNode) Then
               Marshal.ReleaseComObject(cNode)
             End If
             '<---
>           Next
          Finally
            If Not childNodes Is Nothing AndAlso Marshal.IsComObject(childNodes) Then
              Marshal.ReleaseComObject(childNodes)
            End If
          End Try
>         Idt -= 4
>     End Sub
> End Class

――のような感じになるのでは、と予想しています。

# For Each 中での解放処理が必要かどうかを調べたのですが、わかりませんでした。
# もしかしたら不要かもしれませんし、あるいは IEnumerator の方を解放する必要が
# あるのかも知れませんが、現時点ではわかりません。m(_ _)m
# hhttp://www.divakk.co.jp/aoyagi/csharp_tips_vssenum.html

[ツリー表示へ]
タイトルRe^7: Webbrowser.Documentから...
記事No2444
投稿日: 2005/10/20(Thu) 13:58
投稿者YAS
[OSのVer]:Windows    [VBのVer]:VB.NET  
魔界の仮面弁士さん,具体的なコードをありがとうございます。
Marshal.IsComObjectやMarshal.ReleaseComObjectについて
よく調べてみます。
開放処理やエラー処理はいつも適当で.NET2005ではUsingが使える
ときはUsingで,それ以外はCloseがあればfinallyでCloseする位でした。
この機会に教えていただいたコードをよく勉強してみます。
安定した動作を得るには欠かせない知識ですが,自分が使う分にはまぁいいか
といつも手抜きをしていました。
小学生に使わせたいプログラムを作っているので,今回は気合を入れて
がんばってみようと思います。

ご指導ありがとうございました。

[ツリー表示へ]
タイトルRe^7: Webbrowser.Documentから...
記事No2539
投稿日: 2005/11/07(Mon) 14:17
投稿者YAS
[OSのVer]:Windows    [VBのVer]:VB.NET  
みなさんこんにちは。
随分前のスレッドをむしかえして申し訳ありません。

COMオブジェクトを利用するときには解放処理に気を遣わなければ
ならないということがわかりました。

VB2005からはUsingが使えるということで,COMオブジェクトをラップ
してみました。問題なく動作しているように見えるのですが,COMが
本当に解放されているのか,リークしていないのか確かめる方法が
よくわかりません。
いかがなものでしょうか?

    Class clsCOMObject
        Implements IDisposable

        Public COMObject As Object

        Sub New(ByVal COMObj As Object)
            COMObject = COMObj
        End Sub

        Private disposed As Boolean = False

        Private Overloads Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposed Then
                If disposing Then
                End If
                If Not COMObject Is Nothing AndAlso Marshal.IsComObject(COMObject) Then
                    Marshal.ReleaseComObject(COMObject)
                End If
            End If
            Me.disposed = True
        End Sub

        Public Overloads Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Protected Overrides Sub Finalize()
            Dispose(False)
            MyBase.Finalize()
        End Sub

    End Class

[ツリー表示へ]
タイトルRe^8: Webbrowser.Documentから...
記事No2540
投稿日: 2005/11/07(Mon) 15:45
投稿者魔界の仮面弁士
> VB2005からはUsingが使えるということで,COMオブジェクトをラップ
> してみました。問題なく動作しているように見えるのですが,COMが
> 本当に解放されているのか,リークしていないのか確かめる方法が
> よくわかりません。

厳密なチェックではありませんが、後述する XMLファイルを「C:\TEST.WSC」という
ファイル名で保存しておき、『Option Strict Off』のモードにて、
    Dim O As Object = GetObject("script:C:\TEST.WSC") '「Initialize」と表示されるはず。
    Dim X As New clsCOMObject(O)                      '今回作成したラッパーに渡す。
    MessageBox.Show(O())                              '「YAS」と表示される予定。
    X.Dispose()                                      '破棄により「Terminate」と表示される。
    'MessageBox.Show(O.Method())                      '破棄済みなので、例外が発生すれば OK 。
のようなコードでチェックしてみるとか。


<?xml version="1.0" encoding="UTF-8"?>
<package><component><public><method name="Method" dispid="0" /></public>
<script language="VBScript"><![CDATA[Option Explicit
Dim X
Function Method()
  Method = TypeName(X)
End Function
Set X = New YAS
Class YAS
  Private Sub Class_Initialize()
    MsgBox "Initialize", vbInformation
  End Sub
  Private Sub Class_Terminate()
    MsgBox "Terminate", vbInformation
  End Sub
End Class
]]></script></component></package>

[ツリー表示へ]