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

[ 最新記事及び返信フォームをトピックトップへ ]

■34953 / inTopicNo.1)  複数のPageに分かれる場合のスクレ―ピング
  
□投稿者/ Wan 一般人(45回)-(2021/11/13(Sat) 17:08:43)
  • アイコン環境/言語:[Windows10 VisualStudio2019 VB.net WindowsForm] 
    分類:[.NET] 

    Webで複数のPageに分かれたデータをWebClientを使用してデータを取得して、それぞれのPage数と合致したTextBox.Nameに表示しようとしています。(今回は、階層的にtaxtBoxのControlが配置しているので、子も検索するコードになっています)I/O待ちが主体の処理なので、Parallelで回すのでは無く、WebClient.OpenReadAsyncで処理するのが最適と思い次のようなコードを書きました。
    Public Class Form1
        Private Sub onBinaryLoad(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)
            If Not (e.[Error] IsNot Nothing) OrElse (e.Cancelled) Then
                DirectCast(Me.Controls.Find("TextBox" & e.UserState, True).First, TextBox).Text = New StreamReader(e.Result).ReadToEnd
            End If
        End Sub
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim 変数Max As Integer = 16
            For i = 1 To 変数Max
                Dim TmpWc = New WebClient
                AddHandler TmpWc.OpenReadCompleted, AddressOf onBinaryLoad
                TmpWc.OpenReadAsync(New Uri("https://XXXX&page=" & i), i)
            Next
        End Sub
    End Class
    提示したコードでは、Page数が16と限定していますが、実際は、不定です。
    
    不定数となる全てのPageの読み込みが完了した判断は、どのように行えば宜しいのでしょうか?
    詳しい方、いらっしゃいましたらご指南宜しくお願い致します。
    
    

マルチポストを報告
違反を報告
引用返信 削除キー/
■34954 / inTopicNo.2)  Re[1]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ 魔界の仮面弁士 大御所(1407回)-(2021/11/13(Sat) 17:23:23)
  • アイコンNo34953に返信(Wanさんの記事)
    > 不定数となる全てのPageの読み込みが完了した判断は、どのように行えば宜しいのでしょうか?

    まずはそのサイトの管理者に相談してみてください。

    「ページ数をどのように管理しているのか」は、それぞれの WebPage ごとに異なるため、
    汎用的な方法はありません。それぞれのサイトごとに適した方法を模索してください。

    最大ページ数をあらかじめ記載しているサイトもあれば、
    末尾ページを超えた URL を指定すると 404 Not Found を返すサイトもあれば、
    コンテンツが 0 個の空ページを表示するというサイトもありますし、
    あるいは無限スクロールかつ無限ページネーションなサイトだってありえるわけで。


    > 複数のPageに分かれる場合のスクレ―ピング

    「スクレ―ピング」ではなく
    「スクレーピング」もしくは
    「スクレイピング」ですね。

    最近の Windows のフォントは
    ― (エム・ダッシュ) と
    ー (長音記号) の違いが分かりにくい…。
違反を報告
引用返信 削除キー/
■34955 / inTopicNo.3)  Re[2]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ Wan 一般人(46回)-(2021/11/13(Sat) 22:47:00)
  • アイコン早速の御指南有難うございます!
    文章力が無く、質問の本質を伝えられず、お手数をお掛けして申し訳ありません!最大ページ数の取得方法の質問では無く、最大ページ数が、仮に16とした場合のコードを示したつもりでしたが、ベテラン様から見ると、最大ページ数の取得方法についての質問に見えると言う事は、プログラミングとしてでは無い意味で勉強になります!最大ページ数は、把握出来る環境にあります!webclientの非同期を実行した時に、コンプリートイベントが、複数発行される場合、全ての非同期処理が、全て完了した事を把握する事は出来ますか?
    仮に、最大ページ数が、16だった場合にどの様に全ての非同期処理が完了した事をが検出出来るのか?御指南頂ければ幸いです!
    文章力が無い為、意図が伝わらないかも?
    また、英単語のカタカナ表記については、発音の関係上、厳密な表記は不可能な事から、日本人ならこの程度は、伝わるだろうと、相手の事を考えずに書いた事をお詫び致します!
    なんだか?本質を外れたやり取りになってしまった気もしますが、お気を悪くなされる事が無い事を節に願います!


違反を報告
引用返信 削除キー/
■34956 / inTopicNo.4)  Re[3]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ Wan 一般人(47回)-(2021/11/14(Sun) 11:58:44)
  • アイコン
    次のようなコードで自己解決しました。
    お騒がせしてすみませんでした。
      Private Sub onBinaryLoad(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)
            If Not (e.[Error] IsNot Nothing) OrElse (e.Cancelled) Then
                DirectCast(Me.Controls.Find("TextBox" & e.UserState, True).First, TextBox).Text = New StreamReader(e.Result).ReadToEnd
            End If
        End Sub
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            ServicePointManager.DefaultConnectionLimit = 8
            Dim 変数Max As Integer = 16
            Dim WcList As New List(Of WebClient)
            For i = 1 To 変数Max
                Dim TmpWc = New WebClient
                WcList.Add(TmpWc)
                AddHandler TmpWc.OpenReadCompleted, AddressOf onBinaryLoad
                TmpWc.OpenReadAsync(New Uri("https:XXXX&page=" & i), i)
            Next
            Do
                Application.DoEvents()
            Loop Until WcList.All(Function(n) n.IsBusy = False)
        End Sub

