最近剛剛離職,空閑下來的時間自己除了好好的休息之外也在琢磨著今後的路應該怎麼走下去。想到部落格園上不是有一個招聘的子站麼,隨即上去逛了逛。可是很快問題就來了,恩,是的,如你所想,上面的招聘條目太多了,雖然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 do18 {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 report18 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方面的知識了 :)