- 浏览: 808264 次
文章分类
最新评论
-
469957559:
能受累发个源码包吗 我弄了好几天 没出来 想学习下 469 ...
SWFUpload多文件上传 文件个数限制 setStats() -
flywing521:
为什么配置 web.xml 时报错?
flex实现session -
宅男贰号:
iteye我不常上,回复时请发件通知一下,wooh@163.c ...
flex实现session -
宅男贰号:
ThreadLocal不是线程,可以理解为它是线程变量,你的例 ...
flex实现session -
jstl1point0:
学习了,监听后oncreate和onresum也不会执行了是吧 ...
Android横竖屏的设置和使用
lucene.Net--学习笔记(3)---C#'网络爬虫' 源码详解
我们知道,要想对数据进行检索,最基本也是最重要的东西就是数据本身了。
本章介绍如何获取大量的网页信息。
相信大家都听说过‘网络爬虫’,我们正是通过这种方式搜集网页的。
一、下面首先简单的介绍一下网络爬虫的基本结构:
简单的讲就是:
1、从一个url开始搜索,将这个页面上的所有链接保存,放入一个queue中。
2、接着从这个queue中取出一个url,重复第1步
这个过程类似于BFS(广度优先搜索)。(为了防止url被重复使用,这里可以用两个集合分别存放已下载与未下载的url)。
由于下载网页的速度与网速有关,cpu的时间大部分时间都消耗到了等待上面,因此,这里的网络爬虫采用的是多线程的方式。
二、分析网络爬虫的源码:
首先对程序各个类进行简要的讲解:
Cyh_HttpServer类:
该类中只有一个方法public string GetResponse(string url)功能是对指定的url获取该页面的html,实现该功能必须解决以下几个问题:
1.如何获取指定url的html?
其实实现该功能很简单,在C#中通过HttpWebResponse类的调用就能实现,具体方法是:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream reader = response.GetResponseStream();
然后从reader流中读取内容就行了
2.编码问题,网页通常使用utf-8或gb2312进行编码,如果程序在读取流时使用了错误的编码会导致中文字符的错误
3.对于有些页面的html可能会非常大所以我们要限制大小,在程序中最在读取不会超过100k。
代码如下:
/// <summary> /// <para>HTTP服务类</para> /// 由于在程序外该类是不可见的,所以声明时用了internal. /// </summary> internal class Cyh_HttpServer { public string GetResponse(string url) { string html = string.Empty; //文本内容 string encoding = string.Empty; //文本格式 #region MyRegion try { //创建一个hettpReq请求对象,包含要传递的值name HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = "get"; //发送方式 request.ContentType = "text/html"; //Http标头的值 request.Timeout = 30 * 1000; //请求超时时间 byte[] buffer = new byte[1024]; //使用using的作用,可以在using结束时,回收所有using段内的内存 //创建一个响应对象,并重请求对象中得到响应对象的事例。 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (Stream reader = response.GetResponseStream()) //得到回应过来的流 { reader.ReadTimeout = 30 * 1000; #region 处理流 //MemoryStream是一个支持存储区为内存的流。 using (MemoryStream memory = new MemoryStream()) { int index = 1; int sum = 0; //限制的读取的大小不超过100k while (index > 0 && sum < 100 * 1024) { index = reader.Read(buffer, 0, 1024); if (index > 0) { memory.Write(buffer, 0, index); //将缓存写入memory sum += index; } } //网页通常使用utf-8或gb2312进行编码 html = Encoding.GetEncoding("gb2312").GetString(memory.ToArray()); //返回与指定代码页名称关联的编码。 if (string.IsNullOrEmpty(html)) { return html; } else { Regex re = new Regex(@"charset=(?<charset>[\s\S]*?)[""|']"); Match m = re.Match(html.ToLower()); encoding = m.Groups["charset"].ToString(); } if (string.IsNullOrEmpty(encoding) || string.Equals(encoding.ToLower(), "gb2312")) { return html; } else { //不是gb2312编码则按charset值的编码进行读取 return Encoding.GetEncoding(encoding).GetString(memory.ToArray()); } } #endregion } } } #endregion catch { return ""; } } }
Cyh_AbsChain类:
/// <summary> /// <para>职责链抽象类</para> /// 对于AbsChain采用的是职责链设计模式, /// 目的是抽象出网络爬虫处理html的过程, /// 因为在spider程序集中并不真正处理如何解析html, /// 用户只需重载AbsChain类中的process方法,完成自定义的处理过程 /// </summary> public abstract class Cyh_AbsChain { /// <summary> /// 责任链中的一个 hander /// </summary> private Cyh_AbsChain _handler = null; internal Cyh_AbsChain Handler { get { return _handler; } } /// <summary> /// 待处理的url /// </summary> private string _url = string.Empty; public string Url { get { return _url; } set { _url = value; } } /// <summary> /// 文本处理过程(Protected abstract) /// </summary> /// <param name="htmlStream">html文本</param> protected abstract void Process(string html); /// <summary> /// 设置下一个处理节点 /// </summary> /// <param name="handler">下一个处理节点</param> public void SetProcessHandler(Cyh_AbsChain handler) { _handler = handler; } /// <summary> /// Cyh_AbsChain 开始处理 /// </summary> /// <param name="htmlStream">html文本流</param> public void Start_AbsChain(string html) { Process(html); //处理 用户重载方法 if (Handler != null) { Handler.Url = Url; Handler.Start_AbsChain(html); } } }
Cyh_ChainMain类:
/// <summary><para>ChainMain类是对AbsChain类的具体实现</para> /// 它的Process方法是个空方法, /// 所以你可以把它理解成它就是具体处理职责链上的头节点, /// 通过ChainMain类的_handler将处理任务往下传递, /// 用户通过调用ChainMain的SetProcessHandler方法设置下一个处理节点, /// 这个节点必须由用户继承AbsChain并实现抽象方法Process /// </summary> internal class Cyh_ChainMain : Cyh_AbsChain { /// <summary> 需要用户重置的处理函数 </summary> protected override void Process(string html) { } }
Cyh_WordThread类:
/// <summary> /// <para>工作线程</para> /// <para>WorkThread类是工作线程类, /// 每个工作线程类都包括</para> /// <para>一个职责链的头节点ChainMain、一个HttpServer类和一个UrlStack,</para> /// 其中UrlStack类采用了单构件设计模式, /// 所以对于整个应该用程序都是使用一个UrlStack对象。 /// </summary> internal class Cyh_WordThread { #region 定义头节点ChainMain、HttpServer类和UrlStack private Cyh_ChainMain _chainHeader = new Cyh_ChainMain(); internal Cyh_ChainMain ChainMain { get { return _chainHeader; } } private Cyh_HttpServer _httpServer = new Cyh_HttpServer(); internal Cyh_HttpServer HttpServer { get { return _httpServer; } } public Cyh_UrlStack UrlStack { get { return Cyh_UrlStack.Instance; } } private bool _isRun = false; public bool IsRun { get { return _isRun; } } #endregion /// <summary> /// <para>工作线程入口函数</para> /// Start_WordThread()从UrlStack中取出url, /// 并调用Cyh_HttpServer的GetResponse方法取出Url对应网页的HTML代码, /// 并将HTML代码传递给职责链的头节点Cyh_ChainMain, /// 由它的Start_AbsChain()方法开始处理。 /// /// 它是先调用自身类的Process方法, /// 然后再调用_handler.Start_AbsChain()方法, /// 就这样把处理过程传递下去。 /// </summary> public void Start_WordThread() { #region Try try { this._isRun = true; while (_isRun) { string url = this.UrlStack.Pop(); if (!string.IsNullOrEmpty(url)) { string html = _httpServer.GetResponse(url); if (!string.IsNullOrEmpty(html)) { this.ChainMain.Url = url; //处理得到的html this.ChainMain.Start_AbsChain(html); } } } } #endregion catch { } } /// <summary> /// 停止工作线程 /// </summary> public void Stop_WorkThread() { this._isRun = false; } }
Start_WordThread方法是工作线程的入口方法,它从Cyh_UrlStack中取出url,并调用Cyh_HttpServer的GetResponse方法取出Url对应 网页的HTML代码,并将HTML代码传递给职责链的头节点Cyh_ChainMain,由它的Start_WordThread方法开始处理。回忆一下Cyh_AbsChain的 Start_AbsChain()方法,它是先调用自身类的Process方法,然后再调用_handler.Start_AbsChain(方法,就这样把处理过程传递下去。
Cyh_UrlStack类:
/// <summary> /// UrlStack类非常的简单, /// 它采用单构件设计模式, /// 整个程序只用到一个UrlStack对象 /// 并维护了一个数据结构, /// 该数据结构用来存储需要爬虫抓取的Url /// </summary> public class Cyh_UrlStack { private static Cyh_UrlStack _urlstack = new Cyh_UrlStack(); /// <summary> stack、用来存放url </summary> private Queue<string> _stack = new Queue<string>(); /// <summary> stack的最大存放数量 </summary> private readonly int _maxLength = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["MaxLength"]); /// <summary> 构造函数 </summary> private Cyh_UrlStack() { } /// <summary> UrlStack的实例 </summary> public static Cyh_UrlStack Instance { get { return _urlstack; } } public void Push(string url) { lock (this) { if (!_stack.Contains(url)) { if (_stack.Count >= _maxLength) { _stack.Dequeue(); //移除并返回位于 Queue 开始处的对象。 } _stack.Enqueue(url); //将url添加到 Queue 的结尾处。 } } } public string Pop() { lock (this) { if (_stack.Count > 0) { return _stack.Dequeue(); } else { return ""; } } } public int Count { get { return _stack.Count; } } }
Cyh_AbsThreadManager类:
/// <summary> /// <para>AbsThreadManager的主要功能是管理开启WorkThread工作线程,</para> /// 与监控线线程的,WorkThread对象与Thread对象一一对应, /// 这两个对象都被封在ObjThread对象里 /// /// 在AbsThreadManagers中用List<ObjThread>来维护一系列的线程对象与WorkThread对象, /// 同时在 AbsThreadManagers中增加了一个监控线程, /// 用来查看工作线程的工作线程, /// 若工作线程死去,由监控线程重新启动。 /// </summary> public abstract class Cyh_AbsThreadManager { public int _maxThread = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["MaxCount"]); /// <summary> 用List<ObjThread>来维护一系列的线程对象与WorkThread对象, </summary> internal List<Cyh_ObjThread> list = new List<Cyh_ObjThread>(); private bool _isRun = false; /// <summary> 用来监控线程存活死亡的主线程 </summary> private System.Threading.Thread _watchThread = null; /// <summary> 当前深度 </summary> public int Current { get { return Cyh_UrlStack.Instance.Count; } } /// <summary> /// 开启服务 /// </summary> /// <param name="url">种子URL</param> public void Start_AbsThreadManager(string url) { Cyh_UrlStack.Instance.Push(url); _isRun = true; //初始化线程list for (int i = 0; i < _maxThread; i++) { this.AddObjThread(); } _watchThread = new System.Threading.Thread(Watch); _watchThread.Start(); } /// <summary> 停止服务 </summary> public void Stop_AbsThreadManager() { _isRun = false; _watchThread.Join(); //阻塞调用线程,直到线程终止为止。 foreach (Cyh_ObjThread obj in list) { obj.WorkThread.Stop_WorkThread(); obj.Thread.Abort(); obj.Thread.Join(); } list.RemoveRange(0, list.Count); } /// <summary> 增加一个线程 </summary> private void AddObjThread() { Cyh_ObjThread thread = new Cyh_ObjThread(); //初始化一个新的Thread thread.WorkThread = new Cyh_WordThread(); //设置该线程用于处理职责链中的下一个节点 thread.WorkThread.ChainMain.SetProcessHandler(GetChainHeader()); thread.Thread = new System.Threading.Thread(thread.WorkThread.Start_WordThread); list.Add(thread); //线程list中加入新的thread thread.Thread.Start(); //开启该线程 } /// <summary> /// <para>设置职责链头节点,该方法由用户设定</para> /// 返回一个继承了Cyh_AbsChain类的对象, /// 这个对象将会被设置到 Cyh_ChainMain的_handler中 /// </summary> /// <returns>返回用户定义的Chain</returns> protected abstract Cyh_AbsChain GetChainHeader(); /// <summary> /// 监测存活的或正在运行的线程, /// 将运行结束或死亡的进程去除, /// 并新增线程 /// </summary> internal void Watch() { List<Cyh_ObjThread> newList = new List<Cyh_ObjThread>(); while (this._isRun) { try { //检测存活的线程并保存下来, foreach (Cyh_ObjThread temp in this.list) { if (temp.WorkThread.IsRun && temp.Thread.IsAlive) { newList.Add(temp); } } //更新list中的线程 this.list.RemoveRange(0, list.Count); list.AddRange(newList); int newCount = this._maxThread - this.list.Count; //加入其它新的线程,使list中的线程数达到_maxThread for (int i = 0; i < newCount; i++) { this.AddObjThread(); } newList.RemoveRange(0, newList.Count); //System.Threading.Thread.Sleep(5 * 1000); } catch { } finally { } } } }
在Cyh_AbsThreadManager中用到了一个类Cyh_ObjThread,它是一种线程的类型,看定义:
Cyh_ObjThread类:
internal class Cyh_ObjThread { private Cyh_WordThread _workThread; internal Cyh_WordThread WorkThread { get { return _workThread; } set { _workThread = value; } } private System.Threading.Thread _thread; public System.Threading.Thread Thread { get { return _thread; } set { _thread = value; } } }
以上为网络爬虫中的重要的几个类,还有几个用于客户端的类没有各处,如果需要源代码的可以在评论中提出,人员较多的话,我可以上传。
---------------------------------------------------2012年9月17日23:49:28
相关推荐
Lucene.Net-2.9.2 c# 源码,已经用它做了查询网站
Lucene.Net 3.0 最新源码 Lucene.net是Lucene的.net移植版本,是一个开源的全文检索引擎开发包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。开发人员可以基于...
最新版本Lucene.Net-2.3.2
Lucene.Net 4.8版本源代码及案例,内包含Lucene.Net 4.8最新版本源代码及部分使用案例,Lucene.Net + 盘古分词一直是net旗下的最佳搜索方案,你GET到了吗?
Lucene.Net是一个搜索引擎库,C#编写的。NET运行时用户和有针对性的。Lucene搜索库是基于倒排索引。Lucene.Net有三个主要目标: 1.保持现有生产线,由线端口从Java到C#,完全自动化和商品化的过程中,该项目可以...
Apache-Lucene.Net-3.0.3-RC2.bin
Lucene.Net-2.0.doc 更多信息请查看 www.iubang.com
Lucene.Net ultra fast search for MVC or WebForms site => made easy!
lucene.net-1.4.3.final-004-21Nov05.bin
lucene.net.4.8.0-beta00005最新版,全文索引
Lucene.Net.dll 版本2.9.2
Lucene.Net,全文检索开源代码,(C#)
构建自己的搜索引擎,lucene.net给个人站长带来了自己引擎的可能!
Lucene.Net,已编译,不含源代码
lucene.net 2.9.1 源码,lucene.net 2.9.1最新dll
lucene.NET 中文分词 高亮 lucene.NET 中文分词 高亮 lucene.NET 中文分词 高亮 lucene.NET 中文分词 高亮
Lucene.Net2.0 类MSDN操作文档
说明:使用Lucene.NET技术实现搜索引擎范例 使用到的DLL插件包括: DictSeg.dll - 1.0.1.0 FredCK.FCKeditorV2.dll - 2.6.3.22451 FreeTextBox.dll - 3.3.1.12354 FTAlgorithm.dll - 1.0.1.0 Lucene.Net.dll - 1.9....
Lucene.net 搜索引擎 Lucene.net源码 Lucene.net中文文档; 好的东西需要分享