网络采集软件核心技术剖析系列(1)---如何使用C#语言获取博客园某个博主的全部随笔链接及标题

简介:

一 本系列随笔概览及产生的背景

自己开发的豆约翰博客备份专家软件工具问世3年多以来,深受广大博客写作和阅读爱好者的喜爱。同时也不乏一些技术爱好者咨询我,这个软件里面各种实用的功能是如何实现的。

该软件使用.NET技术开发,为回馈社区,现将该软件中用到的核心技术,开辟一个专栏,写一个系列文章,以飨广大技术爱好者。

本系列文章除了讲解网络采编发用到的各种重要技术之外,也提供了不少问题的解决思路和界面开发的编程经验,非常适合.NET开发的初级,中级读者,希望大家多多支持。

很多初学者常有此类困惑,“为什么我书也看了,C#相关的各个方面的知识都有所了解,但就是没法写出一个像样的应用呢?”,

这其实还是没有学会综合运用所学知识,锻炼出编程思维,建立起学习兴趣,我想该系列文章也许会帮到您,但愿如此。

开发环境:VS2008

源码位置:https://github.com/songboriceboy/NetworkGatherEditPublish

源码下载办法:安装SVN客户端(本文最后提供下载地址),然后checkout以下的地址:https://github.com/songboriceboy/NetworkGatherEditPublish

系列文章提纲拟定如下:

1.如何使用C#语言获取博客园某个博主的全部随笔链接及标题;
2.如何使用C#语言获得博文的内容;
3.使用C#语言如何将html网页转换成pdf(html2pdf)
4.如何使用C#语言下载博文中的全部图片到本地并可以离线浏览
5.如何使用C#语言合成多个单个的pdf文件到一个pdf中,并生成目录
6.网易博客的链接如何使用C#语言获取到,网易博客的特殊性;
7.微信公众号文章如何使用C#语言下载;
8.如何获取任意一篇文章的全部图文
9.如何使用C#语言去掉html中的全部标签获取纯文本(html2txt)
10.如何使用C#语言将多个html文件编译成chm(html2chm)
11.如何使用C#语言远程发布文章到新浪博客
12.如何使用C#语言开发静态站点生成器
13.如何使用C#语言搭建程序框架(经典Winform界面,顶部菜单栏,工具栏,左边树形列表,右边多Tab界面)
14.如何使用C#语言实现网页编辑器(Winform)

......

二 第一节主要内容简介(如何使用C#语言获取博客园某个博主的全部随笔链接及标题)

获取某个博主的全部博文链接及标题的解决方案,演示demo如下图所示:可执行文件下载

三 基本原理

 要想采集的某个博主的全部博文网页地址,需要分2步:

1.通过分页链接获取到网页源代码;

2.从获取到的网页源代码中解析出文章地址和标题;

第一步,首先找到分页链接,比如我的博客

第一页 http://www.cnblogs.com/ice-river/default.html?page=1

第二页 http://www.cnblogs.com/ice-river/default.html?page=2

 我们可以写个函数把这些分页地址字符串保存至一个队列中,如下代码所示,

下面的代码中,我们默认保存了500页,500页*20篇=10000篇博文,一般够用了,除非对于特别高产的博主。

还有一点,有心的朋友们可能会问,500页是不是太多了,有的博主只有2,3页,我们有必要去采集500个分页来获取全部博文链接么?

这里因为我们不知道某个博主到底写了多少篇博文(分成几页),所以,我们先默认取500页

,后面会讲到一种判断已经获取到全部文章链接的办法,其实我们并不会每个博主都访问500个分页。

 protected void GatherInitCnblogsFirstUrls()
        {            string strPagePre = "http://www.cnblogs.com/";            string strPagePost = "/default.html?page={0}&OnlyTitle=1";            string strPage = strPagePre + this.txtBoxCnblogsBlogID.Text + strPagePost;            for (int i = 500; i > 0; i--)
            {                string strTemp = string.Format(strPage, i);
                m_wd.AddUrlQueue(strTemp);

            }
        }

 至于获取某个网页的源文件(就是你在浏览器中,对某个网页右键---查看网页源代码功能)

C#语言已经为我们提供了一个现成的HttpWebRequest类,我将其封装成了一个WebDownloader类,具体细节大家可以参考源代码,主要函数实现如下:

     public string GetPageByHttpWebRequest(string url, Encoding encoding, string strRefer)
        {            string result = null;
   
            WebResponse response = null;
            StreamReader reader = null;            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)";
                request.Accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";                if (!string.IsNullOrEmpty(strRefer))
                {
                    Uri u = new Uri(strRefer);
                    request.Referer = u.Host;
                }                else
                {
                    request.Referer = strRefer;
                }
                request.Method = "GET";
                response = request.GetResponse();
                reader = new StreamReader(response.GetResponseStream(), encoding);
                result = reader.ReadToEnd();
                
            }            catch (Exception ex)
            {
                result = "";
            }            finally
            {                if (reader != null)
                    reader.Close();                if (response != null)
                    response.Close();
                
            }            return result;
        }

