2015年2月16日月曜日

VB HTML の表をデータベースに取り込む

インターネット上には、有益な 表 の類が沢山あります。
でも、ページの作りで、カラム(縦罫)のソートが出来なかったり、余計な情報も多い為、
自分用に、表の内容を加工したいと思う事もありますね。
今回は、VB で、自分のデータベースとして保存するものを書いてみました。


HTML にお詳しければ、 カラム(縦罫)のソートは、それ用のスクリプトをソースコードに書き加えれば済みます。
データの保存も、ブラウザの ページを保存 で  完全版を残せば、取り敢えずは何とかなります。
でも、検索やソートや閲覧の自由度の観点からは、VB の様な言語での方が、柔軟性が高い気がします。

いやぁ、ここの処、あまり書いていなかったので、ウラシマ太郎状態 でした ... 。  ふぅ。.

但し、ここでの作業は、 始めに 表 ありき! です。
汎用性が高い書き方にはなっていません。

最初に、IDE 等で、元になる 表 を作らなければなりません。
謂わば、データベースの設計をしておいて、データの入力部分は、
インターネット上から、ダウンロードした、HTML ファイルを使って、自動入力する方法です。
また、ページの中に複数の表がある事は想定していません。
データ量が少なければ、コピーペーストや手入力で十分な場合だってありますし ... 。

元になる表が全てを左右する為、ここに全貌を示す事はしません。
HTML から、テーブル部分を抽出して、TD タグの内容を変数に取る 部分だけを示します。
この内容を元に、データベースへのレコード追加 部分を書き込む必要があります。

先ずは、元の HTML コードを。 
   説明用に、自分の Blog の過去記事から流用しました。 例としては、行数は かなり 少なめ。.

    <!DOCTYPE html>

    <html lang="jp" xmlns=http://www.w3.org/1999/xhtml>
    <head>
       <meta charset="utf-8" />
       <title>MyLinkList Local</title>
      <!--sorter js Load -->

       <body>
       My Link List<br />
       <br />

       <table id="sample" >
         <thead>
         <th width= "60">分類</th>
         <th width="120">区分</th>
         <th width="220">内容</th>
         <th width="150">LINK</th>
         <th width="250">Memo</th>
         </thead>
         <tbody>
           <tr>
           <td>検索</td>
           <td>検索 Google</td>
           <td>Google ホーム</td>
           <td><a href="http://www.google.co.jp/">Google Home</a></td>
           <td></td>
           </tr>
           <tr>
           <td>気象</td>
           <td>気象 台風</td>
           <td>台風情報</td>
           <td><a href="http://www.jma.go.jp/jp/typh/">台風情報 気象庁</a></td>
           <td>台風中心クリックで詳細</td>
           </tr>
           <!--*** 注: 説明の為、データ部分を省略しています。***-->
         </tbody>
       </table>

       <style>
          <!--*** 注: 説明の為、スクリプト部分を省略しています。***-->
       </style>

       </body>
    </html>

さて、以下のコードで、HTML からテーブルデータを抽出します。
実際には、データベース用に構成したデータセットをデータグリッドビューに表示し、追加していくのがいいと思います。
ここでは、便宜的に、テキストボックス(マルチライン)に吐き出しています。

Form1 に用意したのは、 Label1 / Button1 / WebBrowser1 / TextBox1 の4つです。

