WebBrowserで表のデータ収集

解決


フィート  2003-09-06 08:34:00  No: 108540

下のWebBrowserを使ったデータ取得の質問に興味を持ち自分もやってみようと思い
ランキングのデータ収集を考えたのですが

WebBrowser1.Document.body.innerTEXTだと

これが
<tr><td>順位</td><td>ID</td><td>点数</td></tr>
<tr><td>1</td><td>フィート</td><td>35</td></tr>

このように
順位ID点数
1フィート35

なってしまいます。
リンクは個別にデータを取得できることは分かりましたが、
表は無理ですか?


ねろ  2003-09-06 19:44:44  No: 108541

意味がちょっと判らないのですが、
順位 -> 1
ID   -> フィート
点数 -> 35
という表のデーターは取れていると思いますが。


ねろ  2003-09-06 21:35:59  No: 108542

すみません意味が判りました
WebBrowser.Document.body.outerhtml
でとってタグを解析したらどうでしょう。


花ちゃん  URL  2003-09-07 00:02:09  No: 108543

DAO を使ってHTMLファイルのテーブルデータを取得するのが簡単かと。


魔界の仮面弁士  2003-09-07 00:33:44  No: 108544

> 表は無理ですか?
とれますよ。
HTMLのTABLEエレメントは、DHTMLの「tableオブジェクト」として取得できます。

tableオブジェクトは、THEADエレメントを表す「tHeadプロパティ」と、TFOOTエレメントを表す「tFootプロパティ」、そして、TBODYエレメントのコレクションを表す「tBodiesプロパティ」を持っています。
さらに、tBodiesプロパティから「tBodiesコレクション」が得られ、そこから個々の「tBodyオブジェクト」を取得できます。

そして、table、tHead、tFoot、tBodyそれぞれのオブジェクトは、各行を表す「rowsプロパティ」を持っています。(これは、TRエレメントのコレクションを表しています)
また、rowsプロパティから「rowsコレクション」が得られ、そこから個々の「trオブジェクト」を取得できます。

で、trオブジェクトは、さらに、各セル…すなわちtd(あるいはth)エレメントを表す「cellsプロパティ」を持っています。

これらを使う事で、個々のセルの内容を容易に取得・設定できます。また、セルや行の追加や削除も可能です。

各プロパティ等については、下記を参考にしてください。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpisdk/dhtml/references/objects/objects.asp
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/objects/table.asp?frame=true


フィート  2003-09-07 04:39:23  No: 108545

WebBrowser.Document.body.outerhtmlでやろうとしたのですが直接とる方法はないものかと思い質問しました。

DAOで検索で調べましたが初心者に優しいサイトがなかったです・・(泣)

エレメントですけど、
こんな感じで書けばいいんでしょうけど、動く気配がないです。

Private Sub Form_Load()
      WebBrowser1.Navigate "http://www.yahoo.co.jp"
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
      Text1.Text = WebBrowser1.Document.body.Table.tBody.rows
End Sub


魔界の仮面弁士  2003-09-07 08:04:22  No: 108546

> DAOで検索で調べましたが初心者に優しいサイトがなかったです・・(泣)
DAO、あるいは、ADO + Jet OLEDB(Engine Type は &H80を指定)での取得は、
単純なHTML表の場合は有効ですが、結合列を含むような複雑な表の場合は
期待した結果にはならない事があります。汎用性という面ではお奨めしません。

> こんな感じで書けばいいんでしょうけど、動く気配がないです。
>      Text1.Text = WebBrowser1.Document.body.Table.tBody.rows
……考え方としては間違っていませんが、記述は正しくありませんね。(^^;

# まずは、先に掲載したページ(Internet SDK)を良く見てください。
# bodyオブジェクトに、Tableプロパティという物はありませんよ。
# (Internet SDKは、お手持ちのMSDNライブラリにも掲載されていると思います)

DHTMLにて、任意のエレメント(この場合はTABLEエレメント)を取得する方法は
幾つかありますが、代表的な物を幾つか挙げてみますと、
  getElementByIdメソッド
  getElementsByNameメソッド
  getElementsByTagNameメソッド
  allコレクション
などを使う方法が用意されています。

で、とりあえずまずは、TABLEエレメントを
  Dim objTable As Object
のような変数に Set してください。例えばこんな感じです。

    Dim objTables As Object
    Dim objTable  As Object

    Set objTables = Me.WebBrowser1.Document.getElementsByTagName("TABLE")
    Debug.Print "TABLEの数:", objTables.length
    If objTables.length > 0 Then
        Set objTable = objTables(0)    '最初のTABLEエレメントを取得
    End If

———しかし、もしもTable要素を取得できていたとしても、先の
>       Text1.Text = WebBrowser1.Document.body.Table.tBody.rows
というコードには、まだ問題が残されていたりします。

まず、このコードでは Table.tBodyというコードが書かれていますが、
私の先の回答には、「tBodyプロパティ」という記述は無かったはずです。
回答にあったのは、tBodiesプロパティでしたよね?

また、rowsプロパティが返すものは、「TRオブジェクトのコレクション」です。
コレクションをそのまま Text1.Text に表示する事はできないのです。(※1)

さて、ここで少し、HTMLテーブルの構造を説明させてください。(※2)

* HTMLの文法的には、TABLEエレメントは、その下に
*    0個または1個の<CAPTION>  ……表題部
*    0個以上の<COL>または<COLGROUP>  ……列定義部
*    0個または1個の<THEAD>  ……ヘッダ部
*    0個または1個の<TFOOT>  ……フッタ部
*    1個以上の<TBODY>  ……明細部
* というエレメントが、この順番で配置されていなければいけません。
*
* ただし、THEADエレメントもTFOOTエレメントもなく、かつTBODYエレメントが
* 一つしか含まれないテーブルの場合だけは、TBODYの記述を省略する事ができ、
* その場合だけは、TABLE の直下に TR が来る事ができる事になっています。

さて、上記のTABLE構造を踏まえた上で、VBのコーディングに話を戻します。

まず、この『1個以上の<TBODY>』を取得するための記述が、
先に回答した「tBodies」というプロパティとなります。

例えば、TABLE内の最初の TBODY を取得するためには、
    Dim objTBodies As Object
    Dim objTBody   As Object
    Set objTBodies = objTable.tBodies
    Debug.Print "TBODYの数:", objTBodies.length
    Set objTBody = objTBodies(0)
のようになります。(※3)

で、上記のように tBodyオブジェクト を取得した後は、
    Set objRows = objTBody.rows
    Debug.Print "TRの数:", objRows.length
    Set objRow = objRows(0)
という感じで、TBODYの下にある、最初のTRオブジェクトを取得できます。(※4)

同様に、cellsプロパティを使って、
    Set objCells = objRow.cells
    Debug.Print "セルの数:", objCells.length
    Set objCell = objCells(0)
という感じで、TRの下にある、最初のTD(またはTH)オブジェクトを取得できます。

あとは、このセルの innerText、innerHTML、outerHTML プロパティなどで
テキストを取得したり、あるいは firstChild、childNodex、allプロパティなどで
その下にある別のエレメントを拾うなどしていけば、望む物が出来ると思います。

例えば、HTML中に含まれる最初のTABLEエレメントを取得し、その一番左上のセルの
テキストの内容を取得する場合は、以下のようになります。
  Set objTables = Me.WebBrowser1.Document.getElementsByTagName("TABLE")
  Text1.Text = objTables(0).rows(0).cells(0).innerText

-----------
(※1) rowsコレクションを、そのまま表示する事はできない、と書きましたが、
実際には、DHTMLのほぼ全てのオブジェクトが、規定の「toStringメソッド」を
持っており、ここから自動的に"[object]"という文字列が返されるようになっています。
ですから、もしも先のコードが、正しくrowsコレクションを取得していたのならば、
   Text1.Text = objTBody.rows.toString()
のような意味として実行され、Text1には"[object]"という文字列が表示されます。

(※2) いちいち、HTMLの文法構造まで持ち出すのは冗長かとも思いましたが、
DHTMLは、HTMLをプログラムから操作できるような物なので、HTMLの構造を
理解しておいてもらった方が、解説しやすかったのです。

(※3) なお、『TABLEの直下に TBODYが無い場合(TABLEの直下にTRがある場合)』でも、
tBodiesプロパティを利用して、tBodyオブジェクトを取得することができます。
これはHTMLテーブルの構造の解説でも述べたように、TABLEの直下にTRがある場合というのが
すなわち、「TBODYの記述が省略されているだけの状態」であるため、結果として、
    objTable.tHead   → これは、Nothing を返す
    objTable.tFoot   → これも、Nothing を返す
    objTable.tBodies → これは、tBodyのコレクションオブジェクトを返す
    objTable.tBodies.length → 省略されたTBODYは1つだけなので、これは1を返す
のようなオブジェクト構造となるからです。

(※4) 同様に、Set objRows = objTable.rows という感じで、tableオブジェクトから、
直接TRオブジェクトのコレクションを取得する方法もあります。
tHead、tBody、tFootに対するrowsコレクションの場合は、それぞれの直下にあるTRのみを
その範囲としますが、tableオブジェクトのrowsコレクションの場合は、それら全てを
含んだ、表全体の行(trオブジェクト)を含んでいる事になります。


フィート  2003-09-07 21:54:41  No: 108547

ありがとうございます。ちょっと時間がかかりそうなのでじっくり読んでから来ます。


フィート  2003-09-10 08:15:43  No: 108548

↑読みきりました。何度か実験して全ての物を取得できそうな感じがしました。TableのなかのTableも取得できました。

Objectを使ったものは初めてなんですけど、下の式で開始ボタンを押すと必ず
「実行時エラー'91'
  オブジェクト変数またはWithブロック変数が設定されていません。」
と表示されます。デバックから再度、開始ボタンを押すとうまく表示されるのですが、exeファイルにしたときバグって使えません。なにかが足りないのは分かるのですが何が足りないのかわかりません。

Private Sub Form_Load()
WebBrowser1.Navigate "http://www.yahoo.co.jp/"
Dim objTables As Object
Dim objTable  As Object

Set objTables = WebBrowser1.Document.getElementsByTagName("TABLE")
    If objTables.length > 0 Then
        Text1.Text = objTables(1).rows(0).cells(0).innerTEXT
    End If
End Sub


フィート  2003-09-25 18:04:43  No: 108549

だれかお願いします。
ここのランキング取得したいです。
Private Sub Form_Load()
WebBrowser1.Navigate "http://www.e-typing.ne.jp/index.asp?mode=levelcheck&state=rank"

End Sub
Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)

    Dim objTables As Object
    Dim objTable As Object

    
    Set objTables = WebBrowser1.Document.getElementsByTagName("TABLE")

    If objTables.length > 0 Then
        Set objTable = objTables(12)
        Set objTables = objTable.getElementsByTagName("TABLE")
        For i = 1 To 50
            For j = 0 To 2
                Text1.Text = Text1.Text + objTables(1).rows(i).cells(j).innerTEXT + " "
            Next j
            Text1.Text = Text1.Text + Chr(13) + Chr(10)
        Next i
    End If