第一个参数传入的就是,我们上面形成的500个分页地址,函数的返回值就是网页的源代码(我们想要的文章地址和标题就在其中,接下来我们要把它们解析出来)。

第二步:从获取到的网页源代码中解析出文章地址和标题

我们要利用大名鼎鼎的HtmlAgilityPack类库,HtmlAgilityPack是一个HTML文档的解析利器,通过它我们可以方便的获得网页的标题,正文,分类,日期等等,理论上任何元素,相关的文档网上有很多,这里就不多说了。这里我们给HtmlAgilityPack增加了一个扩展方法以提取出任意网页源文件的全部超级链接GetReferences和链接对应的文本GetReferencesText。

    private void GetReferences()
        {
            HtmlNodeCollection hrefs = m_Doc.DocumentNode.SelectNodes("//a[@href]");            if (Equals(hrefs, null))
            {
                References = new string[0];                return;
            }

            References = hrefs.
                Select(href => href.Attributes["href"].Value).
                Distinct().
                ToArray();
        }
private void GetReferencesText()
        {            try
            {
                m_dicLink2Text.Clear();
                HtmlNodeCollection hrefs = m_Doc.DocumentNode.SelectNodes("//a[@href]");                if (Equals(hrefs, null))
                {                    return;
                }                foreach (HtmlNode node in hrefs)
                {                    if (!m_dicLink2Text.Keys.Contains(node.Attributes["href"].Value.ToString()))                        if(!HttpUtility.HtmlDecode(node.InnerHtml).Contains("img src")                            && !HttpUtility.HtmlDecode(node.InnerHtml).Contains("img ")                            && !HttpUtility.HtmlDecode(node.InnerHtml).Contains(" src"))
                         m_dicLink2Text.Add(node.Attributes["href"].Value.ToString(), HttpUtility.HtmlDecode(node.InnerHtml));
                }                int a = 0;
            }            catch (System.Exception e)
            {
                System.Console.WriteLine(e.ToString());
            }

        }

但是注意到,到此为止我们是获取到了某个网页中的全部链接地址,这其实距离我们想要的还差点,所以我们需要在这些链接地址集合中过滤出我们真正想要的博文地址。

这时我们需要用到强大的正则表达式工具,同样C#中提供了现成的支持类,但是需要我们对正则表达式有所了解,这里就不讲解正则表达式的相关知识了,不懂的请自行百度之。

首先我们需要观察博文链接地址的格式:

随便找几篇博文:

http://www.cnblogs.com/ice-river/p/3475041.html

http://www.cnblogs.com/zhijianliutang/p/4042770.html

我们发现链接和博主ID有关,所以博主ID我们需要有个变量( this.txtBoxCnblogsBlogID.Text)进行记录,

上面的链接模式用正则表达式可以表示如下:

"www\.cnblogs\.com/" + this.txtBoxCnblogsBlogID.Text + "/p/.*?\.html$";

简单解释一下:\代表转义,因为.在正则表达式中有重要含义;$代表结尾,html$的意思就是以html结尾。.*?是什么,很重要且不太好理解

 

正则有两种模式,一种为贪婪模式(默认),另外一种为懒惰模式,以下为例:
(abc)dfe(gh)
对上面这个字符串使用(.*)将会匹配整个字符串,因为正则默认是尽可能多的匹配。
虽然(abc)满足我们的表达式,但是(abc)dfe(gh)也同样满足,所以正则会匹配多的那个。
如果我们只想匹配(abc)和(gh)就需要用到以下的表达式
(.*?)
在重复元字符*或者+后面跟一个?,作用就是在满足的条件下尽可能少匹配。

 

