1

Googleの検索結果のURLでメールアドレスを検索するアプリを開発しています。問題は、各ページで見つかった値と電子メールを見つけたURLを、電子メールとURLの2つの列を持つdatagridviewに返す必要があることです。これにはParallel.ForEachを使用していますが、もちろんランダムなURLを返しますが、実際にメールを見つけたURLは返しません。

public static string htmlcon;  //htmlsource

public static List<string> emailList = new List<string>();

public static string Get(string url, bool proxy)
    {
        htmlcon = "";

        try
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            if (proxy)
                req.Proxy = new WebProxy(proxyIP + ":" + proxyPort);
            req.Method = "GET";
            req.UserAgent = Settings1.Default.UserAgent;
            if (Settings1.Default.EnableCookies == true)
            {
                CookieContainer cont = new CookieContainer();
                req.CookieContainer = cont;
            }
            WebResponse resp = req.GetResponse();
            StreamReader SR = new StreamReader(resp.GetResponseStream());
            htmlcon = SR.ReadToEnd();

            Thread.Sleep(400);
            resp.Close();
            SR.Close();
        }
        catch (Exception)
        {
            Thread.Sleep(500);
        }

        return htmlcon;

    }



  private void copyMails(string url)
    {    
        string emailPat = @"(\b[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b)";
        MatchCollection mailcol = Regex.Matches(htmlcon, emailPat, RegexOptions.Singleline);
        foreach (Match mailMatch in mailcol)
        {
            email = mailMatch.Groups[1].Value;
            if (!emailList.Contains(email))
            {
                emailList.Add(email);



                  Action dgeins = () => mailDataGrid.Rows.Insert(0, email, url);
                  mailDataGrid.BeginInvoke(dgeins);

             }
        }
     }

  private void SEbgWorker_DoWork(object sender, DoWorkEventArgs e)
 {
     //ALOT OF IRRELEVAMT STUFF BEING RUN

     Parallel.ForEach(allSElist.OfType<string>(), (s) =>
        {
            //Get URL
            Get(s, Settings1.Default.Proxyset);


            //match mails 1st page 
            copyMails(s);
            });

 }

これで、Getリクエスト(「s」はリストのURL)を実行してから、URLのhtmlソースからcopyMails(s)を実行します。正規表現を使用してメールをコピーします。並行して実行しないと、datagridview内の各電子メールの正しいURLが返されます。これを並行して行うにはどうすればよいですか?それでもdatagridviewで正しい一致を取得できますか?

ありがとう

4

2 に答える 2

7

WherePLINQを使用してフィルタリング(擬似コード)する方がよいでしょう。

var results = from i in input.AsParallel()
              let u = get the URL from i
              let d = get the data from u
              let v = try get the value from d
              where v is found
              select new {
                Url = u,
                Value = v
              };

その下にAsParallelは、TPLのLINQ演算子Select、、Where...)の実装が使用されます。


更新:より多くの情報があります

