DOBON.NETプログラミング道掲示板

ツリー一括表示

Nomalアイコン WebView2によるスクレ―ピング /Wan (23/11/10(Fri) 17:06) #35535
Nomalアイコン Re[1]: WebView2によるスクレ―ピング /Wan (23/11/10(Fri) 17:16) #35536
Nomalアイコン Re[1]: WebView2によるスクレ―ピング /魔界の仮面弁士 (23/11/11(Sat) 22:04) #35538
  └Nomalアイコン Re[2]: WebView2によるスクレ―ピング /Hongliang (23/11/11(Sat) 18:36) #35537
    └Nomalアイコン Re[3]: WebView2によるスクレ―ピング /Wan (23/11/12(Sun) 10:51) #35539 解決み!


親記事 / ▼[ 35536 ] ▼[ 35538 ]
■35535 / 親階層)  WebView2によるスクレ―ピング
□投稿者/ Wan 付き人(81回)-(2023/11/10(Fri) 17:06:14)
  • アイコン環境/言語:[VisualBasic2019 Windows10 Basic Framework4.7.2] 
    分類:[.NET] 

    WebView2を使って、スクレ―ピングを考えています。クラス名でTableを抽出しtrとtdで構成された表をJavaScriptで二次元配列に代入し、returnで返してたつもりです。が、DataTableの変数で受け取れません。途方に暮れています。どなたか?詳しい方いらっしゃいましたら教えて頂けないでしょうか?
    スクレ―ピングについては、複数ページに渡るので、WebView2_NavigationCompletedが発生するたびに、DataTableに追加し続けるコードにしたいのが最終目標です。

    Dim DataTable_Scraping As New DataTable
    DataGridView1.DataSource = DataTable_Scraping

    Private Async Sub WebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles WebView21.NavigationCompleted
    Dim js As New System.Text.StringBuilder
    js.AppendLine("const hyou=[];let i=0;")
    js.AppendLine("var TrElems = document.getElementsByClassName('Tableが含まれるクラス名')[0].getElementsByTagName('tr');")
    js.AppendLine("Array.prototype.forEach.call(TrElems, function(TrElem) {")
    js.AppendLine(" hyou.push([]);")
    js.AppendLine(" var TdElems = TrElem.getElementsByTagName('td');")
    js.AppendLine(" Array.prototype.forEach.call(TdElems, function(TdElem) {")
    js.AppendLine(" hyou[i].push(TdElem.textContent);")
    js.AppendLine(" });")
    js.AppendLine(" i=i+1;")
    js.AppendLine("});")
    js.AppendLine("return hyou;")

    DataTable_Scraping = Await WebView21.ExecuteScriptAsync(js.ToString())
    '読み込み結果を判定
    If e.IsSuccess Then
         ‘ここで次のページへの移動用の要素をクリックしている。動作確認済み
         ‘getElementsByClassName('****')の要素数が1の場合は、終わり
    Await WebView21.ExecuteScriptAsync(
    "document.getElementsByClassName('****')[1].getElementsByTagName('a')[0].click();")
    Else
    Console.WriteLine(e.WebErrorStatus)
    End If
    End Sub

違反を報告
[ □ Tree ] 返信 削除キー/

▲[ 35535 ] / 返信無し
■35536 / 1階層)  Re[1]: WebView2によるスクレ―ピング
□投稿者/ Wan 付き人(82回)-(2023/11/10(Fri) 17:16:18)
  • アイコン追伸

    return hyou;の部分を
    console.table(hyou)とすると
    Edgeの開発者ツール-コンソールで動かして表として表示されますので、
    JavaScript上では、表形式になっているようです。

    また、varをletやconstに変えると、Edgeのコンソール上でも、上手く動かない理由も教えて頂ければ幸いです。



違反を報告
[ 親 35535 / □ Tree ] 返信 削除キー/