所以,上面的正则表达式的意思就是“含有www.cnblogs.com/接着博主ID然后再接着/p/然后再接着任意多个字符直到遇到html结尾为止”。

然后,我们就可以通过C#代码来过滤符合这个模式的全部链接了,主要代码如下:

   MatchCollection matchs = Regex.Matches(normalizedLink, m_strCnblogsUrlFilterRule, RegexOptions.Singleline);                if (matchs.Count > 0)
                {                    string strLinkText = "";                    if (links.m_dicLink2Text.Keys.Contains(normalizedLink))
                        strLinkText = links.m_dicLink2Text[normalizedLink];                    if (strLinkText == "")
                    {                        if (links.m_dicLink2Text.Keys.Contains(link))
                            strLinkText = links.m_dicLink2Text[link].TrimEnd().TrimStart();
                    }

                    PrintLog(strLinkText + "\n");
                    PrintLog(normalizedLink + "\n");
                    

                    lstThisTimesUrls.Add(normalizedLink);
                }

 判断全部文章链接获取完成:之前,我们是计划采集500个分页地址,但是有可能该博主的全部博文只有几页,那么我们该如何判断全部文章都下载完成了呢?

办法其实很简单,就是我们使用2个集合,一个是当前下载的全部文章集合,一个是本次下载到的文章集合,如果本次下载的全部文章,之前下载的全部集合中都有了,那么说明全部文章都下载完成了。

程序中,我将这个判断封装成了一个函数,代码如下:

  private bool CheckArticles(List<string> lstThisTimesUrls)
        {            bool bRet = true;            foreach (string strTemp in lstThisTimesUrls)
            {                if (!m_lstUrls.Contains(strTemp))
                {
                    bRet = false;                    break;
                }
            }            foreach (string strTemp in lstThisTimesUrls)
            {                if (!m_lstUrls.Contains(strTemp))
                    m_lstUrls.Add(strTemp);
            }         
            return bRet;
        }

 

四 其他比较重要的知识

1.BackgroundWorker工作者线程的使用,因为我们的采集任务是一个比较耗时的工作,所以我们不应该放到界面主线程去做,我们应该启动一个后台线程,c#中最方便的后台线程使用方法就是利用BackgroundWorker类。

2.由于我们需要在解析出每一篇文章的地址及标题后,在界面上打印出来,同时因为我们不能在工作者线程中去修改界面控件,所以这里我们需要使用C#中的代理delegate技术,通过回调的方式来实现在界面上输出信息。

 

        TaskDelegate deles = new TaskDelegate(new ccTaskDelegate(RefreshTask));        
        public void RefreshTask(DelegatePara dp)
        {            //如果需要在安全的线程上下文中执行            if (this.InvokeRequired)
            {                this.Invoke(new ccTaskDelegate(RefreshTask), dp);                return;
            }          
            //转换参数            string strLog = (string)dp.strLog;
            WriteLog(strLog);

        }        protected void PrintLog(string strLog)
        {
            DelegatePara dp = new DelegatePara();

            dp.strLog = strLog;
            deles.Refresh(dp);
        }        public void WriteLog(string strLog)
        {            try
            {
                strLog = System.DateTime.Now.ToLongTimeString() + " : " + strLog;           
                this.richTextBoxLog.AppendText(strLog);                this.richTextBoxLog.SelectionStart = int.MaxValue;                this.richTextBoxLog.ScrollToCaret();
            }            catch
            {
            }
        }









本文转自 xchsp 51CTO博客,原文链接:http://blog.51cto.com/freebird/1586526,如需转载请自行联系原作者

