您的位置:首页 > 其它

[开源 .NET 跨平台 数据采集 爬虫框架: DotnetSpider] [三] 模型配置方式实现爬虫

2016-06-17 14:42 866 查看
上一篇介绍了最基础的使用方法,也是最自由最灵活的方式,但是其实大多数情况下是可以用模型配置的方式实现一个爬虫的。我们做框架,肯定要尽可能的把一些重复性的工作帮你们做掉,当然灵活性等也会打折扣,但肯定有存在的意义。

另,爬虫已经对dotnet core rc2 做了适配,可以在dotnet core rc2环境下运行啦

定义数据对象类

[Schema("test", "sku", TableSuffix.Today)]
[TypeExtractBy(Expression = "//li[@class='gl-item']/div[contains(@class,'j-sku-item')]", Multi = true)]
[Indexes(Index = new[] { "category" }, Unique = new[] { "category,sku", "sku" })]
public class Product : ISpiderEntity
{
[StoredAs("category", DataType.String, 20)]
[PropertyExtractBy(Expression = "name", Type = ExtractType.Enviroment)]
public string CategoryName { get; set; }

[StoredAs("cat3", DataType.String, 20)]
[PropertyExtractBy(Expression = "cat3", Type = ExtractType.Enviroment)]
public int CategoryId { get; set; }

[StoredAs("url", DataType.Text)]
[PropertyExtractBy(Expression = "./div[1]/a/@href")]
public string Url { get; set; }

[StoredAs("sku", DataType.String, 25)]
[PropertyExtractBy(Expression = "./@data-sku")]
public string Sku { get; set; }

[StoredAs("commentscount", DataType.String, 32)]
[PropertyExtractBy(Expression = "./div[5]/strong/a")]
public long CommentsCount { get; set; }

[StoredAs("shopname", DataType.String, 100)]
[PropertyExtractBy(Expression = ".//div[@class='p-shop']/@data-shop_name")]
public string ShopName { get; set; }

[StoredAs("name", DataType.String, 50)]
[PropertyExtractBy(Expression = ".//div[@class='p-name']/a/em")]
public string Name { get; set; }

[StoredAs("venderid", DataType.String, 25)]
[PropertyExtractBy(Expression = "./@venderid")]
public string VenderId { get; set; }

[StoredAs("jdzy_shop_id", DataType.String, 25)]
[PropertyExtractBy(Expression = "./@jdzy_shop_id")]
public string JdzyShopId { get; set; }

[StoredAs("run_id", DataType.Date)]
[PropertyExtractBy(Expression = "Monday", Type = ExtractType.Enviroment)]
public DateTime RunId { get; set; }

[PropertyExtractBy(Expression = "Now", Type = ExtractType.Enviroment)]
[StoredAs("cdate", DataType.Time)]
public DateTime CDate { get; set; }
}


数据对象必须继承ISpiderEntity

Schema定义数据库及表名,可以定义表名后缀

Indexes定义:主键、唯一索引、索引,如果是由多个列构成索引用,号隔开

TypeExtractBy定义这个数据对象的抽取规则, 如上面的数据对象是用于抓取京东的商品信息,我们打开一个页面看一下:




每个商品为一个li, 并且我们需要存为一行数据,因此TypeExtractBy我们可以指定为XPATH, 并且填上能够把所有元素查询出来表达式://li[@class='gl-item'], 同时把Multi设置为True以告诉爬虫这个表达式查询结果为List. 当你熟练度够了之后,这个表达式还可以改成我上面的

//li[@class='gl-item']/div[contains(@class,'j-sku-item')]

原因是京东有一个DIV藏了好几个套装的情况,每个套装又是一个SKU,以上XPATH可以尽可能多的取到数据

  5. 定义数据列:首先定义一个public的属性,在属性上添加StoreAs用于定义在数据库中的存储类型,同时定义该属性的抽取规则PropertyExtractBy,这时候要注意的是,属性的抽取是要计算相对行上面数据行的PATH,而不是绝对的。如下,



我们行首先已经定位到了gl-i-warp j-sku-item这个DIV,假设你想从这个DIV的attribute中取得sku数据,这时你应该这么定义(.表示当前节点,具体可以去w3school学一下XPATH)

