C#爬蟲實踐
忘了什麽時候加的,iPad上的人人視頻追劇了《我的天才女友》,沒事的時候看了下,感覺還不錯,進一步了解到原著那不勒斯四部曲,感覺視頻進度有些慢,就想找找書看看,一時沒找到【PS:購買實體書四十多塊錢,雖然目前買得起,但是只是看看故事而不是收藏,不值得買,希望以後有機會補票,而且更習慣使用sp4看pdf或者多看多設備同步閱讀進度】,不過找到了在線觀看的網站,正好這一段時間有使用C#,就想著使用C#自動將內容抓取下來。斷斷續續的大概五六個小時的時間,終於功能上實現了。
由於沒怎麽研究過爬蟲相關知識,不知道是否符合爬蟲設計與實踐的一些原則,自己做所的不過是通過webrequest抓取頁面內容,然後人工分析尋找特征點,找到自己想要的內容。針對這次的任務首先通過目錄抓取所有的章節以及對應的頁面鏈接,然後分別抓取每個頁面的內容,將章節和內容保存起來。目錄中章節鏈接的提取和每個頁面的內容提取都是通過觀察嘗試實驗得到的,不知道是自己哪裏設計出了問題,或者就是爬蟲本身的特點,感覺很難寫出通用的爬蟲,很難得到通用的特征點,即使都是在線閱讀站點,前端代碼不一樣,提取的特征都不一樣,當前我是直接獲取頁面內容進行分析,也許會有一些成熟的庫,可以直接提取所要的內容。
不管怎麽說,折騰了一場,記錄下來,以便以後需要的時候可以查看,而且存儲在網絡上可以防止丟失。
獲取頁面內容code:
/* * 2019年1月25日09:04:14 * 輸入網址,輸出網址內容 * NOTE: * 針對項目進行的特定開發,非通用,編碼采取目標網址編碼,目前選擇GB2312 */ public string GetContent(string url) { string rStr = ""; System.Net.WebRequest req= null; System.Net.WebResponse resp = null; System.IO.StreamReader iosr = null; try { req = System.Net.WebRequest.Create(url); resp = req.GetResponse(); iosr= new System.IO.StreamReader(resp.GetResponseStream(), Encoding.GetEncoding("gb2312")); rStr = iosr.ReadToEnd(); //Console.WriteLine(rStr); } catch (Exception e) { Console.WriteLine(e.ToString()); } return rStr; }
從目錄頁獲得每個章節的名稱以及鏈接
其中獲得目標內容的起止標誌zhangjie2 /ul就是通過觀察實驗得到的,目前采用的這種方法比較原始,分析頁面內容,不同頁面可能都不一樣,不知道目前有沒有成熟的框架可以方便迅捷的實現目的。
//從初始連接中獲得每一個章節名稱以及對應的鏈接。 List<KeyValuePair<string,string>>ParserContentToGetChapters(string str) { bool StartFlag=false; string StartStr = "zhangjie2"; string Endstr = @"/ul"; // 章節 鏈接 List<KeyValuePair<string,string>>characters = new List<KeyValuePair<string,string>>(); Lexer lexer = new Lexer(str); INode node = lexer.NextNode(); while(node!=null) { string local = node.GetText(); if(local.Contains(StartStr)) { StartFlag = true; } if(local.Contains(Endstr)) { StartFlag = false; } if(StartFlag) { if(local.Contains("href")) { List<string> tmp = this.GetHerfAndTitle(local); characters.Add(new KeyValuePair<string, string>(tmp[1],tmp[0])); } } node = lexer.NextNode(); } return characters; }
List<string> GetHerfAndTitle(string input) { List<string> ret = new List<string>(); string[] strs = input.Split(‘"‘); int stageflag = 0; foreach(string str in strs) { if(str.Contains("a href=")) { stageflag = 1; continue; } else if(str.Contains("title=")) { stageflag = 2; continue; } if(stageflag==0) { continue; } else if(stageflag==1) { ret.Add(websit + str); } else if(stageflag==2) { ret.Add(str); break; } else { break; } } return ret; }
獲得每個章節內容:
KeyValuePair<string,string> GetSinglePage(string Name,string url) { KeyValuePair<string, string> ret = new KeyValuePair<string, string>(); string content = ""; string tmp = this.GetContent(url); content = this.SinglePageContent(tmp); ret = new KeyValuePair<string, string>(Name, content); return ret; } //從單個頁面中找出所有的內容 string SinglePageContent(string all) { string ret = ""; bool StartFlag = false; string StartStr = "div id=\"onearcxsbd\" class=\"onearcxsbd\""; string Endstr = @"分頁"; // 章節 鏈接 List<KeyValuePair<string, string>> characters = new List<KeyValuePair<string, string>>(); Lexer lexer = new Lexer(all); INode node = lexer.NextNode(); while (node != null) { string local = node.GetText(); if (local.Contains(StartStr)) { Console.WriteLine("start"); StartFlag = true; node = lexer.NextNode(); continue; } else if(local == Endstr) { Console.WriteLine("end"); StartFlag = false; } if (StartFlag) { if(local == "br /") { } else { ret += local; } } node = lexer.NextNode(); } ret = this.DealString(ret); Console.WriteLine(ret); return ret; } //將一些html中的轉義字符恢復 string DealString(string input) { string ret = input; Dictionary<string, string> localdict = new Dictionary<string, string>(); localdict.Add("·", "·"); localdict.Add("“", "“"); localdict.Add("”", "”"); localdict.Add("—", "—"); foreach (var tmp in localdict) { ret = ret.Replace(tmp.Key, tmp.Value); } return ret; }
其他:
1.編碼問題,後面需要能夠自動獲取頁面所使用的編碼,當初因為使用簡繁體操作系統的緣故,浪費了不少時間;
2.最開始考慮使用Dictionary,不過Dictionary對順序無法保證,改用List<KeyValuePair<string,string>>,防止章節順序錯亂;
3.統一匯總讀寫還是單條逐個讀寫,各有利弊,最終采取單個章節單獨讀寫,同時有對內容進行匯總;
4.直接得到的html頁面內容中,有些轉義字符需要恢復本來的字符,比如· 轉換為 · 等。
C#爬蟲實踐