使用HtmlAgilityPack爬取網站資訊並存儲
阿新 • • 發佈:2018-11-28
前言:打算做一個藥材價格查詢的功能,但剛開始一點資料都沒有靠自己找資訊錄入的話很麻煩的,所以只有先到其它網站抓取存到資料庫再開始做這個了。
HtmlAgilityPack在c#裡應該很多人用吧,簡單又強大。之前也用它做過幾個爬取資訊的小工具。不過很久了原始碼都沒有了,都忘了怎麼用了,這次也是一點一點找資料慢慢做出來的!
(不過最麻煩的是將資料存到mysql,.net資料庫我一直用的都是mssql,所以第一次做連線mysql遇到了好多問題。)
1、使用HtmlAgilityPack
- 下載HtmlAgilityPack類庫,並引用到你的專案
我這裡使用的控制檯專案
專案新增引用
程式碼裡新增引用
2、分析網頁
- 網頁地址:http://www.zyctd.com/jiage/1-0-0-0.html
首先看每一頁的url變化,觀察後發現這個很簡單:
第一頁就是:1-0-0或者1-0-0-1表示第一頁
第二頁就是:1-0-0-2一次類推
- 然後再分析他的原始碼
很明顯這一頁的資料都放在了ul標籤裡了,而且還有類名:<ul class="priceTableRows">,
然後再看下ul下的li標籤,li標籤裡的html寫的也都相同,然後就可以開始寫程式碼抓取了。
3、抓取資訊
- 首先新建一個類檔案,來儲存抓取的資訊。因為我是直接存到資料庫用的是ado.net實體資料模型生成的檔案。
- 下面是ado.net實體資料模型生成的檔案:
//------------------------------------------------------------------------------ // <auto-generated> // 此程式碼已從模板生成。 // // 手動更改此檔案可能導致應用程式出現意外的行為。 // 如果重新生成程式碼,將覆蓋對此檔案的手動更改。 // </auto-generated> //------------------------------------------------------------------------------ namespace 測試專案1 { using System; using System.Collections.Generic; public partial class C33hao_price { public long ID { get; set; } public string Name { get; set; } public string Guige { get; set; } public string Shichang { get; set; } public decimal Price { get; set; } public string Zoushi { get; set; } public decimal Zhouzd { get; set; } public decimal Yuezd { get; set; } public decimal Nianzd { get; set; } public int editDate { get; set; } public string other { get; set; } } }
- 下面這個是剛開始測試存到本地時寫的類:
-
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 測試專案1 { public class Product { /// <summary> /// 品名 /// </summary> public string Name { get; set; } /// <summary> /// 規格 /// </summary> public string Guige { get; set; } /// <summary> /// 市場 /// </summary> public string Shichang { get; set; } /// <summary> /// 最新價格 /// </summary> public string Price { get; set; } /// <summary> /// 走勢 /// </summary> public string Zoushi { get; set; } /// <summary> /// 周漲跌 /// </summary> public string Zhouzd { get; set; } /// <summary> /// 月漲跌 /// </summary> public string Yuezd { get; set; } /// <summary> /// 年漲跌 /// </summary> public string Nianzt { get; set; } } }
下面是主要的處理程式碼
-
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HtmlAgilityPack; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace 測試專案1 { public class Program { /// <summary> /// 本地測試資訊類 /// </summary> static List<Product> ProductList = new List<Product>(); /// <summary> /// 資料庫生成的資訊類 /// </summary> static List<C33hao_price> PriceList = new List<C33hao_price>(); public static void Main(string[] args) { int start = 1;//開始頁數 int end = 10;//結束頁數 Console.WriteLine("請輸入開始和結束頁數例如1-100,預設為1-10"); string index = Console.ReadLine();//獲取使用者輸入的頁數 if(index != "") { //分割頁數 string[] stt = index.Split('-'); start = Int32.Parse(stt[0]); end = Int32.Parse(stt[1]); } //迴圈抓取 for(int i = start; i<= end; i++) { string url = string.Format("http://www.zyctd.com/jiage/1-0-0-{0}.html", i); HtmlWeb web = new HtmlWeb(); HtmlDocument doc = web.Load(url);//獲取網頁 HtmlNode node = doc.DocumentNode; string xpathstring = "//ul[@class='priceTableRows']/li";//路徑 HtmlNodeCollection aa = node.SelectNodes(xpathstring);//獲取每一頁ul下的所有li標籤裡的html if (aa == null) { Console.WriteLine("出錯:當前頁為{0}", i.ToString()); continue; } foreach(var item in aa) { //處理li標籤資訊新增到集合 string cc = item.InnerHtml; test(cc); } } //寫入json並存到本地 //string path = "json/test.json"; //using(StreamWriter sw = new StreamWriter(path)) //{ // try // { // JsonSerializer serializer = new JsonSerializer(); // serializer.Converters.Add(new JavaScriptDateTimeConverter()); // serializer.NullValueHandling = NullValueHandling.Ignore; // //構建Json.net的寫入流 // JsonWriter writer = new JsonTextWriter(sw); // //把模型資料序列化並寫入Json.net的JsonWriter流中 // serializer.Serialize(writer,ProductList); // //ser.Serialize(writer, ht); // writer.Close(); // sw.Close(); // } // catch (Exception ex) // { // string error = ex.Message.ToString(); // Console.WriteLine(error); // } //} int count = PriceList.Count();//抓取到的資訊條數 Console.WriteLine("獲取資訊{0}條", count); Console.WriteLine("開始新增到資料庫"); Insert();//插入到資料庫 Console.WriteLine("資料新增完畢"); Console.ReadLine(); } /// <summary> /// 處理資訊並新增到集合中 /// </summary> /// <param name="str">li標籤的html內容</param> static void test(string str) { //Product product = new Product(); C33hao_price Price = new C33hao_price(); HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(str); HtmlNode node = doc.DocumentNode; //獲取藥材名稱 string namepath = "//span[@class='w1']/a[1]";//名稱路徑 HtmlNodeCollection DomNode = node.SelectNodes(namepath);//根據路徑獲取內容 //product.Name = DomNode[0].InnerText; Price.Name = DomNode[0].InnerText;//將內容新增到物件中 //獲取規格 string GuigePath = "//span[@class='w2']/a[1]"; DomNode = node.SelectNodes(GuigePath); //product Price.Guige = DomNode[0].InnerText; //獲取市場名稱 string adsPath = "//span[@class='w9']"; DomNode = node.SelectNodes(adsPath); Price.Shichang = DomNode[0].InnerText; //獲取最新價格 string pricePath = "//span[@class='w3']"; DomNode = node.SelectNodes(pricePath); Price.Price = decimal.Parse(DomNode[0].InnerText); //獲取走勢 string zoushiPath = "//span[@class='w4']"; DomNode = node.SelectNodes(zoushiPath); Price.Zoushi = DomNode[0].InnerText; //獲取周漲跌 string zhouzdPath = "//span[@class='w5']/em[1]"; DomNode = node.SelectNodes(zhouzdPath); Price.Zhouzd = decimal.Parse(GetZD(DomNode[0].InnerText)); //獲取月漲跌 string yuezdPath = "//span[@class='w6']/em[1]"; DomNode = node.SelectNodes(yuezdPath); Price.Yuezd = decimal.Parse(GetZD(DomNode[0].InnerText)); //獲取年漲跌 string nianzdPath = "//span[@class='w7']/em[1]"; DomNode = node.SelectNodes(nianzdPath); Price.Nianzd = decimal.Parse(GetZD(DomNode[0].InnerText)); //新增時間 Price.editDate = Int32.Parse(GetTimeStamp());//轉換為時間戳格式,方便php使用 //ProductList.Add(product); PriceList.Add(Price);//新增到物件集合 } //查詢 static void Query() { var context = new mallEntities(); var member = from e in context.member select e; foreach(var u in member) { Console.WriteLine(u.member_name); Console.WriteLine(u.member_mobile); } Console.ReadLine(); } //插入 static void Insert() { var context = new mallEntities(); C33hao_price Price = new C33hao_price(); int i = 0; foreach (C33hao_price item in PriceList) { context.C33hao_price.Add(item); context.SaveChanges(); i++; Console.WriteLine("{0}/{1}", i, PriceList.Count); } } /// <summary> /// 獲取時間戳 /// </summary> /// <returns></returns> public static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /// <summary> /// 去除字串中的百分比 /// </summary> /// <param name="str">處理的字串</param> /// <returns></returns> public static string GetZD(string str) { string st = str.Substring(0, str.Length - 1); return st; } } }
- 以上程式碼主要是存到資料庫,下面說下怎麼存到本地。
-
4、儲存到本地
儲存到本地只需要把test方法裡的Price物件改為Product型別,然後再add到ProductList集合裡,再把註釋的//寫入json並存到本地//方法取消註釋就好了。