まず、コードにはいくつかの問題があります。

  1. 変数htmlconstatic、複数のスレッドによって直接使用されます。これが根本的な問題である可能性があります。2つの入力値だけを考えてください。最初のスレッドはGet設定を完了します。その後、2番目のスレッドを開始するためhtmlconのスレッドの呼び出しはHTML GETを完了し、に書き込みます。`メールでcopyMailsGethtmlcon

  2. このリストemailListには、複数のスレッドによってロックされることなくアクセスすることもできます。.NET(およびその他のプログラミングプラットフォーム)のほとんどのコレクションタイプはスレッドセーフではないため、一度に1つのスレッドへのアクセスを制限する必要があります。

  3. それぞれの方法でさまざまな活動を混同しています。単一責任の原則を適用することを検討してください。

  4. Thread.Sleep例外を処理するには?!例外を処理できない(つまり、条件を解決できない)場合は、何もしません。この場合、アクションがスローされると、Parallel.Foreachwillがスローされます。これは、HTMLGETの失敗を処理する方法を定義するまで実行されます。

3つの提案:

  1. 私の経験では、クリーンなコード(執拗な程度まで)は物事を簡単にします:フォーマットの詳細は重要ではありません(1つの真のブレーススタイルが優れていますが、一貫性が重要です)。フォーマットを確認してクリーンアップするだけで、問題#1と#2が発生しました。

  2. 良いネーミング。ドメインの重要な用語でない限り、数行を超えるコードで使用されているものを省略しないでください。例えば。s並列ループのアクションパラメータは実際にはURLなので、それを呼び出します。この種のことは、コードをすぐに理解しやすくします。

  3. 電子メールの正規表現について考えてみてください。一致しない有効な電子メールが多数あります(たとえば+、複数の論理アドレスを提供するための使用:ローカルルールexmaple+one@gamil.comに配信さexample@gmail.comれ、ローカルルールに使用できます)。また、アポストロフィ( " '")は有効な文字です(そして、これを間違えてアドレスを拒否したWebサイトに不満を感じている既知の人々)。

2番目:比較的直接的なクリーンアップ:

public static string Get(string url, bool proxy) {

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
    if (proxy) {
        req.Proxy = new WebProxy(proxyIP + ":" + proxyPort);
    }
    req.Method = "GET";
    req.UserAgent = Settings1.Default.UserAgent;
    if (Settings1.Default.EnableCookies == true) {
        CookieContainer cont = new CookieContainer();
        req.CookieContainer = cont;
    }
    using (WebResponse resp = req.GetResponse())
    using (StreamReader SR = new StreamReader(resp.GetResponseStream())) {
        return SR.ReadToEnd();
    }

}

private static Regex emailMatcher = new Regex(@"(\b[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b)", RegexOptions.Singleline);

private static string[] ExtractEmails(string htmlContent) {    

    return emailMatcher.Matches(htmlContent).OfType<Match>
                       .Select(m => m.Groups[1].Value)
                       .Distinct()
                       .ToArray();
 }

private void SEbgWorker_DoWork(object sender, DoWorkEventArgs e) {

    Parallel.ForEach(allSElist.OfType<string>(), url => {
        var htmlContent = Get(url, Settings1.Default.Proxyset);
        var emails = ExtractEmails(htmlContent);

        foreach (var email in emails) {
            Action dgeins = () => mailDataGrid.Rows.Insert(0, email, url);
            mailDataGrid.BeginInvoke(dgeins);
        }
}

ここに私が持っています:

  • ステートメントを利用しusingて、リソースのクリーンアップを自動化しました。
  • すべての変更可能な共有状態を排除しました。
  • Regexスレッドセーフなインスタンスメソッドを持つように明示的に文書化されています。したがって、必要なインスタンスは1つだけです。
  • ノイズの除去:ExtractEmails抽出ではURLが使用されないため、URLを渡す必要はありません。
  • GetHTML取得のみを実行しExtreactEMail、抽出のみを実行するようになりました

3番目:上記は、最も遅い操作であるHTMLGETでスレッドをブロックします。

HttpWebRequest.GetResponse本当の並行性の利点は、応答ストリームをそれらの非同期の同等のものに置き換えて読み取ることです。

.NET 4では使用Taskすることが答えになりますが、/メソッドのペアが提供されていないため、Stream自分で直接操作してエンコードする必要があります。しかし、.NET 4.5はほぼここにあるので、いくつかを適用します/ :StreamReaderBeginABCEndABCasyncawait

  • で何もしませんExtractEMails
  • Getは非同期になり、HTTPGETでブロックすることも結果を読み取ることもできません。
  • SEbgWorker_DoWorkタスクを直接使用して、TPLを操作するためのさまざまな方法が混ざりすぎないようにします。は単純に続行できるので(失敗していない場合–特に指定しない限り、前のタスクが正常に完了した場合にのみ続行します)GetTask<string>ContinueWith

これは.NET4.5で機能するはずですが、これが機能する有効なURLのセットがないと、テストできません。

public static async Task<string> Get(string url, bool proxy) {

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
    if (proxy) {
        req.Proxy = new WebProxy(proxyIP + ":" + proxyPort);
    }
    req.Method = "GET";
    req.UserAgent = Settings1.Default.UserAgent;
    if (Settings1.Default.EnableCookies == true) {
        CookieContainer cont = new CookieContainer();
        req.CookieContainer = cont;
    }
    using (WebResponse resp = await req.GetResponseAsync())
    using (StreamReader SR = new StreamReader(resp.GetResponseStream())) {
        return await SR.ReadToEndAsync();
    }

}

private static Regex emailMatcher = new Regex(@"(\b[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b)", RegexOptions.Singleline);

private static string[] ExtractEmails(string htmlContent) {    

    return emailMatcher.Matches(htmlContent).OfType<Match>
                       .Select(m => m.Groups[1].Value)
                       .Distinct()
                       .ToArray();
 }

private void SEbgWorker_DoWork(object sender, DoWorkEventArgs e) {

    tasks = allSElist.OfType<string>()
                     .Select(url => {
        return Get(url, Settings1.Default.Proxyset)
                .ContinueWith(htmlContentTask => {
                    // No TaskContinuationOptions, so know always OK here
                    var htmlContent = htmlContentTask.Result;
                    var emails = ExtractEmails(htmlContent);
                    foreach (var email in emails) {
                        // No InvokeAsync on WinForms, so do this the old way.
                        Action dgeins = () => mailDataGrid.Rows.Insert(0, email, url);
                        mailDataGrid.BeginInvoke(dgeins);
                    }
                });
    });

    tasks.WaitAll();
}
于 2012-07-21T13:14:07.853 に答える
0

public static string htmlcon; // htmlsource

public static List emailList = new List();

問題は、これらのメンバーhtmlconとemailListがスレッド間およびイテレーション間で共有リソースであるためです。Parallel.ForEachでの各反復は、並列で実行されます。それがあなたが奇妙な行動をする理由です。

問題を解決する方法:

  • コードを変更し、静的変数や共有状態なしで実装してみてください。

オプションはParallel.ForEachからTPLタスクチェーンに変更されるため、この変更を行うと、1つの並列操作の結果が他の入力データになり、共有状態を回避するためにコードを変更する多くの方法のオプションとして使用されます。

  • ロックまたは同時収集を使用します。htmlcon変数は揮発性にすることができますが、リストを使用すると、ロックまたは同時コレクションを実行する必要があります。

より良い方法は、共有状態を回避するようにコードを変更することです。その方法は、タスクチェーンだけでなく、実装に基づく多くのオプションです。

于 2012-07-23T13:20:14.993 に答える