1. 程式人生 > >使用HtmlAgilityPack爬取網站資訊並存儲

使用HtmlAgilityPack爬取網站資訊並存儲

前言:打算做一個藥材價格查詢的功能,但剛開始一點資料都沒有靠自己找資訊錄入的話很麻煩的,所以只有先到其它網站抓取存到資料庫再開始做這個了。

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並存到本地//方法取消註釋就好了。