Public Class Form1
    'Test Program
    'Label1:FilePath / Button1:ReadHTML / WebBrowser1:HTML for DOM / TextBox1:MultiLines ShowReadingResult
    'TR TD Tags tr td OK UpperLower:NoError
    'HTML Table InnerText Catching   a Tag = Ref URL NoExecuting


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Width = 680
        Me.Height = 360
        Me.Label1.Location = New Point(12, 25)
        Me.Button1.Location = New Point(345, 20)
        Me.WebBrowser1.Size = New Point(408, 240)
        Me.WebBrowser1.Location = New Point(12, 70)
        Me.TextBox1.Multiline = True
        Me.TextBox1.Size = New Point(205, 240)
        Me.TextBox1.Location = New Point(440, 70)
        Me.TextBox1.ScrollBars = ScrollBars.Vertical


        Me.Button1.Text = "GetItem"
        Me.Label1.Text = "c:\users\UserName\Documents\TMP\test.htm"

        Dim winPath As String = Me.Label1.Text
        Dim u As New Uri(winPath)
        Me.WebBrowser1.Navigate(u)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim doc As HtmlDocument = Me.WebBrowser1.Document
        Dim elements As HtmlElementCollection
        Dim element As HtmlElement
        Dim thTags As HtmlElementCollection
        Dim tdTags As HtmlElementCollection
        Dim tdTag As HtmlElement
        Dim tdTagStr As String
        Dim tdTagStrs As String = ""
        Dim result As Boolean
        Dim resultPre As Boolean
        Dim counterRows As Integer = 0
        Const LineEnd As String = "---------------------"

        elements = doc.GetElementsByTagName("TR")

        For Each element In elements

            resultPre = element.InnerHtml.Contains("<TH")
            If resultPre Then
                thTags = element.GetElementsByTagName("TH")
                tdTagStrs = "TableColumns : " + Str(thTags.Count) + vbCrLf + LineEnd
            End If

            result = element.InnerHtml.Contains("<TD")
            If result Then
                counterRows = counterRows + 1
                tdTags = element.GetElementsByTagName("TD")
                Dim counterColumn As Integer = -1
                For Each tdTag In tdTags
                    counterColumn = counterColumn + 1
                    tdTagStr = tdTag.InnerText
                    tdTagStrs = tdTagStrs + vbCrLf + tdTagStr
                Next
                tdTagStrs = tdTagStrs + vbCrLf + LineEnd
            End If
        Next

        tdTagStrs = tdTagStrs + vbCrLf + LineEnd + " End" + vbCrLf + Str(counterRows) + " Lines"
        Me.TextBox1.Text = tdTagStrs
    End Sub

End Class

このコードを実行すると、
ロード時には HTML が左の WebBrowser1 に表示され、 Button1 でフラットテキストで TextBox1 に表示されます。
本来、表示に係る部分(デザイナのプロパティで設定)は 灰色 で表示しています。  (画面表示調整のみ)
また、抽出結果の表示部分は、 紫色 に.しました。
        尚、同じ変数に値を加えて行く場合の常套手段は StringBuilder なのですが、 
        ここでは例なので分り易さ重視 ?。  そのまま使う場合には、ちゃんと、直して下さいね。. 
この部分を 変数 counterColumn の値に応じて、 データセットの各カラムに追加して行く様に変えて下さい。

また、ここでは、 ファイル指定はコード内でハードコーディングしていますが、
実際には、拡張子を指定した ファイル選択ダイアログ が便利でしょうね。  事実、自分用のコードではそうしています。.

HTML コード自体も、実際のものは、 Script や Table (レイアウト用途で) が多用されていて、
お目当ての表からデータ抽出するには、 条件文を考えたり、 Script の実行を阻止したり、 そんな対策が必要です。
Table 自体も色々な記述方法があり、
TH タグではなく、TD タグの属性変更で見出しを表示していたり と様々なケースがあります。
TD タグも、ページ構成用にハイパーリンク目的のデータ行(本来不要)が付加されていたりします。
    条件文 : TD の項目数や内容で条件分岐 ・ Table に id や class が振ってあれば それで区別出来て楽
    Script の実行を阻止 : .Navigate(u) 前に、 Me.WebBrowser1.ScriptErrorsSuppressed = True
    その他(読込待機) : WebBrowser1.DocumentCompleted イベントハンドラ利用
  これらを加味すると、上記コードは、ブロック構成(Sub の中身も含)が変わると思います。.



ここで示した以外のアプローチ方法 としては、
HTML ソースから、データ部分を CSV (Comma-Separated Values) ファイルとして抽出して、一気に操作する方法もあります。
要は、HTML ソースのフラットテキストからの 抽出を どう考えるか ですね。
まぁ、一例として、ご覧下さい。



0 件のコメント:

コメントを投稿