当前位置: 首页 > 编程语言 > C#

批量下载网页C#分享

时间:2023-04-10 22:04:31 C#

批量下载网页C#我的应用需要我将大量网页下载到内存中,以便进一步解析和处理。最快的方法是什么?我目前的方法(如下所示)似乎太慢了,导致偶尔超时。for(inti=1;i<=pages;i++){stringpage_specific_link=baseurl+"&page="+i.ToString();}尝试{WebClientclient=newWebClient();varpagesource=client.DownloadString(page_specific_link);客户端.Dispose();sourcelist.Add(页面源);}catch(Exception){}}如何解决这个问题在很大程度上取决于您要下载的页面数量以及您引用的网站数量。我会使用像1,000这样的好数字。如果你想从一个站点下载那么多页面,这比你需要下载分布在几十个或数百个站点的1,000个页面要花费更长的时间。原因是,如果您访问具有大量并发请求的单个站点,您可能最终会被阻止。因此,您必须实施一种“礼貌政策”,即在单个网站上发出多个请求之间的延迟。延迟的长短取决于很多因素。如果您网站的robots.txt文件有抓取延迟条目,您应该尊重这一点。如果他们不希望您每分钟访问多个页面,这与您应该爬行的速度差不多。如果没有抓取延迟,您应该根据网站响应的时间来延迟。例如,如果您可以在500毫秒内从站点下载一个页面,则将延迟设置为X。如果需要整整一秒,则将延迟设置为2X。您可以将延迟限制为60秒(除非crawl-delay更长),我建议将最小延迟设置为5到10秒。我不推荐使用Parallel.ForEach。我的测试表明它做得不好。有时它会使连接负担过重,并且通常不允许足够的并发连接。我将创建一个WebClient实例队列并编写如下内容://CreatequeueofWebClientinstancesBlockingCollectionClientQueue=newBlockingCollection();//用一些WebClient实例初始化队列//现在处理urlsforeach(varurlinurls_to_download){varworker=ClientQueue.Take();worker.DownloadStringAsync(url,...);初始化排队的WebClient实例时,将其OnDownloadStringCompleted事件处理程序设置为指向已完成的事件处理程序。该处理程序应将字符串保存到文件中(或者您应该只使用DownloadFileAsync),然后客户端将自己添加回ClientQueue。在我的测试中,我已经能够使用此方法支持10到15个并发连接。除此之外,我在DNS解析方面遇到问题(“DownloadStringAsync”不会异步执行DNS解析)。您可以获得更多联系人,但这样做需要做很多工作。这是我过去采用的方法,它可以非常快速地下载数千页。尽管如此,这绝对不是我使用高性能网络爬虫的方法。我还应该注意到这两个代码块之间在资源使用方面的巨大差异:WebClientMyWebClient=newWebClient();foreach(varurlinurls_to_download){MyWebClient.DownloadString(url);}------------foreach(urls_to_download中的varurl){WebClientMyWebClient=newWebClient();MyWebClient.DownloadString(url);第一个为所有请求分配一个WebClient实例。第二个为每个请求分配一个WebClient。巨大差距。WebClient使用大量系统资源,在较短的时间内分配数千个资源会影响性能。相信我......我遇到了这个。您最好只分配10或20个WebClient(并行处理所需的数量),而不是为每个请求分配一个。为什么不使用网络爬虫框架。它可以处理所有你喜欢的东西(多线程、httprequests、解析链接、调度、礼貌等)。Abot(https://code.google.com/p/abot/)为您处理所有这些,并且是用c#编写的。除了@David的完全有效答案之外,我还想添加他的方法的一个稍微清晰的“版本”。varpages=newList{"http://bing.com","http://stackoverflow.com"};varsources=newBlockingCollection();Parallel.ForEach(pages,x=>{using(varclient=newWebClient()){varpagesource=client.DownloadString(x);sources.Add(pagesource);}});另一种使用异步的方法:staticIEnumerableGetSources(Listpages){varsources=newBlockingCollection();varlatch=newCountdownEvent(pages.Count);foreach(varpinpages){using(varwc=newWebClient()){wc.DownloadStringCompleted+=(x,e)=>{sources.Add(e.Result);闩锁信号();};wc.DownloadStringAsync(新Uri(p));}}latch.Wait();返回源;为此,您应该使用并行编程。有很多方法可以实现你想要的;最简单的是这样的:varpageList=newList();for(inti=1;i(pageList,(page)=>{try{WebClientclient=newWebClient();varpagesource=client.DownloadString(page);client.Dispose();lock(sourcelist)sourcelist.Add(pagesource);}catch(Exception){}});我有一个类似的案例,这是我的UsingSystem;usingSystem.Threading;usingSystem.Collections.Generic;usingSystem.Net;usingSystem.IO;namespaceWebClientApp{classMainClassApp{privatestaticintrequests=0;privatestaticobjectrequests_lock=newobject();publicstaticvoidMain(){Listurls=newList{"http://www.google.com","http://www.slashdot.org"};foreach(varurlinurls){ThreadPool.QueueUserWorkItem(GetUrl,url);}intcur_req=0;while(cur_req你应该考虑使用Paralel,因为速度慢是因为你的软件在等待对于I/O,为什么不在我等待I/O的线程开始时呢?虽然其他答案都是完全有效的,但所有这些答案(在撰写本文时)都缺少一些非常重要的东西:对网络的调用是IO绑定的,并让线程等待这样的操作会使系统资源紧张,并对您的系统资源影响造成负担。您真正想做的是利用WebClient类上的异步方法(正如一些人指出的那样)和TaskParallel库来处理基于事件的异步模式。首先,您将获得要下载的网址:IEnumerableurls=pages.Select(i=>newUri(baseurl+"&page="+i.ToString(CultureInfo.InvariantCulture)));然后你将为每个URL创建一个WebClient的新实例,它使用TaskCompletionSource类异步处理调用(这不会燃烧线程):IEnumerable>tasks=urls.Select(url=>{//创建任务完成源。vartcs=newTaskCompletionSource>();//Webclient.varwc=newWebClient();//附加到DownloadStringCompletedevent.client.DownloadStringCompleted+=(s,e)=>{//完成后处理客户端.using(wc){//如果有错误,则设置它。if(e.Error!=null){tcs.SetException(e.Error);}//否则,如果取消则设置取消。elseif(e.Cancelled){tcs.SetCanceled();}else{//设置结果tcs.SetResult(newTuple(url,e.Result));}}};//异步启动进程,不要烧一个thread.wc.DownloadStringAsync(url);//返回任务。returntcs.Task;});现在您有了IEnumerable,您可以转换为数组并使用Task.WaitAll等待所有结果://Materializethetasks.Task>materializedTasks=tasks.ToArray();//等待全部完成。Task.WaitAll(物化任务);然后,您可以使用Task实例上的Result属性来获取url和内容对://循环遍历每个results.foreach(MaterializedTasks.Select(t=>t.Result)中的元组对){//pair。Item1将包含Uri。//pair.Item2将包含内容。注意上面的代码有一个没有错误处理的警告如果你想要更大的吞吐量,而不是等待整个列表完成,你可以在下载完成后处理单个页面的内容;任务旨在像管道一样使用,当您完成工作单元时,让它继续下一个工作,而不是等待所有项目完成(如果它们可以以异步方式完成)。我正在使用活动线程数和任意限制:以上是C#学习教程:网页上C#分享的所有内容大量下载。如果对大家有用,需要详细了解C#学习教程,希望大家多加关注——privatestaticvolatileintactiveThreads=0;publicstaticvoidRecordData(){varnbThreads=10;varsource=db.ListOfUrls;//数千个urlvariterations=source.Length/groupSize;对于(inti=0;iRecordUri(item));//我想在这里等到处理更多数据以避免过载while(activeThreads>30)Thread.Sleep(100);}}privatestaticasyncTaskRecordUri(Uriuri){使用(WebClientwc=newWebClient()){Interlocked.Increment(refactiveThreads);wc.DownloadStringCompleted+=(sender,e)=>Interlocked.Decrement(refiterationsCount);varjsonData="";Root对象根;jsonData=awaitwc.DownloadStringTaskAsync(uri);varroot=JsonConvert.DeserializeObject(jsonData);RecordData(root)}}本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:

猜你喜欢