End Sub


フィート  2003-09-25 18:07:35  No: 108550

言い忘れていました。症状は上と同じように

Objectを使ったものは初めてなんですけど、下の式で開始ボタンを押すと必ず
「実行時エラー'91'
  オブジェクト変数またはWithブロック変数が設定されていません。」
と表示されます。デバックから再度、開始ボタンを押すとうまく表示されるのですが、exeファイルにしたときバグって使えません。なにかが足りないのは分かるのですが何が足りないのかわかりません。

です


すーさん  2003-09-25 18:22:22  No: 108551

コンパイルして実行してみましたが問題なく実行できてます。
コマンドボタンを押した後の処理はどうなってるのですか?


Take1  2003-09-25 19:39:35  No: 108552

たしかWebBrowser1_DocumentCompleteは完全に終了されてなくても
発行されることがあったと思います。
もしそれが原因なら

Private Sub Form_Load()
Me.Show
WebBrowser1.Navigate "http://www.e-typing.ne.jp/index.asp?mode=levelcheck&state=rank"

    Do While WebBrowser1.Busy
        DoEvents
    Loop

End Sub

としてみるか・・・

Private Sub WebBrowser1_ProgressChange(ByVal Progress As Long, ByVal ProgressMax As Long)
If Progress = -1 Then
    Dim objTables As Object
    Dim objTable As Object

    
    Set objTables = WebBrowser1.Document.getElementsByTagName("TABLE")

    If objTables.length > 0 Then
        Set objTable = objTables(12)
        Set objTables = objTable.getElementsByTagName("TABLE")
        For i = 1 To 50
            For j = 0 To 2
                Text1.Text = Text1.Text + objTables(1).rows(i).cells(j).innerTEXT + " "
            Next j
            Text1.Text = Text1.Text + Chr(13) + Chr(10)
        Next i
    End If

