CodeProject上看见的感兴趣的文章,先研究着,有空翻译一下: 简介 网页爬虫(也被称做蚂蚁或者蜘蛛)是一个自动抓取万维网中网页数据的程序.网页爬虫一般都是用于抓取大量的网页,为日后搜索引擎处理服务的.抓取的网页由一些专门的程序来建立索引(如:Lucene,DotLucene),加快搜索的速度.爬虫也可以作为链接检查器或者HTML代码校验器来提供一些服务.比较新的一种用法是用来检查E-mail地址,用来防止Trackback spam. 爬虫概述 在这篇文章中,我将介绍一个用C#写的简单的爬虫程序.这个程序根据输入的目标URL地址,来有针对性的进行网页抓取.用法相当简单,只需要输入你想要抓取的网站地址,按下"GO"就可以了. 这个爬虫程序有一个队列,存储要进行抓取的URL,这个设计和一些大的搜索引擎是一样的.抓取时是多线程的,从URL队列中取出URL进行抓取,然后将抓取的网页存在指定的存储区(Storage 如图所示).用C#Socket库进行Web请求.分析当前正在抓取的页面中的链接,存入URL队列中(设置中有设置抓取深度的选项) 状态查看 这个程序提供三种状态查看:
线程查看 . . 请求查看 请求查看显示了所有的最近下载的页面的列表,也显示了HTTP头中的详细信息. 每个请求的头显示类似下面的信息: GET / HTTP/1.0 Host: www.cnn.com Connection: Keep-Alive Response头显示类似如下信息: HTTP/1.0 200 OK Date: Sun, 19 Mar 2006 19:39:05 GMT Content-Length: 65730 Content-Type: text/html Expires: Sun, 19 Mar 2006 19:40:05 GMT Cache-Control: max-age=60, private Connection: keep-alive Proxy-Connection: keep-alive Server: Apache Last-Modified: Sun, 19 Mar 2006 19:38:58 GMT Vary: Accept-Encoding,User-Agent Via: 1.1 webcache (NetCache NetApp/6.0.1P3) 还有一个最近下载的页面的列表 Parsing page Found: 356 ref(s) http://www.cnn.com/ http://www.cnn.com/search/ http://www.cnn.com/linkto/intl.html 设置 这个程序提供一些参数的设置,包括:
文件类型 爬虫支持的下载下来的文件类型,用户可以添加MIME类型,支持可下载的文件包括一个默认类型用来用户可添加,编辑和删除MIME类型. 用户可以选择让所有MIME类型为下列数字. 输出 输出设定包括下载文件夹, 而请求的数目应保持在要求查看复审请求细节. 连接 连线设定包含:
高级 高级设置:
保持活动连接: GET /CNN/Programs/nancy.grace/ HTTP/1.0 Host: www.cnn.com Connection: Keep-Alive "连接:保活"告诉服务器不会关闭连接, 但服务器的选择,把它打开或关闭它, 但应回复到客户端socket的决定. 所以服务器可以不断告诉客户,他将它打开了包括"连接: 保活"在他的replay如下: HTTP/1.0 200 OK Date: Sun, 19 Mar 2006 19:38:15 GMT Content-Length: 29025 Content-Type: text/html Expires: Sun, 19 Mar 2006 19:39:15 GMT Cache-Control: max-age=60, private Connection: keep-alive Proxy-Connection: keep-alive Server: Apache Vary: Accept-Encoding,User-Agent Last-Modified: Sun, 19 Mar 2006 19:38:15 GMT Via: 1.1 webcache (NetCache NetApp/6.0.1P3) 或者也可以告诉客户它拒绝如下: HTTP/1.0 200 OK Date: Sun, 19 Mar 2006 19:38:15 GMT Content-Length: 29025 Content-Type: text/html Expires: Sun, 19 Mar 2006 19:39:15 GMT Cache-Control: max-age=60, private Connection: Close Server: Apache Vary: Accept-Encoding,User-Agent Last-Modified: Sun, 19 Mar 2006 19:38:15 GMT Via: 1.1 webcache (NetCache NetApp/6.0.1P3) WebRequest and WebResponse 问题: 当我开始这篇文章典我所用webrequest类以及webresponse像以下代码: WebRequest request = WebRequest.Create(uri); WebResponse response = request.GetResponse(); Stream streamIn = response.GetResponseStream(); BinaryReader reader = new BinaryReader(streamIn, TextEncoding); byte[] RecvBuffer = new byte[10240]; int nBytes, nTotalBytes = 0; while((nBytes = reader.Read(RecvBuffer, 0, 10240)) > 0) { nTotalBytes += nBytes; } reader.Close(); streamIn.Close(); response.Close(); 本程序运作良好,但它有一个非常严重的问题,因为webrequest类函数getresponse门锁进入 所有其他进程webrequest告诉纳莉反应关闭的最后一道防线,在前面的代码. 等我看到总是一个线程下载,而其他人正等着getresponse. 要解决这个严重的问题,我有我的执行两班mywebrequest和mywebresponse. mywebrequest和mywebresponse使用Socket类来管理连接,而是类似webrequest和webresponse但却支持 并行反应在同一时间. 此外mywebrequest支持建旗keepalive支持keep-alive连接. 所以,我的新的代码whould像: request = MyWebRequest.Create(uri, request/*to Keep-Alive*/, KeepAlive); 刚刚更换getresponsestream与直接获取套接字会员mywebresponse阶层. 这样做,我做了简单的伎俩,使socket读明年开始后的头回答, 读一字节的时间告诉header完成,如下列代码: MyWebResponse response = request.GetResponse(); byte[] RecvBuffer = new byte[10240]; int nBytes, nTotalBytes = 0; while((nBytes = response.socket.Receive(RecvBuffer, 0, 10240, SocketFlags.None)) > 0) { nTotalBytes += nBytes; if(response.KeepAlive && nTotalBytes >= response.ContentLength && response.ContentLength > 0) break; } if(response.KeepAlive == false) response.Close(); /* reading response header */ 因此,用户myresponse类只会继续接收从第一位置的页面. 线程管理:线索数目的履带式是指用户通过设置. 它的默认值是10线程,但它可改变的设置选项连接. 履带代码处理这种改变的财产threadcount如下列代码: Header = ""; byte[] bytes = new byte[10]; while(socket.Receive(bytes, 0, 1, SocketFlags.None) > 0) { Header += Encoding.ASCII.GetString(bytes, 0, 1); if(bytes[0] == '/n' && Header.EndsWith("/r/n/r/n")) break; } private int ThreadCount { get { return nThreadCount; } set { Monitor.Enter(this.listViewThreads); for(int nIndex = 0; nIndex < value; nIndex ++) { if(threadsRun[nIndex] == null || threadsRun[nIndex].ThreadState != ThreadState.Suspended) { threadsRun[nIndex] = new Thread(new ThreadStart(ThreadRunFunction)); threadsRun[nIndex].Name = nIndex.ToString(); threadsRun[nIndex].Start(); if(nIndex == this.listViewThreads.Items.Count) { ListViewItem item = this.listViewThreads.Items.Add((nIndex+1).ToString(), 0); string[] subItems = { "", "", "", "0", "0%" }; item.SubItems.AddRange(subItems); } } else if(threadsRun[nIndex].ThreadState == ThreadState.Suspended) { ListViewItem item = this.listViewThreads.Items[nIndex]; item.ImageIndex = 1; item.SubItems[2].Text = "Resume"; threadsRun[nIndex].Resume(); } } nThreadCount = value; Monitor.Exit(this.listViewThreads); } } 如果theadcode增加了用户的代码创建一个新的线程,或暂时中止线程. 其他,树叶制的过程suppending额外的工作线程threads自己如下. 每个工作线程有一个名字等于其指数的线程数组. 如果线程名称值大于threadcount继续其工作,并进入中止模式. 爬行深度:它是深入到履带goes,在航行过程. 每个url已有初步深度相当于母公司深度加一, 在深度为0头url插入的用户. 在成交的URL从任何页都插入后,在年底的URL队列 意思是"先入先出"的行动. 和所有线程可插入排队随时在以下部分代码: void EnqueueUri(MyUri uri) { Monitor.Enter(queueURLS); try { queueURLS.Enqueue(uri); } catch(Exception) { } Monitor.Exit(queueURLS); } 而每个线程可以取出第一url在排队,要求它在以下部分代码: MyUri DequeueUri() { Monitor.Enter(queueURLS); MyUri uri = null; try { uri = (MyUri)queueURLS.Dequeue(); } catch(Exception) { } Monitor.Exit(queueURLS); return uri; } |
|