[StoredAs("sku", DataType.String, 25)]
[PropertyExtractBy(Expression = "./@data-sku")]
public string Sku { get; set; }

  6. 环境变量:我们经常用到的一个场景是,抓取SKU的链接是从category过来的,我们想保留category的信息到SKU表中,这时候如何把category的信息传递到数据对象中呢? 需要做两步:

第一是在初始化的StartUrl时要添加信息

context.AddStartUrl("http://list.jd.com/list.html?cat=9987,653,655&page=2&JL=6_0_0&ms=5#J_main", new Dictionary<string, object> { { "name", "手机" }, { "cat3", "655" } });

第二是定义环境变量属性抽取规则:

[StoredAs("category", DataType.String, 20)]
[PropertyExtractBy(Expression = "name", Type = ExtractType.Enviroment)]
public string CategoryName { get; set; }

  7. 特殊数据:大多会用到当天时间,或者星期一等特殊时间,我们也可以使用环境变量来实现

[StoredAs("run_id", DataType.Date)]
[PropertyExtractBy(Expression = "Monday", Type = ExtractType.Enviroment)]
public DateTime RunId { get; set; }

[PropertyExtractBy(Expression = "Now", Type = ExtractType.Enviroment)]
[StoredAs("cdate", DataType.Time)]
public DateTime CDate { get; set; }

定义爬虫

定义一个类,继承SpiderBuilder

protected override SpiderContext GetSpiderContext()
{
SpiderContext context = new SpiderContext();
context.SetSpiderName("JD sku/store test " + DateTime.Now.ToString("yyyy-MM-dd HHmmss"));
context.AddTargetUrlExtractor(new Extension.Configuration.TargetUrlExtractor
{
Region = new Extension.Configuration.Selector { Type = ExtractType.XPath, Expression = "//span[@class=\"p-num\"]" },
Patterns = new List<string> { @"&page=[0-9]+&" }
});
context.AddPipeline(new MysqlPipeline
{
ConnectString = "Database='test';Data Source=;User ID=root;Password=;Port=4306"
});
context.AddStartUrl("http://list.jd.com/list.html?cat=9987,653,655&page=2&JL=6_0_0&ms=5#J_main", new Dictionary<string, object> { { "name", "手机" }, { "cat3", "655" } });
context.AddEntityType(typeof(Product));
return context;
}


使用SetSpiderName设置爬虫名称,不合规范的名字会抛异常

使用AddTargetUrlExtractor配置目标页的解析器(在定题爬虫中一般是翻页), Region表式你需要在哪一个元素下面的url, 正则的作用是把找到的所有url用正则作匹配,符合规则的就是你想要的合法翻页链接。如下图,我们把翻页的区域定位到class为p-nm这个span下面,同时用正则&page=[0-9]+&来做筛选



  3. 配置URL调度,如果不配置则默认使用内存Queue, 可以使用已经实现的Queue或者RedisScheduler(用于分布式)

context.SetScheduler(new RedisScheduler
{
Host = "",
Password = "",
Port = 6379
});


  4. 我们添加一个MySql的数据管道,只需要配置好你的连接字符串即可

  5. 添加StartUrl, 这里可以添加额外数据用于传递到目标数据对象

  6. 添加目标数据对象:

context.AddEntityType(typeof(Product));

  7. 运行爬虫:

JdSkuSampleSpider spiderBuilder = new JdSkuSampleSpider();
spiderBuilder.Run("rerun");






  8. 额外配置:大家可以自己查看API,可以配置线程数SetThreadNum, 在广义搜索爬取时控制深度(SetDeep), 设置Pipeline的数据提交频次SetCachedSize等

完整代码参见:

https://raw.githubusercontent.com/zlzforever/DotnetSpider/master/src/Java2Dotnet.Spider.Test/Example/JdSkuSample.cs

可以看到,只需区区几行代码就可以实现一个爬虫,帮你建表、分表,抓取、抽取数据,还是比较简单的

下一篇

介绍如何抓取一些API,JSONPATH的数据


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: