1

まず、このような素晴らしいコミュニティに感謝します。ここでの質問と回答から多くのことを学びました。これはSOに関する私の最初の質問なので、優しくしてください:)

わかりましたが、まず最初に:

-最初のコード バージョン:

private async void buttonWebScrap_Click(object sender, EventArgs e)
{
    ClickLink("/ptk/sun/core/cookie/CookiesHandler.accept");

    await Task.Delay(750);

    if (_backgroundTaskRunning || !ClickLink("msisdn-change")) return;

    _backgroundTaskRunning = true;
    await LongTaskAsync();
}

private async Task LongTaskAsync()
{
    const string previous = "msisdn-pool-prev";
    const string next = "msisdn-pool-next";

    var tempNumbers = new List<object>();
    
    while (true)
    {
        await Task.Delay(750);

        var document = webBrowser.DocumentText;
        var htmlDoc = new HtmlAgilityPack.HtmlDocument();
        htmlDoc.LoadHtml(document);

        var numbers = htmlDoc.DocumentNode.SelectNodes("//a[starts-with(@id, 'msisdn')]");

        tempNumbers.AddRange(from number in numbers
                             where number.Id != previous && number.Id != next
                             select number.InnerText.RemoveEnters().RemoveSpaces().ReplaceSpecificChars());
        tempNumbers.Add("-------------------------");

        if (tempNumbers.Count >= 24)
        {
            listBoxNumbers.Items.AddRange(tempNumbers.ToArray());
            tempNumbers.Clear();
        }
        
        if (ClickLink(next) == false)
        {
            break;
        }
    }
} 

private bool ClickLink(string linkId)
{
    if (webBrowser.Document != null)
    {
        var elementById = webBrowser.Document.GetElementById(linkId);

        if (elementById != null)
        {
            elementById.InvokeMember("click");
        }
        else
        {
            return false;
        }

        if (webBrowser.Document.Window != null)
        {
            webBrowser.Document.Window.ScrollTo(0, 480);
        }
    }
    else
    {
        return false;
    }

    return true;
}

-2 番目のコード バージョン:

private void MainForm_Load(object sender, EventArgs e)
    {
    _webBrowserDocuments = new ConcurrentQueue<string>();
    _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

    _progress = new Progress<string>();
    _progress.ProgressChanged += (o, s) => _objects.Add(s);

    _objects = new BindingList<string>();
    listBoxNumbers.DataSource = _objects;
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    WebBrowserEmulation.Delete();
}

private async void buttonWebScrap_Click(object sender, EventArgs e)
{
    await WebBrowserClickLinkAsync("/ptk/sun/core/cookie/CookiesHandler.accept");

    if (_backgroundTaskRunning || !(await WebBrowserClickLinkAsync("msisdn-change"))) return;

    await Task.Delay(5000);
    var cts = new CancellationTokenSource();

    await WebBrowserDocumentDownloadAsync(cts);
    await DocumentParseAsync(_progress, cts);

    _backgroundTaskRunning = true;
}

private async Task DocumentParseAsync(IProgress<string> progress, CancellationTokenSource cts)
{
    await Task.Factory.StartNew(() =>
        {
            while (true)
            {
                string tempDocument;
                if (_webBrowserDocuments.TryDequeue(out tempDocument))
                {
                    var htmlDoc = new HtmlAgilityPack.HtmlDocument();
                    htmlDoc.LoadHtml(tempDocument);

                    var numbers = htmlDoc.DocumentNode.SelectNodes("//a[starts-with(@id, 'msisdn')]");

                    foreach (var number in numbers.Where(number => number.Id != Previous && number.Id != Next).
                                                   Select(x => x.InnerText.RemoveEnters().RemoveSpaces().ReplaceSpecificChars()))
                    {
                        progress.Report(number);
                    }

                    progress.Report("-------------------------");
                }

                if (cts.IsCancellationRequested)
                {
                    break;
                }
            }
        }, cts.Token);
}

private async Task WebBrowserDocumentDownloadAsync(CancellationTokenSource cts)
{
    await Task.Factory.StartNew(async () =>
        {
            while (true)
            {
                await Task.Delay(1000);

                _webBrowserDocuments.Enqueue(webBrowser.DocumentText);

                if (await WebBrowserClickLinkAsync(Next)) continue;
                cts.Cancel();
                break;
            }
        }, new CancellationToken(), TaskCreationOptions.None, _uiScheduler);
}

private async Task<bool> WebBrowserClickLinkAsync(string linkId)
{
    return await Task.Factory.StartNew(() =>
        {
            if (webBrowser.Document != null)
            {
                var elementById = webBrowser.Document.GetElementById(linkId);

                if (elementById != null)
                {
                    elementById.InvokeMember("click");
                }
                else
                {
                    return false;
                }

                if (webBrowser.Document.Window != null)
                {
                    webBrowser.Document.Window.ScrollTo(0, 480);
                }
            }
            else
            {
                return false;
            }

            return true;
        }, new CancellationToken(), TaskCreationOptions.None, _uiScheduler);
}

最初はすべて問題なく動作していますが、約 500 の数値を Web スクレイピングした後、「GUI」が少し遅くなります。それが async/await パターンの「悪い」理解によるものなのか、それとも他の何かによるものなのかはわかりません。このタスクには 2 番目のバージョンの方が適していると思いましたが、それでもまだ遅いです :/. 誰かがこれで私を助けることができますか?

Web クライアントの代わりに Web ブラウザ コントロールを使用しているのはなぜですか? はるかに簡単になることはわかっていますが、Webスクレイピング元のサイトは(私が見ているように)Java(jsessionId)+ ajaxで作成されており、「適切な」リンクはありません。

さらに詳細が必要な場合は、書いてください ;)

前もって感謝します。

編集:

  • 2 番目のバージョンは、タスク (またはタスク) を返すメソッドを使用して、MainForm からの現在の SynchronizationContext での待機を簡素化します (そのうちの 2 つだけ)。

  • 最初のバージョンは、await/async を使用する最初のアプローチでした (LongTaskAsync() メソッドが await Task.Delay() と非同期であることがわかるように)。

  • これは完成したコードです (SynchronizationContext の取得、ListBox.DataSource を BindList に設定するなどの考えはありません)。

4

1 に答える 1

3

スローダウンは、ユーザー インターフェイスに値を追加したことが原因である可能性があります。

ループ中に、項目をリスト ボックスに追加しています。

    if (tempNumbers.Count >= 24)
    {
        listBoxNumbers.Items.AddRange(tempNumbers.ToArray());
        tempNumbers.Clear();
    }

結果が増えると、リスト ボックスの表示がボトルネックになり、速度が遅くなります。リスト ボックスは常に UI スレッドで更新する必要があるため、時間の経過とともに UI の応答が遅くなります。

BindingList<T>2 番目のオプションでは一度に 1 つのアイテムにアイテムを追加し、追加するたびに UI が更新されるため、2番目のオプションはさらに悪化する可能性があります。

これは、 VirtualModeが true に設定された ListView を使用することで軽減できます。これにより、新しい項目の追加によって画面が強制的に更新されるのを防ぐことができます。

于 2013-05-14T17:51:43.160 に答える