前言:打算做一個藥材價格查詢的功能,但剛開始一點數據都沒有靠自己找信息錄入的話很麻煩的,所以只有先到其它網站抓取存到數據庫再開始做這個了。
HtmlAgilityPack在c#裡應該很多人用吧,簡單又強大。之前也用它做過幾個爬取信息的小工具。不過很久了源代碼都沒有了,都忘了怎麼用了,這次也是一點一點找資料慢慢做出來的!
(不過最麻煩的是將數據存到mysql,.net數據庫我一直用的都是mssql,所以第一次做連接mysql遇到了好多問題。)
1、使用HtmlAgilityPack
我這裡使用的控制台項目

項目添加引用

代碼裡添加引用
2、分析網頁
首先看每一頁的url變化,觀察後發現這個很簡單:
第一頁就是:1-0-0或者1-0-0-1表示第一頁
第二頁就是:1-0-0-2一次類推

很明顯這一頁的數據都放在了ul標簽裡了,而且還有類名:<ul class="priceTableRows">,
然後再看下ul下的li標簽,li標簽裡的html寫的也都相同,然後就可以開始寫代碼抓取了。

3、抓取信息
//------------------------------------------------------------------------------
// <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 mallyobo360Entities();
var member = from e in context.C33hao_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 mallyobo360Entities();
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並存到本地//方法取消注釋就好了。
待續。。。。。。。。。。