我可以使用多线程和并行编程进行网页抓取吗?



我很难理解多线程和并行编程。我有一个小应用程序(Scraper)。我正在使用硒与c# . net。我有一个文件,其中包含地址从业务。然后我用我的刮板寻找公司名称和他们的网站。之后,我再根据他们的公司网站抓取通用电子邮件地址

问题在这里。如果我手工做这个,我需要3年才能完成5万份记录。我算过了。哈哈这就是我创建刮刀的原因。一个普通的控制台应用程序需要5到6天才能完成。然后,我决定也许使用多线程和并行编程可以减少时间。

所以,我做了一个小样本测试。我注意到一项记录需要10秒才能完成。然后10条记录花了100秒。我的问题是为什么多线程花了同样的时间?

我不确定我对多线程的期望和理解是否错误。我以为使用Parallel.ForEach可以启动所有10个记录,并在10秒内完成,为我节省90秒。这是正确的假设吗?有人能告诉我实际上多线程和并行编程是如何工作的吗?

private static List<GoogleList> MultiTreadMain(List<FileStructure> values)
{
List<GoogleList> ListGInfo = new List<GoogleList>();
var threads = new List<Thread>();
Parallel.ForEach (values, value =>
{
if (value.ID <= 10)
{
List<GoogleList> SingleListGInfo = new List<GoogleList>();
var threadDesc = new Thread(() =>
{
lock (lockObjDec)
{
SingleListGInfo = LoadBrowser("https://www.google.com", value.Address, value.City, value.State,
value.FirstName, value.LastName,
"USA", value.ZipCode, value.ID);
SingleListGInfo.ForEach(p => ListGInfo.Add(p));
}
});
threadDesc.Name = value.ID.ToString();
threadDesc.Start();
threads.Add(threadDesc);
}
});
while (threads.Count > 0)
{
for (var x = (threads.Count - 1); x > -1; x--)
{
if (((Thread)threads[x]).ThreadState == System.Threading.ThreadState.Stopped)
{
((Thread)threads[x]).Abort();
threads.RemoveAt(x);
}
}
Thread.Sleep(1);
}

return ListGInfo;
}

这可能不是您所面临的特定问题的答案,但它可能暗示了一般问题"为什么多线程不是更快"。假设Selenium有一个公共类EdgeDriver,实现方式如下:

public class EdgeDriver
{
private static object _locker = new();
public void GoToUrl(string url)
{
lock (_locker)
{
GoToUrlInternal(url);
}
}
internal void GoToUrlInternal(string url) //...
}

作为该类的消费者,您无法看到私有_locker字段或内部方法。这些是对您隐藏的实现细节,要知道这个类在做什么,唯一的方法是阅读文档。因此,如果实现看起来像上面的人为示例,那么通过创建多个EdgeDriver实例并在Parallel.ForEach循环中调用它们的GoToUrl方法来加速程序的任何尝试都将是徒劳的。静态对象上的lock将确保一次只允许一个线程调用GoToUrlInternal,而所有其他线程都必须等待轮到它们。这被称为` `,调用被序列化` `。而这只是为什么多线程可能不如单线程运行的代码快的众多可能原因之一。

我希望下面的代码片段能给你一些指导。我在文件结构列表中划分记录之间的工作。根据问题陈述,我认为这里没有必要加锁

private static List<GoogleList> MultiTreadMain(List<FileStructure> values)
{
var tasks = new List<Task<List<GoogleList>>>();
var toBeScraped = values.Where(p => p.Id >= 10);
Parallel.ForEach (toBeScraped, value =>
{
Task<List<GoogleList>> task = Task<List<GoogleList>>.Factory.StartNew(() =>
{
return ProcessRequestAsync(value);
});
tasks.Add(task);
});
var mergedTask = Task.WhenAll(tasks);
List<GoogleList> ListGInfo = new List<GoogleList>();

foreach(var item in mergedTask.GetAwaiter().GetResult())
{
ListGInfo.AddRange(item.GetAwaiter().GetResult());
}
return ListGInfo;
}
public static List<GoogleList> ProcessRequestAsync(FileStructure value)
{
List<GoogleList> SingleListGInfo = new List<GoogleList>();
SingleListGInfo = LoadBrowser("https://www.google.com", value.Address, value.City, value.State,
value.FirstName, value.LastName,
"USA", value.ZipCode, value.ID);
SingleListGInfo.ForEach(p => ListGInfo.Add(p));
return SingleListGInfo;
}

最新更新