• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > 利用C#实现网络爬虫

利用C#实现网络爬虫

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了利用公式c,利用c语言控制摄像头,c语言利用递归方法求5,利用公式c s 证明,利用公式c a b等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

网络爬虫在信息检索与处理中有很大的作用,是收集网络信息的重要工具。

接下来就介绍一下爬虫的简单实现。

爬虫的工作流程如下

爬虫自指定的URL地址开始下载网络资源,直到该地址和所有子地址的指定资源都下载完毕为止。

下面开始逐步分析爬虫的实现。

1. 待下载集合与已下载集合

为了保存需要下载的URL,同时防止重复下载,我们需要分别用了两个集合来存放将要下载的URL和已经下载的URL。

因为在保存URL的同时需要保存与URL相关的一些其他信息,如深度,所以这里我采用了Dictionary来存放这些URL。

具体类型是Dictionary<string, int> 其中string是Url字符串,int是该Url相对于基URL的深度。

每次开始时都检查未下载的集合,如果已经为空,说明已经下载完毕;如果还有URL,那么就取出第一个URL加入到已下载的集合中,并且下载这个URL的资源。

2. HTTP请求和响应

C#已经有封装好的HTTP请求和响应的类HttpWebRequest和HttpWebResponse,所以实现起来方便不少。

为了提高下载的效率,我们可以用多个请求并发的方式同时下载多个URL的资源,一种简单的做法是采用异步请求的方法。

控制并发的数量可以用如下方法实现

private void DispatchWork()
{
 if (_stop) //判断是否中止下载
 {
  return;
 }
 for (int i = 0; i < _reqCount; i++)
 {
  if (!_reqsBusy[i]) //判断此编号的工作实例是否空闲
  {
   RequestResource(i); //让此工作实例请求资源
  }
 }
}
</div>

 由于没有显式开新线程,所以用一个工作实例来表示一个逻辑工作线程

private bool[] _reqsBusy = null; //每个元素代表一个工作实例是否正在工作
private int _reqCount = 4; //工作实例的数量
</div>

 每次一个工作实例完成工作,相应的_reqsBusy就设为false,并调用DispatchWork,那么DispatchWork就能给空闲的实例分配新任务了。 

 接下来是发送请求

private void RequestResource(int index)
 {
  int depth;
  string url = "";
  try
  {
   lock (_locker)
   {
    if (_urlsUnload.Count <= 0) //判断是否还有未下载的URL
    {
     _workingSignals.FinishWorking(index); //设置工作实例的状态为Finished
     return;
    }
    _reqsBusy[index] = true;
    _workingSignals.StartWorking(index); //设置工作状态为Working
    depth = _urlsUnload.First().Value; //取出第一个未下载的URL
    url = _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);
  }
 }
</div>

第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]; //接收数据包的buffer
 private StringBuilder _sb = new StringBuilder(); //存放所有接收到的字符

 public HttpWebRequest Req { get; private set; } //请求
 public string Url { get; private set; } //请求的URL
 public 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;
 }
}

</div>

TimeoutCallback的定义是

private void TimeoutCallback(object state, bool timedOut)
{
 if (timedOut) //判断是否是超时
 {
  RequestState rs = state as RequestState;
  if (rs != null)
  {
   rs.Req.Abort(); //撤销请求
  }
  _reqsBusy[rs.Index] = false; //重置工作状态
  DispatchWork(); //分配新任务
 }
}
</div>

接下来就是要处理请求的响应了

