江门移动网站建设报价,汕头企业免费建站,微网站自己怎么做的,免费h5网站制作平台爬虫自指定的URL地址开始下载网络资源#xff0c;直到该地址和所有子地址的指定资源都下载完毕为止。
下面开始逐步分析爬虫的实现。
待下载集合与已下载集合
为了保存需要下载的URL#xff0c;同时防止重复下载#xff0c;我们需要分别用了两个集合来存放将要下载的URL和…爬虫自指定的URL地址开始下载网络资源直到该地址和所有子地址的指定资源都下载完毕为止。
下面开始逐步分析爬虫的实现。
待下载集合与已下载集合
为了保存需要下载的URL同时防止重复下载我们需要分别用了两个集合来存放将要下载的URL和已经下载的URL。
因为在保存URL的同时需要保存与URL相关的一些其他信息如深度所以这里我采用了Dictionary来存放这些URL。
具体类型是Dictionarystring, int 其中string是Url字符串int是该Url相对于基URL的深度。
每次开始时都检查未下载的集合如果已经为空说明已经下载完毕如果还有URL那么就取出第一个URL加入到已下载的集合中并且下载这个URL的资源。
HTTP请求和响应
C#已经有封装好的HTTP请求和响应的类HttpWebRequest和HttpWebResponse所以实现起来方便不少。
为了提高下载的效率我们可以用多个请求并发的方式同时下载多个URL的资源一种简单的做法是采用异步请求的方法。
控制并发的数量可以用如下方法实现
private void DispatchWork()
{if (_stop) //判断是否中止下载{return;}for (int i 0; i _reqCount; i){if (!_reqsBusy[i]) //判断此编号的工作实例是否空闲{RequestResource(i); //让此工作实例请求资源}}
}由于没有显式开新线程所以用一个工作实例来表示一个逻辑工作线程
private bool[] _reqsBusy null; //每个元素代表一个工作实例是否正在工作
private int _reqCount 4; //工作实例的数量每次一个工作实例完成工作相应的_reqsBusy就设为false并调用DispatchWork那么DispatchWork就能给空闲的实例分配新任务了。
接下来是发送请求 每次一个工作实例完成工作相应的_reqsBusy就设为false并调用DispatchWork那么DispatchWork就能给空闲的实例分配新任务了。
接下来是发送请求
private void RequestResource(int index){int depth;string url ;try{lock (_locker){if (_urlsUnload.Count 0) //判断是否还有未下载的URL{_workingSignals.FinishWorking(index); //设置工作实例的状态为Finishedreturn;}_reqsBusy[index] true;_workingSignals.StartWorking(index); //设置工作状态为Workingdepth _urlsUnload.First().Value; //取出第一个未下载的URLurl _urlsUnload.First().Key;_urlsLoaded.Add(url, depth); //把该URL加入到已下载里_urlsUnload.Remove(url); //把该URL从未下载中移除}HttpWebRequest req (HttpWebRequest)WebRequest.Create(url);req.Method _method; //请求方法req.Accept _accept; //接受的内容req.UserAgent _userAgent; //用户代理RequestState rs new RequestState(req, url, depth, index); //回调方法的参数var result req.BeginGetResponse(new AsyncCallback(ReceivedResource), rs); //异步请求ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, //注册超时处理方法TimeoutCallback, rs, _maxTime, true);}catch (WebException we){MessageBox.Show(RequestResource we.Message url we.Status);}}
private void RequestResource(int index){int depth;string url ;try{lock (_locker){if (_urlsUnload.Count 0) //判断是否还有未下载的URL{_workingSignals.FinishWorking(index); //设置工作实例的状态为Finishedreturn;}_reqsBusy[index] true;_workingSignals.StartWorking(index); //设置工作状态为Workingdepth _urlsUnload.First().Value; //取出第一个未下载的URLurl _urlsUnload.First().Key;_urlsLoaded.Add(url, depth); //把该URL加入到已下载里_urlsUnload.Remove(url); //把该URL从未下载中移除}HttpWebRequest req (HttpWebRequest)WebRequest.Create(url);req.Method _method; //请求方法req.Accept _accept; //接受的内容req.UserAgent _userAgent; //用户代理RequestState rs new RequestState(req, url, depth, index); //回调方法的参数var result req.BeginGetResponse(new AsyncCallback(ReceivedResource), rs); //异步请求ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, //注册超时处理方法TimeoutCallback, rs, _maxTime, true);}catch (WebException we){MessageBox.Show(RequestResource we.Message url we.Status);}}第7行为了保证多个任务并发时的同步加上了互斥锁。_locker是一个Object类型的成员变量。
第9行判断未下载集合是否为空如果为空就把当前工作实例状态设为Finished如果非空则设为Working并取出一个URL开始下载。当所有工作实例都为Finished的时候说明下载已经完成。由于每次下载完一个URL后都调用DispatchWork所以可能激活其他的Finished工作实例重新开始工作。
第26行的请求的额外信息在异步请求的回调方法作为参数传入之后还会提到。
第27行开始异步请求这里需要传入一个回调方法作为响应请求时的处理同时传入回调方法的参数。
第28行给该异步请求注册一个超时处理方法TimeoutCallback最大等待时间是_maxTime且只处理一次超时并传入请求的额外信息作为回调方法的参数。
RequestState的定义是
class RequestState
{private const int BUFFER_SIZE 131072; //接收数据包的空间大小private byte[] _data new byte[BUFFER_SIZE]; //接收数据包的bufferprivate StringBuilder _sb new StringBuilder(); //存放所有接收到的字符public HttpWebRequest Req { get; private set; } //请求public string Url { get; private set; } //请求的URLpublic int Depth { get; private set; } //此次请求的相对深度public int Index { get; private set; } //工作实例的编号public Stream ResStream { get; set; } //接收数据流public StringBuilder Html{get{return _sb;}}public byte[] Data{get{return _data;}}public int BufferSize{get{return BUFFER_SIZE;}}public RequestState(HttpWebRequest req, string url, int depth, int index){Req req;Url url;Depth depth;Index index;}
}