这段时间对网络爬虫比较感兴趣,实现起来实际上比较简单。无非就是http的web请求,然后对返回的html内容进行内容筛选。本文的重点不在于这里,而在于多线程做http请求。例如我要实现如下场景:我有N个对象集合,需要通过http的方式获取每个对象的相关信息。废话不多说,直接上代码
实现方式一:依次循环遍历对象集合,这种方式最为普通
for (int i = 0; i < videoInfoList.Count; i++) { //普通方式 directRun(videoInfoList[i]); } private void directRun(VideoInfo item) { var htmlStr = GetHtmlCode(item.url); item.name= getName(htmlStr); videoInfoQueue.Enqueue(item); } private static string GetHtmlCode(string url) { string htmlCode; HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); webRequest.Timeout = 10000; webRequest.Method = "GET"; webRequest.UserAgent = "Mozilla/4.0"; webRequest.Headers.Add("Accept-Encoding", "gzip, deflate"); try { HttpWebResponse webResponse = (System.Net.HttpWebResponse)webRequest.GetResponse(); using (System.IO.Stream streamReceive = webResponse.GetResponseStream()) { using (var zipStream = new System.IO.Compression.GZipStream(streamReceive, System.IO.Compression.CompressionMode.Decompress)) { using (StreamReader sr = new System.IO.StreamReader(zipStream, Encoding.UTF8)) { htmlCode = sr.ReadToEnd(); } } } } catch { return null; } finally { // 释放资源 webRequest.Abort(); } return htmlCode; }
实现方式二:使用线程池,使用异步多线程的方式提高效率
在使用线程池的时候一定要注意设置ServicePointManager.DefaultConnectionLimit, 因为默认不设置是2,会导致同时的http请求只能是2个,因为这个问题我自己也卡了很久。使用队列管理,启动一个定时器线程,实时刷新显示获取到的数 据。实际的开发中,队列和线程池往往是一对组合出现。至于入队时候使用锁的问题,这里可以使用volatile也可以直接使用object锁,防止入队出错
//已经入队的数目 private int loadingNum = 0; //总数目 private int importNum = 0; //定义队列 private Queue<VideoInfo> videoInfoQueue = new Queue<VideoInfo>(); //锁 private object sb = new object(); ServicePointManager.DefaultConnectionLimit = 20; for (int i = 0; i < videoInfoList.Count; i++) { //多线程 ThreadPool.QueueUserWorkItem(multithreadingRun, videoInfoList[i]); Thread.Sleep(1); } private void multithreadingRun(object o) { VideoInfo item = o as VideoInfo; var htmlStr = GetHtmlCode(item.url); item.name = getName(htmlStr); //使用锁入队 lock (sb) { videoInfoQueue.Enqueue(item); } } //使用定时器进行出队显示 private void Timer1_Tick(object sender, EventArgs e) { if (videoInfoQueue.Count > 0) { VideoInfo item = videoInfoQueue.Dequeue(); label1.Text ++= item.name; } if (loadingNum == importNum) { timer1.Stop(); } }
至此,结束,本文也是我的第一篇博文,欢迎指教!