最近工作中需求定時爬取不同城市每天的溫度。其實就是通過編程的方法去抓取不同網站網頁進行分析篩選的過程。.NET提供了很多類去訪問並獲得遠程網頁的數據,比如WebClient類和HttpWebRequest類。這些類對於利用HTTP去訪問遠端的網頁並且下載下來是很有用的,但在對於所下載下來的HTML的解析能力方面,則顯得功能很弱了。推薦一個開源的組件HTML Agility Pack(http://htmlagilitypack.codeplex.com/),它的設計目標是盡可能簡化對HTML文檔的讀和寫。這個包本身是利用了DOM文檔對象模型去解析HTML的。在此順便記錄一下最近收集的爬取歷史和當前天氣的網站備用:
- 支持歷史天氣查詢的中文網站:http://lishi.tianqi.com/
- 類似網站:http://tianqi.2345.com/wea_history/59287.htm (最近2-3年數據)
- 網站:http://www.wunderground.com/history/ (最近18年數據)
- 這個網站支持的時間更長: http://www.tutiempo.net/en/Climate/
- 中國天氣網:http://www.weather.com.cn/,
中國天氣網(Weather.com.cn)
- 該網提供有如下三個Json格式的查詢接口,以北京為例:
- http://www.weather.com.cn/data/sk/101010100.html
- http://www.weather.com.cn/data/cityinfo/101010100.html
- http://m.weather.com.cn/data/101010100.html
- ID是一個9位的數字,按照長度可以分為如下四部分:101(國家代號) 01(省) 01(二級地區) 00(三級地區)
- 要獲取所有的地區代號,通過如下方式:https://wqbot.blob.core.windows.net/botdemo/CityCode.xml
示例Demo
編程使用示例如下:我們要獲取如下網頁中的天氣信息:
下載HTML Agility Pack組件,新建控制台程序,在你的工程中引用相應framework版本對應的組件,示例代碼如下:
string url = @"http://lishi.tianqi.com/beijing/201701.html"; var webGet = new HtmlWeb(); var document = webGet.Load(url); var div = document.DocumentNode.SelectNodes("//div[@class='tqtongji2']/ul"); foreach (HtmlNode node in div) { var tmpNode = node.SelectNodes("li"); Console.WriteLine(string.Format("{0}-----------{1}---------{2}----------{3}", tmpNode[0].InnerText, tmpNode[1].InnerText, tmpNode[2].InnerText, tmpNode[3].InnerText)); } Console.ReadKey();
程序運行效果:中文存在亂碼,如下圖
通過分析HTML Agility Pack源碼,在HtmlWeb類的Get(Uri uri, string method, string path, HtmlDocument doc)方法中,局部變量 resp是http請求的response。設置斷點發現resp.ContentEncoding為空。因此通過HttpWebRequest來下載數據,示例代碼如下:
string url = @"http://lishi.tianqi.com/beijing/201701.html"; HttpWebRequest req = WebRequest.Create(new Uri(url)) as HttpWebRequest; req.Method = "GET"; WebResponse rs = req.GetResponse(); Stream rss = rs.GetResponseStream(); HtmlDocument doc = new HtmlDocument(); doc.Load(rss); var div = doc.DocumentNode.SelectNodes("//div[@class='tqtongji2']/ul"); foreach (HtmlNode node in div) { var tmpNode = node.SelectNodes("li"); Console.WriteLine(string.Format("{0}-----------{1}---------{2}----------{3}", tmpNode[0].InnerText, tmpNode[1].InnerText, tmpNode[2].InnerText, tmpNode[3].InnerText)); } Console.ReadKey();
代碼運行效果如下:
that's ok!!!