Set objTables = Nothing
Set objTable = Nothing

End If

End Sub

で、試してみてください。


Take1  2003-09-25 20:31:39  No: 108553

m(_ _)mすみません。
はやとちりです。
フィートさんと同じError発生しました。
Set objTables = objTable.getElementsByTagName("TABLE")
を取得してる最中にFor〜Nextがはじまってるようです。
ちょっと待機させる方法もありますが、もっといい方法が
あると思いますので・・。


Take1  2003-09-25 20:48:21  No: 108554

m(_ _)mまたまたすみません。

>Set objTables = objTable.getElementsByTagName("TABLE")を取得してる最中

ではなくて、objTables(1).rows(i)が50ないときに発生してました。

    Dim objTables As Object
    Dim objTable As Object

On Error Resume Next
    Set objTables = WebBrowser1.Document.getElementsByTagName("TABLE")
    Text1.Text = ""
    If objTables.length > 0 Then
        Set objTable = objTables(12)
        Set objTables = objTable.getElementsByTagName("TABLE")
        For i = 1 To 50
            For j = 0 To 2
                Text1.Text = Text1.Text + objTables(1).rows(i).cells(j).innerTEXT + " "
            Next j
            Text1.Text = Text1.Text + Chr(13) + Chr(10)
        Next i
    End If
If Err.Number Then Err.Clear
Set objTables = Nothing
Set objTable = Nothing

こんな感じでとりあえず回避できますか?


魔界の仮面弁士  2003-09-25 21:51:21  No: 108555

ふぅむ……開発環境で動くという事は、取得のタイミングなのでしょうかね。

とりあえず当方で、下記のコードをコンパイルして実行してみましたが、
今の所、エラーは発生していないようです。

Option Explicit

Private Sub Form_Load()
    WebBrowser1.Navigate "http://www.e-typing.ne.jp/index.asp?mode=levelcheck&state=rank"
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
    Dim objElement As Object
    Dim objRow     As Object
    Dim objCells   As Object
    
    Set objElement = WebBrowser1.Document.getElementsByName("1")(0)
    If objElement Is Nothing Then
        Exit Sub
    End If
    Do Until objElement.tagName = "TABLE"
        Set objElement = objElement.parentNode
    Loop
    
    Text1.Text = ""
    Text1.Visible = False
    For Each objRow In objElement.rows
        On Error Resume Next
        With objRow.cells
            AppendText .item(0).innerText
            AppendText vbTab
            AppendText .item(2).innerText
            AppendText vbTab
            AppendText .item(1).innerText
            AppendText vbNewLine
        End With
        On Error GoTo 0
    Next
    Text1.SelStart = 0
    Text1.Visible = True

    Set objElement = Nothing
End Sub

Private Sub AppendText(ByVal NewString As String)
    Text1.SelStart = Len(Text1.Text)
    Text1.SelText = NewString
End Sub


フィート  2003-09-27 04:13:38  No: 108556

どうもみなさまありがとうございます。なんとか出来そうです。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加