Parallel.For和web抓取问题

时间:2016-01-17 13:11:55

标签: c# web-scraping duplicates parallel.for

我正在尝试创建一个多线程Web scraper。为此我使用Parallel.For.Here是我的代码:

string source = "http://www.lolsummoners.com/ladders/eune/";
        string regex_Search = "leagues/.*>(.*)</a></td>";
        List<string> user_List = new List<string>();
        int page_Begin = 1;
        int page_End = 10;

        ParallelOptions p_Option = new ParallelOptions();
        p_Option.MaxDegreeOfParallelism = 3;
        Parallel.For(page_Begin, page_End, p_Option, index =>
            {
                try
                {
                    WebClient web = new WebClient();
                    web.Encoding = Encoding.UTF8;
                    String html = web.DownloadString(source + page_Begin);
                    MatchCollection matches = Regex.Matches(html, @regex_Search);
                    foreach(Match match_Find in matches)
                    {
                        string user = match_Find.Groups[1].Value.Replace(" ", string.Empty);
                        user_List.Add(user);
                        Console.WriteLine(user);
                    }
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                page_Begin++;
            });
        Console.ReadKey();

我的问题是,如果我使用多个线程,我会得到重复项。有没有办法解决这个问题?我不需要循环来从同一个网页获取相同的名称,那就是&#39;为什么我在最后增加page_Begin变量。这就是我所说的: Duplication issue

1 个答案:

答案 0 :(得分:1)

由于您未使用索引进行当前并行传递,因此您看到了重复项。相反,您使用之前定义的page_Begin。所以在两次传递期间至少有两个线程,两者都将开始在1下载!随着增量,后续传递将下载重复。

更改此行:

String html = web.DownloadString(source + page_Begin);

...至

String html = web.DownloadString(source + index );

并发困境

List<>不是线程安全的,因此您可能希望使用来自TPL的良好的线程安全集合,例如ConcurrentBag<>

MSDN:

  

袋子对于存储物品非常有用,当订购无关紧要时,与套装不同,袋子支持重复。 ConcurrentBag是一个线程安全的包实现,针对同一个线程生成和使用存储在包中的数据的场景进行了优化。

System.Collections.Concurrent中定义的集合都非常高效,并且比通常使用lock(object)编写的常规集合快得多。

改变这个:

List<string> user_List = new List<string>();

...为:

ConcurrentBag<string> user_List = new ConcurrentBag<string>();

现在您可以从任何线程添加到包中。

稍后当您完成线程处理时,可以将其转换为.ToArray()的数组。