您的位置:首页 > 其它

记一次企业级爬虫系统升级改造(二):基于AngleSharp实现的抓取服务

2016-12-05 11:15 555 查看
  爬虫系统升级改造正式启动:

    在第一篇文章,博主主要介绍了本次改造的爬虫系统的业务背景与全局规划构思:

    未来Support云系统,不仅仅是爬虫系统,是集爬取数据、数据建模处理统计分析、支持全文检索资源库、其他业务部门和公司资讯系统重要数据来源、辅助决策等功能于一身的企业级Support系统。

    介于好多园友对博主的任务排期表感兴趣,便介绍一下博主当时针对这个系统做的工作任务排期概要(排期表就是更加详细细分外加估算工时的一份excel表格,就不贴出来了):

      1.总分四大阶段,逐步上线,最终达到预期规划

      2.第一阶段实现一个新的采集系统,自动实时化爬取数据、初步规则引擎实现数据规则化、统计邮件自动推送、开放数据检索,并上线替换原有爬虫系统

      3.第二阶段实现规则化引擎升级,扩展成长式规则引擎,并开放采集源提交、管理、规则配置、基础数据服务等基本系统操作

      4.第三阶段引入全文检索,针对规则化数据创建索引,提供数据全文搜索功能,开放工单申请,可定制数据报告

      5.第四阶段引入数据报表功能,开放统计分析结果,并向舆情监控与决策支持方向扩展

    当然,在博主未争取到更多资源的情况下,第一阶段的排期要求了一个月,后面各阶段只做了功能规划,并未做时间排期。

    这也算是一个小手段吧,毕竟第一阶段上线,boss们是可能提很多其他意见,或者遇到其他任务安排的,不能一开始就把时间节点写死,不然最终受伤的可能是程序员自己。


你比他好一点,他不会承认你,反而会嫉妒你,只有你比他好很多,他才会承认你,然后还会很崇拜你,所以要做,就一定要比别人做得好很多。



  代码框架搭建:

    虽然大家都对我的“SupportYun”命名颇有异议,但是我依然我行我素,哈哈~~~总感觉读起来很和谐

    先上一张截止今天,项目结构的整体图:

1     /// <summary>
2     /// 抓取服务抽象基类
3     /// </summary>
4     public abstract class BaseGrabService
5     {
6         /// <summary>
7         /// 线程休眠时间 毫秒
8         /// </summary>
9         private readonly static  int threadSleepTime = 1000;
10
11         /// <summary>
12         /// 加载指定页面
13         /// </summary>
14         /// <param name="url">加载地址</param>
15         /// <param name="charsetType">编码集</param>
16         /// <returns></returns>
17         public string GetHtml(string url, string charsetType)
18         {
19             string result = null;
20             HttpHelper httpHelper = new HttpHelper();
21             result = httpHelper.RequestResult(url, "GET", charsetType);
22             result = ConvertCharsetUTF8(result);
23
24             // 简单的休眠,防止IP被封
25             // TODO:后期视情况做更进一步设计
26             Thread.Sleep(threadSleepTime);
27             return result;
28         }
29
30         /// <summary>
31         /// 强制将html文本内容转码为UTF8格式
32         /// </summary>
33         /// <param name="strHtml"></param>
34         /// <returns></returns>
35         public string ConvertCharsetUTF8(string strHtml)
36         {
37             if (!strHtml.Contains("Content-Type") && !strHtml.Contains("gb2312"))
38             {
39                 if (strHtml.Contains("<title>"))
40                 {
41                     strHtml = strHtml.Insert(strHtml.IndexOf("<title>", StringComparison.Ordinal), "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
42                 }
43             }
44             else
45             {
46                 strHtml = strHtml.Replace("gb2312", "utf-8").Replace("gbk", "utf-8");
47             }
48             return strHtml;
49         }
50
51         /// <summary>
52         /// 根据规则,从html中返回匹配结果
53         /// </summary>
54         /// <param name="doc">html doc</param>
55         /// <param name="rule">规则</param>
56         /// <returns></returns>
57         public IEnumerable<IElement> GetItemList(IDocument doc,string rule)
58         {
59             var itemList = doc.All.Where(m => m.Id == rule.Trim());
60             if (!itemList.Any())
61             {
62                 itemList = doc.All.Where(m => m.ClassName == rule.Trim());
63             }
64             return itemList;
65         }
66
67         /// <summary>
68         /// 获取列表项中的url实体
69         /// </summary>
70         /// <returns></returns>
71         public List<UrlModel> GetUrlList(string strItems)
72         {
73             List<UrlModel> itemList = new List<UrlModel>();
74             Regex reg = new Regex(@"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>");
75             MatchCollection mc = reg.Matches(strItems);
76             foreach (Match m in mc)
77             {
78                 UrlModel urlModel = new UrlModel();
79                 urlModel.Url = m.Groups["url"].Value.Trim().Replace("amp;", "");
80                 urlModel.Title = m.Groups["text"].Value.Trim();
81                 itemList.Add(urlModel);
82             }
83
84             return itemList;
85         }
86     }
87
88     /// <summary>
89     /// URL对象
90     /// </summary>
91     public class UrlModel
92     {
93         /// <summary>
94         /// 连接地址
95         /// </summary>
96         public string Url { get; set; }
97
98         /// <summary>
99         /// 连接Title
100         /// </summary>
101         public string Title { get; set; }
102     }
103
104     /// <summary>
105     /// 详情内容对象
106     /// </summary>
107     public class DetailModel
108     {
109         /// <summary>
110         /// title
111         /// </summary>
112         public string Title { get; set; }
113
114         /// <summary>
115         /// 内容
116         /// </summary>
117         public string FullContent { get; set; }
118     }


View Code
    注意AngleSharpGrabService的OprGrab方法第33行至42行,在做url的构建。因为我们抓取到的a标签的href属性很可能是相对地址,在这里我们需要做判断替换成绝对地址。

    具体逻辑大家可以参考上面的爬取流程图。

    OprGrab方法的第47行即从抓取的具体详情页html中获取详情数据(目前主要获取title和带html标签的内容,具体清理与分析由规则化引擎来完成)。

    具体实现代码并无太多营养,和抓取列表页几乎一致:构建document对象,通过规则匹配出含有title的html片段和含有内容的html片段,再对title进行html标签清洗。

    具体清洗一个html文本html标签的方法已经属于规则化引擎的范畴,容博主下一篇写规则化引擎服务的时候再来贴出并给大家作分析。

    这时候,我们部署在服务器上的windows服务就能按我们配好的规则进行初始数据抓取入库了。

    贴一张博主当前测试抓取的数据截图:



    博主终于算是完成了系统的第一步,接下来就是规则化引擎分析FullContent里面的数据了。

    博主争取本周写完规则化引擎相关的代码,下周再来分享给大家哈!

    可是答应了一个月时间要做好第一阶段的所有内容并上线呢,哎~~~敲代码去


硬的怕横的,横的怕不要命的,疯子都是不要命的,所以疯子力量大,程序员只有一种,疯狂的程序员。


    共勉!!!


原创文章,代码都是从自己项目里贴出来的。转载请注明出处哦,亲~~~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