private void ReceivedResource(IAsyncResult ar)
{
 RequestState rs = (RequestState)ar.AsyncState; //得到请求时传入的参数
 HttpWebRequest req = rs.Req;
 string url = rs.Url;
 try
 {
  HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(ar); //获取响应
  if (_stop) //判断是否中止下载
  {
   res.Close();
   req.Abort();
   return;
  }
  if (res != null && res.StatusCode == HttpStatusCode.OK) //判断是否成功获取响应
  {
   Stream resStream = res.GetResponseStream(); //得到资源流
   rs.ResStream = resStream;
   var result = resStream.BeginRead(rs.Data, 0, rs.BufferSize, //异步请求读取数据
    new AsyncCallback(ReceivedData), rs);
  }
  else //响应失败
  {
   res.Close();
   rs.Req.Abort();
   _reqsBusy[rs.Index] = false; //重置工作状态
   DispatchWork(); //分配新任务
  }
 }
 catch (WebException we)
 {
  MessageBox.Show("ReceivedResource " + we.Message + url + we.Status);
 }
}
</div>

第19行这里采用了异步的方法来读数据流是因为我们之前采用了异步的方式请求,不然的话不能够正常的接收数据。

该异步读取的方式是按包来读取的,所以一旦接收到一个包就会调用传入的回调方法ReceivedData,然后在该方法中处理收到的数据。

该方法同时传入了接收数据的空间rs.Data和空间的大小rs.BufferSize。 

接下来是接收数据和处理

private void ReceivedData(IAsyncResult ar)
{
 RequestState rs = (RequestState)ar.AsyncState; //获取参数
 HttpWebRequest req = rs.Req;
 Stream resStream = rs.ResStream;
 string url = rs.Url;
 int depth = rs.Depth;
 string html = null;
 int index = rs.Index;
 int read = 0;

 try
 {
  read = resStream.EndRead(ar); //获得数据读取结果
  if (_stop)//判断是否中止下载
  {
   rs.ResStream.Close();
   req.Abort();
   return;
  }
  if (read > 0)
  {
   MemoryStream ms = new MemoryStream(rs.Data, 0, read); //利用获得的数据创建内存流
   StreamReader reader = new StreamReader(ms, _encoding);
   string str = reader.ReadToEnd(); //读取所有字符
   rs.Html.Append(str); // 添加到之前的末尾
   var result = resStream.BeginRead(rs.Data, 0, rs.BufferSize, //再次异步请求读取数据
    new AsyncCallback(ReceivedData), rs);
   return;
  }
  html = rs.Html.ToString();
  SaveContents(html, url); //保存到本地
  string[] links = GetLinks(html); //获取页面中的链接
  AddUrls(links, depth + 1); //过滤链接并添加到未下载集合中

  _reqsBusy[index] = false; //重置工作状态




 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 利用C#如何给PDF文档添加文本与图片页眉
  • 利用C#操作WMI指南
  • 利用C#代码实现图片旋转360度
  • 利用C#实现网络爬虫

相关文章

  • 2017-05-28关于C#程序优化的五十种方法
  • 2017-05-28C#实现通过ffmpeg从flv视频文件中截图的方法
  • 2017-05-28C#实现鼠标移动到曲线图上显示值的方法
  • 2017-05-28c#通用登录模块分享
  • 2017-05-28Asp.Net中避免重复提交和弹出提示框的实例代码
  • 2017-05-28C#值类型、引用类型中的Equals和==的区别浅析
  • 2017-05-28C#使用iTextSharp从PDF文档获取内容的方法
  • 2017-05-28C#处理文本文件TXT实例详解
  • 2017-05-28C# 基础入门--变量
  • 2017-05-28C# 邮件发送和接收实现代码

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C#使用StopWatch获取程序毫秒级执行时间的方法
    • 在Winform和WPF中注册全局快捷键实现思路及代码
    • 基于WebClient实现Http协议的Post与Get对网站进行模拟登陆和浏览实例
    • C#实现HTML转WORD及WORD转PDF的方法
    • C# 调用 JavaWebservice服务遇到的问题汇总
    • 登录验证全局控制的几种方式总结(session)
    • timespan使用方法详解
    • C# 调用C++写的dll的实现方法
    • WinForm开发中屏蔽WebBrowser脚本错误提示的方法
    • C#在winform中实现数据增删改查等功能

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有