▲[ 35535 ] / 返信無し
■35538 / 1階層)  Re[1]: WebView2によるスクレ―ピング
□投稿者/ 魔界の仮面弁士 大御所(1566回)-(2023/11/11(Sat) 22:04:19)
  • アイコン
    No35535に返信(Wanさんの記事)
    > スクレ―ピング
    「スクレーピング」が
    「スクレ―ピング」になっていて
    似非日本語感を微妙に覚えるなど(
    
    
    > WebView2を使って、スクレ―ピングを考えています。
    RSS をクロールするだけでで要件を満たせるなら、
    XDocument だけで簡単に済むのですけれどね…。
    ひとまず、ここの掲示板の RSS を拾ってみた例。
    
    
    Imports System.Xml.Linq
    Public Class Form1
        Private WithEvents dgv As DataGridView
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            dgv = New DataGridView() With {.Dock = DockStyle.Fill, .ReadOnly = True, .AllowUserToAddRows = False}
            Controls.Add(dgv)
            Dim doc = XDocument.Load("https://dobon.net/cgi-bin/vbbbs/rss.cgi?ver=2.0")
            Dim items = From item In doc...<item>
                        Select item.<title>.Value,
                            item.<link>.Value,
                            pubDate = Date.Parse(item.<pubDate>.Value),
                            item.<description>.Value
            dgv.DataSource = items.ToArray()
        End Sub
    End Class
    
    
    こちらは、Web ページからスクレイピングする場合。
    
    Imports Microsoft.Web.WebView2.Core
    Imports Microsoft.Web.WebView2.WinForms
    Public Class Form1
        Private WithEvents wv As WebView2
        Private WithEvents ds As DataSet
        Private WithEvents tbl As DataTable
        Private WithEvents dgv As DataGridView
    
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            ds = New DataSet()
            tbl = ds.Tables.Add("dobon")
            wv = New WebView2 With {.Visible = False}
            dgv = New DataGridView() With {.Dock = DockStyle.Fill, .ReadOnly = True, .AllowUserToAddRows = False}
            dgv.DataSource = tbl
    
            tbl.PrimaryKey = New DataColumn() {tbl.Columns.Add("Id", GetType(Integer))}
            tbl.Columns.Add("Solved", GetType(Boolean)).DefaultValue = False
            tbl.Columns.Add("Title")
            tbl.Columns.Add("Category")
            tbl.Columns.Add("FirstAuthor")
            tbl.Columns.Add("FirstPostAt")
            tbl.Columns.Add("LastAuthor")
            tbl.Columns.Add("LastPostAt")
            Controls.AddRange(New Control() {dgv, wv})
    
            Await wv.EnsureCoreWebView2Async()
            'wv.CoreWebView2.Navigate("https://dobon.net/cgi-bin/vbbbs/rss.cgi?ver=2.0")
            wv.CoreWebView2.Navigate("https://dobon.net/cgi-bin/vbbbs/cbbs.cgi?H=F&no=0")
        End Sub
        Private Async Sub wv_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles wv.NavigationCompleted
            Dim js = "(()=>{
                const table=[];
                document.querySelectorAll('TABLE.topiclist').forEach(t=>{
                    [...t.rows].slice(1).forEach(tr=>{
                        const cols=[...tr.cells];
                        const d=[];
                        d[0]=cols[1].querySelector('small>font').innerText.substr(1)*1;
                        d[1]=cols[6].innerText.includes('済');
                        d[2]=cols[1].firstChild.innerText;
                        d[3]=cols[0].innerText;
                        d[4]=cols[3].innerText;
                        d[5]=cols[1].querySelector('small').lastChild.substringData(5,16);
                        d[6]=cols[4].innerText
                        d[7]=cols[5].innerText;
                        table.push(d);
                    });
                });
                return table;
            })();"
    
            Dim result = Await wv.CoreWebView2.ExecuteScriptAsync(js)
            Dim ary = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object()())(result)
            ds.EnforceConstraints = False
            Array.ForEach(ary, AddressOf tbl.Rows.Add)
            ds.EnforceConstraints = True
        End Sub
    End Class

違反を報告
[ 親 35535 / □ Tree ] 返信 削除キー/

▲[ 35536 ] / ▼[ 35539 ]
■35537 / 2階層)  Re[2]: WebView2によるスクレ―ピング
□投稿者/ Hongliang 大御所(646回)-(2023/11/11(Sat) 18:36:02)
  • アイコンVisual Studio 2015から複数行リテラルを扱えるようになったので、文字列が固定であれば変にStringBuilder使うよりリテラルで書いた方が見やすいしパフォーマンスも良いです。

    ExecuteScryptAsyncで実行されるスクリプトは、最後の式を評価してそれを返値とするような挙動になります。
    なので、変数hyouの内容を返値としたいのであれば、単に変数を記述します。
    × return hyou;
    ○ hyou;
    あるいは、以下のように無名関数の呼び出しに全体を変換する方法もあります。
    js = "(function() {
    const hyou = [];
    色々処理する;
    return hyou; })()"

    https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.executescriptasync?view=webview2-dotnet-1.0.2088.41
    ExecuteScriptAsyncの返値(Await結果)は文字列、String型です。
    当然ながらDataTable型の変数に直接代入することはできません。
    この文字列はスクリプトの結果をJSON.stringify()したJSON形式なので、一般的にはデシリアライズして.NET上のオブジェクトとして扱います。
    JSONのデシリアライザとしては、System.Text.JsonでもJson.NETでもお好みのものを使えばいいでしょう。.NET 4.xで組み込みなのはDataContractJsonSerializerというのがありましたっけ。
    ざっと見た様子では、JavaScript上では「「文字列の配列」の配列」のようなので、VB上ではString型の配列の配列、つまり String()() 型としてデシリアライズできます。
    DataTableへはこのString()()から手動で移し替えてください。

    > また、varをletやconstに変えると、Edgeのコンソール上でも、上手く動かない理由も教えて頂ければ幸いです。
    ExecuteScriptAsycで渡したJavaScript内で定義したりした変数はそのまま残ります。
    constやletで宣言した変数は再宣言不可能なので、もう一度ExecuteScriptAsyncしたりするとエラーになります。
    ちなみにconstにせよletにせよブロックスコープなので、上の方で示した無名関数を使う場合、
    (function() { const hyou = []; ... })()
    このhyouはこの無名関数内でのみ有効なので、再度呼び出しても問題ありません。

    何度も使用するJavaScript関数なら、
    CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync
    で登録しておけば便利です。
違反を報告
[ 親 35535 / □ Tree ] 返信 削除キー/

▲[ 35537 ] / 返信無し
■35539 / 3階層)  Re[3]: WebView2によるスクレ―ピング
□投稿者/ Wan 付き人(83回)-(2023/11/12(Sun) 10:51:31)
  • アイコンHongliang様
    いつも、丁寧なご指導ありがとうございます。

    ご指摘のように、
    retrn hyou → hyouにしたら返り値が取れました。

    教えて頂いた内容を熟読して、取得した返り値を加工していきたいと思います。

    ありがとうございました。
解決み!
違反を報告
[ 親 35535 / □ Tree ] 返信 削除キー/


Mode/  Pass/


- Child Tree -