解決み!
違反を報告
引用返信 削除キー/
■34957 / inTopicNo.5)  Re[4]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ 魔界の仮面弁士 大御所(1408回)-(2021/11/15(Mon) 11:28:52)
  • アイコンNo34956に返信(Wanさんの記事)
    > Dim TmpWc = New WebClient

    WebClient は幾つかの理由により、現在は非推奨なクラスとなっています。
    https://docs.microsoft.com/en-us/dotnet/api/system.net.webclient?view=netframework-4.8
    https://docs.microsoft.com/ja-jp/dotnet/core/compatibility/networking/6.0/webrequest-deprecated

    >> Important
    >> We don't recommend that you use the WebClient class for new development. Instead, use the System.Net.Http.HttpClient class.

    代わりに HttpClient を使えば、Task ベースの非同期実装を利用できるので、
    WhenAll を使って「全てのページが終わった後」の処理を簡単に書けます。


    取りあえず今回は WebClient で行くとして…。


    > If Not (e.[Error] IsNot Nothing) OrElse (e.Cancelled) Then
    二重否定文になっていますので、打ち消して
    「If e.Error Is Nothing OrElse e.Cancelled Then」
    と書いた方が素直だと思います。

    でも、この判定処理はそもそもおかしく無いでしょうか。もしかして、
    「If Not (e.Error IsNot Nothing OrElse e.Cancelled) Then」あるいは
    「If e.Error IsNot Nothing AndAlso Not e.Cancelled Then」と書きたかったのでは?


    > Do
    >  Application.DoEvents()
    > Loop Until WcList.All(Function(n) n.IsBusy = False)
    これだと、ループ中に再度ボタンが押されてしまうこともありそうです。

    なによりも、「イベント通知型」の非同期実装であるにもかかわらず、
    それをループ待機してしまっては台無しです。

    ループ待機で「Loop Unitil 最後のページのイベント処理が完了した時」とするのではなく、
    OpenReadCompleted イベントのハンドラーである onBinaryLoad にて、
      If 最後のページのイベント処理が完了した時 Then
        '全て完了した時に動作させたい処理
      End If
    のように実装するようにして、Button1 そのものは
    ループ待機せず、そのまま End Sub に向かって抜けてしまうようにします。


    今回の場合、OpenReadAsync 時に UserToken として 1〜16 の固有値を
    渡しているわけですから、onBinaryLoad 側では CInt(e.UserState) を通じて
    何ページ目のロードが完了したのかも分かるはずですよね。
解決み!
違反を報告
引用返信 削除キー/
■34958 / inTopicNo.6)  Re[5]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ 魔界の仮面弁士 大御所(1409回)-(2021/11/16(Tue) 10:49:26)
  • アイコンNo34957に追記(魔界の仮面弁士の記事)
    > WebClient は幾つかの理由により、現在は非推奨なクラスとなっています。
    > 代わりに HttpClient を使えば、Task ベースの非同期実装を利用できるので、
    > WhenAll を使って「全てのページが終わった後」の処理を簡単に書けます。

    HttpClient + WhenAll のサンプルです。

    "cbbs.cgi?page=-20" 〜 "cbbs.cgi?page=80" までの 6 ページを読み込むので
    Multiline = True な TextBox を 6 個と Button1 を用意してください。


    Option Strict On
    Imports System.Net.Http
    Public Class Form1
      Private textBoxes As New List(Of TextBox)()

      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        textBoxes.AddRange({TextBox1, TextBox2, TextBox3, TextBox4, TextBox5, TextBox6})
      End Sub

      Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Button1.Enabled = False

        Dim queue As New List(Of Task)()
        For p = 0 To 5
          Dim url = $"https://dobon.net/cgi-bin/vbbbs/cbbs.cgi?page={20 * (p - 1)}&H=F&no=0"
          Dim txt = textBoxes(p)
          Dim http As New HttpClient()
          queue.Add(http.GetStringAsync(url).ContinueWith(
            Sub(html) txt.Text = $"{url}{vbCrLf}{html.Result}",
            TaskScheduler.FromCurrentSynchronizationContext()))
        Next
        Await Task.WhenAll(queue)

        Button1.Enabled = True
      End Sub
    End Class
違反を報告
引用返信 削除キー/
■34968 / inTopicNo.7)  Re[6]: 複数のPageに分かれる場合のスクレ―ピング
□投稿者/ Wan 一般人(49回)-(2021/11/21(Sun) 14:22:24)
  • アイコン追加で、丁寧なご説明ありがとうございます。
    継続タスク、スケジュールなどの要素が含まれており物凄く勉強になりました。
    なんとなくですが、Taskという考え方、HttpClientが4.5から追加されて意味が見えてきたような気がしています。
    非同期処理は、始めたばかりなのでわからないことだらけで、くだらない質問ばかりで申し訳ありません。
    本当に有難うございました
解決み!
違反を報告
引用返信 削除キー/



トピック内ページ移動 / << 0 >>

このトピックに書きこむ

Mode/  Pass/


- Child Tree -