前言:打算做一个药材价格查询的功能,但刚开始一点数据都没有靠自己找信息录入的话很麻烦的,所以只有先到其它网站抓取存到数据库再开始做这个了。
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并存到本地//方法取消注释就好了。