目录
相关文章
|
4天前
|
存储 安全 网络安全
云计算与网络安全:技术融合与挑战
【6月更文挑战第20天】本文旨在探讨云计算环境下的网络安全问题,分析云服务模型中的安全挑战,并提出相应的信息安全策略。通过研究当前云服务供应商的安全措施和存在的漏洞,文章强调了在设计、部署及运维阶段应用最佳实践的必要性。此外,还讨论了加密技术和身份管理在提升云环境安全性中的作用,以及如何通过合规性框架来确保数据保护和隐私。
|
2天前
|
云安全 安全 网络安全
云计算与网络安全:技术演进与挑战
【6月更文挑战第22天】在数字化浪潮的推动下,云计算已成为企业信息技术架构的核心。然而,随着云服务的普及,网络安全问题也日益凸显。本文将探讨云计算环境下的网络安全挑战,分析当前云服务中存在的安全风险,并介绍信息安全的最佳实践和未来发展趋势。通过深入分析,旨在为读者提供对云计算与网络安全交织领域的全面理解。
|
2天前
|
机器学习/深度学习 安全 网络安全
云计算与网络安全:技术融合的未来之路
【6月更文挑战第22天】在数字化浪潮的推动下,云计算已成为企业信息技术架构的核心。然而,随着云服务的广泛采纳,网络安全问题也日益凸显。本文将探讨云计算与网络安全之间的相互作用,分析当前云服务中存在的安全挑战,并提出相应的解决策略。我们将深入讨论数据加密、访问控制、威胁检测等关键技术,并展望未来云计算和网络安全技术的发展趋势,以期为业界提供参考和指导。
14 7
|
3天前
|
缓存 安全 Java
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
【6月更文挑战第22天】Java的URL和URLConnection在现代网络编程中扮演关键角色,不仅用于基本HTTP请求,还在微服务(弹性自动化调用)、智能缓存策略、异步处理和安全增强方面展现创新应用。例如,它们支持动态服务发现、HTTP缓存控制、非阻塞I/O和HTTPS加密,助力开发者构建高效、安全的网络解决方案。通过掌握这些技术,可以提升项目性能,应对云计算和大数据时代的挑战。
|
4天前
|
存储 安全 网络安全
云计算与网络安全:技术融合的双刃剑
【6月更文挑战第20天】在数字化浪潮推动下,云计算服务已成为企业IT架构的核心。然而,随着云服务的普及,网络安全威胁也日益增多。本文将探讨云计算环境下的网络安全挑战,分析信息安全技术如何应对这些挑战,并讨论云服务提供商和企业在保障数据安全方面的责任与策略。通过技术性案例分析,揭示云计算与网络安全之间的微妙平衡,为读者提供深入的行业见解。
|
4天前
|
存储 安全 网络安全
云计算与网络安全:技术融合下的挑战与机遇
【6月更文挑战第20天】随着云计算技术的迅猛发展,企业和个人越来越依赖云服务来存储和处理数据。然而,这也带来了新的网络安全挑战。本文将从技术角度深入探讨云计算环境下的网络安全问题,包括云服务的安全性、网络攻击的演变以及信息安全的最佳实践。我们将分析当前云服务中存在的安全漏洞,探讨如何通过技术和策略的结合来提高云环境的安全性,并预测未来云计算与网络安全领域的发展趋势。
|
5天前
|
存储 安全 网络安全
云计算与网络安全:技术创新与挑战
云计算与网络安全作为当今信息技术领域的热门话题,正在不断演进和发展。本文将从云服务、网络安全和信息安全等多个技术领域展开讨论,探索其中的技术创新与挑战。
|
3天前
|
存储 安全 网络安全
云计算与网络安全:技术融合与挑战
随着云计算技术的迅速发展,其安全性问题也日益凸显。本文从云服务、网络安全和信息安全三个角度出发,探讨了云计算与网络安全的关联性及其面临的挑战。首先,介绍了云计算的基本概念和服务模型,然后分析了网络安全的重要性以及在云计算环境下的安全威胁,最后提出了加强云计算环境下的信息安全策略和技术的建议。通过深入分析,本文旨在为读者提供对云计算与网络安全关系的全面理解,以及应对策略的参考。
12 0
|
3天前
|
存储 安全 网络安全
云计算与网络安全:技术融合与挑战应对
当今数字化时代,云计算和网络安全已经成为科技领域的热门话题。本文将探讨云计算和网络安全在技术融合中的重要性以及面临的挑战,从云服务、网络安全、信息安全等多个角度进行分析,并提出相应的解决方案。
|
4天前
|
前端开发 Java 关系型数据库
基于JSP技术的网络视频播放器
基于JSP技术的网络视频播放器

热门文章

最新文章