• 用C#实现网络爬虫(二)


    上一篇《用C#实现网络爬虫(一)》我们实现了网络通信的部分,接下来继续讨论爬虫的实现

    3. 保存页面文件

    这一部分可简单可复杂,如果只要简单地把HTML代码全部保存下来的话,直接存文件就行了。

     1 private void SaveContents(string html, string url)
     2 {
     3     if (string.IsNullOrEmpty(html)) //判断html字符串是否有效
     4     {
     5         return;
     6     }
     7     string path = string.Format("{0}\\{1}.txt", _path, _index++); //生成文件名
     8 
     9     try
    10     {
    11         using (StreamWriter fs = new StreamWriter(path))
    12         {
    13             fs.Write(html); //写文件
    14         }
    15     }
    16     catch (IOException ioe)
    17     {
    18         MessageBox.Show("SaveContents IO" + ioe.Message + " path=" + path);
    19     }
    20 
    21     if (ContentsSaved != null)
    22     {
    23         _ui.Dispatcher.Invoke(ContentsSaved, path, url); //调用保存文件事件
    24     }
    25 }

    第23行这里又出现了一个事件,是保存文件之后触发的,客户程序可以之前进行注册。

    1 public delegate void ContentsSavedHandler(string path, string url);
    2 
    3 /// <summary>
    4 /// 文件被保存到本地后触发
    5 /// </summary>
    6 public event ContentsSavedHandler ContentsSaved = null;

    4. 提取页面链接

    提取链接用正则表达式就能搞定了,不懂的可以上网搜。

    下面的字符串就能匹配到页面中的链接

    http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

    详细见代码

     1 private string[] GetLinks(string html)
     2 {
     3     const string pattern = @"http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
     4     Regex r = new Regex(pattern, RegexOptions.IgnoreCase); //新建正则模式
     5     MatchCollection m = r.Matches(html); //获得匹配结果
     6     string[] links = new string[m.Count]; 
     7 
     8     for (int i = 0; i < m.Count; i++)
     9     {
    10         links[i] = m[i].ToString(); //提取出结果
    11     }
    12     return links;
    13 }


    5. 链接的过滤

    不是所有的链接我们都需要下载,所以通过过滤,去掉我们不需要的链接

    这些链接一般有:

    • 已经下载的链接
    • 深度过大的链接
    • 其他的不需要的资源,如图片、CSS等
     1 //判断链接是否已经下载或者已经处于未下载集合中
     2 private bool UrlExists(string url) 
     3 {
     4     bool result = _urlsUnload.ContainsKey(url);
     5     result |= _urlsLoaded.ContainsKey(url);
     6     return result;
     7 }
     8 
     9 private bool UrlAvailable(string url)
    10 {
    11     if (UrlExists(url))
    12     {
    13         return false; //已经存在
    14     }
    15     if (url.Contains(".jpg") || url.Contains(".gif")
    16         || url.Contains(".png") || url.Contains(".css")
    17         || url.Contains(".js"))
    18     {
    19         return false; //去掉一些图片之类的资源
    20     }
    21     return true;
    22 }
    23 
    24 private void AddUrls(string[] urls, int depth)
    25 {
    26     if (depth >= _maxDepth)
    27     {
    28         return; //深度过大
    29     }
    30     foreach (string url in urls)
    31     {
    32         string cleanUrl = url.Trim(); //去掉前后空格
    33         cleanUrl = cleanUrl.TrimEnd('/'); //统一去掉最后面的'/'
    34         if (UrlAvailable(cleanUrl))
    35         {
    36             if (cleanUrl.Contains(_baseUrl))
    37             {
    38                 _urlsUnload.Add(cleanUrl, depth); //是内链,直接加入未下载集合
    39             }
    40             else
    41             {
    42                 // 外链处理
    43             }
    44         }
    45     }
    46 }

     第34行的_baseUrl是爬取的基地址,如http://news.sina.com.cn/,将会保存为news.sina.com.cn,当一个URL包含此字符串时,说明是该基地址下的链接;否则为外链。

    _baseUrl的处理如下,_rootUrl是第一个要下载的URL

     1 /// <summary>
     2 /// 下载根Url
     3 /// </summary>
     4 public string RootUrl
     5 {
     6     get
     7     {
     8         return _rootUrl;
     9     }
    10     set
    11     {
    12         if (!value.Contains("http://"))
    13         {
    14             _rootUrl = "http://" + value;
    15         }
    16         else
    17         {
    18             _rootUrl = value;
    19         }
    20         _baseUrl = _rootUrl.Replace("www.", ""); //全站的话去掉www
    21         _baseUrl = _baseUrl.Replace("http://", ""); //去掉协议名
    22         _baseUrl = _baseUrl.TrimEnd('/'); //去掉末尾的'/'
    23     }
    24 }


    至此,基本的爬虫功能实现就介绍完了。

    最后附上源代码和DEMO程序,爬虫的源代码在Spider.cs中,DEMO是一个WPF的程序,Test里是一个控制台的单线程版版本。

    PageExtractor.zip

    在下一期中,我们将介绍一些提取出网页中有效信息的方法,敬请期待。。。

  • 相关阅读:
    JavaScript中DOM的层次节点(一)
    JS可维护性代码
    Angular页面加载闪现解决方案 ng-cloak
    无法向会话状态服务器发出会话状态请求。
    算法题--拓扑排序
    2020年04月25日个人赛
    Educational Codeforces Round 86 (Rated for Div. 2)
    博弈--巴什博弈
    2020年04月19日个人赛
    AtCoder Beginner Contest 163(D)
  • 原文地址:https://www.cnblogs.com/Jiajun/p/2552458.html
Copyright © 2020-2023  润新知