对博客园招聘信息的突发奇想

  最近刚刚离职,空闲下来的时间自己除了好好的休息之外也在琢磨着今后的路应该怎么走下去。想到博客园上不是有一个招聘的子站么,随即上去逛了逛。可是很快问题就来了,恩,是的,如你所想,上面的招聘条目太多了,虽然dudu很周到的给出了好多可以进行分类筛选的tag,可是筛选之后的信息量还是很大。另外,自己工作两年多来少有再去关注招聘这块的信息,所以也闹不太清楚现在招聘上面都说了些什么,都倾向于些什么方向。

  何不写一个工具对这些信息进行归纳整理呢?恩,这篇文章最原始的出发点就是这样而来的。觉得应该会很有意思,说干就干,先来简单的想想程序的大概样子吧。

  核心的流程大概是这样子的:

  抓取页面数据 -> 转换数据为原始数据(第一次 生成pickitem) -> 遍历转换后数据为更具体的数据对象(第二次 生成parseitem) -> 针对第二次转换后数据进行筛选,统计 -> 呈现结果

  过程中比较核心的几个业务对象大概有:

  抓取网页的picker, 解析网页的parser, 筛选数据的filter以及最后统计用的counter

  还可以再设计得详细点,再多弄几个业务对象出来,把oo做到极致,系统的稳定性,可扩展性也可以深入考虑考虑,哈哈,想多了这个程序就做不出来啦,还是先就着这些核心功能咱往下写写看吧。

  这里我以picker为例简单来说说代码:

 1 /// <summary>
 2 /// 抓取网页
 3 /// </summary>
 4 public class Picker
 5 {
 6     public IEnumerable<PickItem> PickPage(PickRule rule)
 7     {
 8         return InnerGet(rule).SelectMany(p => { 
 9                                                 var items = rule.DoPick(p);
10                                                 return items == null ? Enumerable.Empty<PickItem>() : items;
11                                                }).ToList();
12     }
13 
14     private IEnumerable<HtmlDocument> InnerGet(PickRule rule)
15     {
16         var currenturl = rule.StartURL;
17         do
18         {
19             yield return HtmlHelper.GetHtmlDocument(currenturl, rule.PageEncode);
20             currenturl = rule.CalcCurrentURL(currenturl);
21 
22         } while (currenturl != string.Empty);
23     }
24 }

  代码很简单,抓取的任务交给了HtmlHelper这个对象(里面对HtmlAgilityPack这个第三方库做了一层简单的封装)来完成。 具体抓取时的规则则是被封装在了一个叫做pickrule的对象中,pickrule的代码如下:

View Code

  程序中其它部分也大多采用的这种设计思路来完成。最后我们来看看作为驱动的“main”函数的代码,如下:

 1 protected void Run_Click(object sender, EventArgs e)
 2 {
 3     Picker picker = new Picker();
 4     PickRule pickrule = new PickRule_cnblogs();
 5     var pages = picker.PickPage(pickrule);
 6 
 7     Parser parser = new Parser();
 8     ParseRule parserule = new ParseRule_cnblogs();
 9     var parseditems = parser.ParsePage(pages, parserule, 500);
10 
11     Filter filter = new Filter(p => p != null && p.PositionCategory.ToLower() == ".net程序员");
12     var jobs = filter.Filting(parseditems).ToList();
13 
14     Counter counter = new Counter();
15     var result = counter.Counting(jobs);
16 
17     //print report
18     foreach (var item in result.OrderByDescending(p => p.Item2).Take(10))
19     {
20         piedata.Append(string.Format("['{0}', {1}],", item.Item1, item.Item2.ToString()));
21     }
22 }

  能够很清楚的看出上面提到过的那个工作流 :)

  好了,最后来看看实际运行的结果吧,首先是一张全集合上.net程序员的统计:

  再来看看上海和成都两地对于.net程序员技能的会不会有不同的要求吧:

  还是挺直观的么不是:)

 

  PS:

  1. 虽然简单,但程序在实际的写作过程中还是遇到了不少麻烦。比如在第二次的信息提取转换中parseitem对象的PositionRequire属性也就是"职位要求"一项。最初的设计是采用分词组件对信息进行分词处理,提取出有效的关键信息赋值给PositionRequire属性,用代码来看就是这样的:

  parseitem1.PositionRequire = new PanGU().Segment("抓取到的html页面中有关职位要求描述的信息");

  经过这样一番处理,PositionRequire可能会是这样子的:“,net”,“asp.net”,“mvc”等等;可是呢由于这种方法严重依赖于分词器的分词效果(查阅了相关文档要想实现我的要求可能还需要添加额外的辅助代码),所以最终采用的还是自己做了一个简单的词典列表和一个术语映射表,采用依次遍历的方式进行处理。

   

  2. 觉得dudu可以考虑为招聘子站增加这么一个数据统计分析的功能,从他那里入手问题可以更好更优雅的解决。比如事先可以将job相关的信息都定义为有格式的信息,后续就可以很方便在格式化的数据之上做进一步的分析处理。

  

  3. 恩。。。从统计的结果来看,我应该好好补充补充自己在asp.net mvc方面的知识了 :)

原文地址:https://www.cnblogs.com/pinopino/p/2700370.html