一、迟到的下期预告

自从上一篇文章发布到现在,大约差不多有3个月的样子,其实一直想把这个实战入门系列的教程写完,一个是为了支持DotnetSpider,二个是为了.Net 社区发展献出一份绵薄之力,这个开源项目作者一直都在更新,相对来说还是很不错的,上次教程的版本还是2.4.4,今天浏览了一下这个项目,最近一次更新是在3天前,已经更新到了2.5.0,而且项目star也已经超过1000了,还是挺受大家所喜爱的,也在这感谢作者们不断的努力。

之所以中间这么长一段时间没有好好写文章,是因为笔者为参加3月份PMP考试备考了,然后考完差不多就到4月份,到了4月份,项目催的急,所以一直拖到了现在才有一点空余时间,希望我的文章完成之后,会对大家有一定的帮助。

二、更新DotnetSpider库

刚刚提到DotnetSpider升级到了2.5.0,所以我们更新一下库,使用最新的版本,技术要与时俱进嘛,将下图中两个类库添加进去就行了。

三、分析汽车之家商品详情页面

3.1分析商品详情页数据

①上次我们发现,当点击参数配置的时候,页面会发送两个ajax请求,分别去获取车型的基本参数,和配置参数,返回的数据是JSON格式。

②通过Chrome的Network抓包可以发现,这两个请求有一个共同点,提交的参数都有data[specid]:28762,我猜测这应该是skuid,大家可以试着在浏览器直接打开这两个地址,直接就可以返回出车型的相关信息,所以问题的关键就是要解决如何获取skuid的问题,获取到了这些车型的数据那不就是手到擒来了。

3.2如何获取商品的sku

其实这个确实是让我很苦恼的一个问题,因为我们打开页面的时候页面的链接中并未包含skuid,[https://mall.autohome.com.cn/detail/284641-0-0.html],所以从URL中获取是不太现实的了,所以我用Element去页面中搜索这个skuid的值,结果发现两个地方有这个值,一个存在于HTML元素中,一个是存在js全局变量中,相比较而言,我认为HTML元素中的相对来说会比较好处理一点,直接获取元素的属性就行了。

四、开发

4.1准备Processor

这次为什么要单独的写一段准备Processor呢,因为此次准备了三个Processor,分别用来处理3个数据,第一个用于获取skuid,第二个用于获取车型基本参数,第三个用于获取车型配置,细心的小伙伴们肯定会发现,这次我们每个Processor都使用了构造函数,里面可以清晰的看到有我们熟悉的正则表达式(PS正则表达式写的很烂,自己有更好的正则写法可以回复在评论里面,让我膜拜一下,嘻嘻),肯定会有人问为什么要这么写呢?

相比上次的教程,这次的抓取流程更为的复杂了,上次我们只是抓了一个列表页,一个接口完全可以搞定,此次我们的流程变成了,第一步,我们需要获取车型详情页的页面,从页面中找到skuid,第二部,将获取的skuid拼接好request放入爬虫的请求集合中,通过新构造的请求去获取数据,那么我们怎么知道哪个请求用哪个Processor进行处理呢,DotnetSpider提供了对url进行校验的进行判断,使用哪个Processor对数据进行处理。

        private class GetSkuProcessor : BasePageProcessor //获取skuid
{
public GetSkuProcessor()
{
TargetUrlsExtractor = new RegionAndPatternTargetUrlsExtractor(".", @"^https:\/\/mall.autohome.com.cn\/detail\/*[0-9]+-[0-9]+-[0-9]+\.html$");
}
protected override void Handle(Page page)
{
string skuid = string.Empty;
skuid = page.Selectable.XPath(".//a[@class='carbox-compare_detail']/@link").GetValue();
page.AddResultItem("skuid", skuid);
page.AddTargetRequest(@"https://mall.autohome.com.cn/http/data.html?data[_host]=//car.api.autohome.com.cn/v1/carprice/spec_paramsinglebyspecid.ashx&data[_appid]=mall&data[specid]=" + skuid);
page.AddTargetRequest(@"https://mall.autohome.com.cn/http/data.html?data[_host]=//car.api.autohome.com.cn/v2/carprice/Config_GetListBySpecId.ashx&data[_appid]=mall&data[specid]=" + skuid);
} }
private class GetBasicInfoProcessor : BasePageProcessor //获取车型基本参数
{
public GetBasicInfoProcessor()
{
TargetUrlsExtractor = new RegionAndPatternTargetUrlsExtractor(".", @"^https://mall\.autohome\.com\.cn/http/data\.html\?data\[_host\]=//car\.api\.autohome\.com\.cn/v1/carprice/spec_paramsinglebyspecid\.ashx*");
}
protected override void Handle(Page page)
{
page.AddResultItem("BaseInfo", page.Content);
}
} private class GetExtInfoProcessor : BasePageProcessor //获取车型配置
{
public GetExtInfoProcessor()
{
TargetUrlsExtractor = new RegionAndPatternTargetUrlsExtractor(".", @"^https://mall\.autohome\.com\.cn\/http\/data\.html\?data\[_host\]=//car\.api\.autohome\.com\.cn/v2/carprice/Config_GetListBySpecId\.ashx*");
}
protected override void Handle(Page page)
{
page.AddResultItem("ExtInfo", page.Content);
}
}

4.2、创建Pipeline

Pipeline基本上变化不大,稍微改造了一下,so easy。

        private class PrintSkuPipe : BasePipeline
{ public override void Process(IEnumerable<ResultItems> resultItems, ISpider spider)
{
foreach (var resultItem in resultItems)
{
if (resultItem.GetResultItem("skuid") != null)
{
Console.WriteLine(resultItem.Results["skuid"] as string);
}
if (resultItem.GetResultItem("BaseInfo") != null)
{
var t = JsonConvert.DeserializeObject<AutoCarParam>(resultItem.Results["BaseInfo"]);
//Console.WriteLine(resultItem.Results["BaseInfo"]);
}
if (resultItem.GetResultItem("ExtInfo") != null)
{
var t = JsonConvert.DeserializeObject<AutoCarConfig>(resultItem.Results["ExtInfo"]);
//Console.WriteLine(resultItem.Results["ExtInfo"]);
} } }
}

新增两个实体类AutoCarParam,AutoCarConfig,其实有重复的,子项可以再进行抽象一下,代码可以减少,还可以节省一点硬盘空间

    public class AutoCarConfig
{
public string message { get; set; }
public ConfigResult result { get; set; }
public string returncode { get; set; }
}
public class ConfigResult
{
public string specid { get; set; } public List<configtypeitem> configtypeitems { get; set; }
} public class configtypeitem
{
public string name { get; set; }
public List<configitem> configitems { get; set; }
}
public class configitem
{
public string name { get; set; }
public string value { get; set; }
} public class AutoCarParam
{
public string message { get; set; }
public ParamResult result { get; set; }
public string returncode { get; set; }
} public class ParamResult
{
public string specid { get; set; } public List<paramtypeitem> paramtypeitems { get; set; }
} public class paramtypeitem
{
public string name { get; set; }
public List<paramitem> paramitems { get;set;}
}
public class paramitem
{
public string name { get; set; }
public string value { get; set; }
}

4.3、构造爬虫

这一块变化也不是很大,变化的的地方看我的注释,因为我们需要有多个Processor,把这几个都添加进去就行。

            var site = new Site
{
CycleRetryTimes = ,
SleepTime = ,
Headers = new Dictionary<string, string>()
{
{ "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" },
{ "Cache-Control","no-cache" },
{ "Connection","keep-alive" },
{ "Content-Type","application/x-www-form-urlencoded; charset=UTF-8" },
{ "User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"}
} };
List<Request> resList = new List<Request>();
Request res = new Request();
res.Url = "https://mall.autohome.com.cn/detail/284641-0-0.html";
res.Method = System.Net.Http.HttpMethod.Get;
resList.Add(res);
var spider = Spider.Create(site, new QueueDuplicateRemovedScheduler(), new GetSkuProcessor(),new GetBasicInfoProcessor(),new GetExtInfoProcessor()) //因为我们有多个Processor,所以都要添加进来
.AddStartRequests(resList.ToArray())
.AddPipeline(new PrintSkuPipe());
spider.ThreadNum = ;
spider.Run();
Console.Read();

五、执行结果

六、总结

这次憋了那么久才写第二篇文章的,实属惭愧,本来2月底这篇文章就要出来的,一直拖到现在,上一篇文章的阅读量整体来说还是不错的超出了我的预期,下面评论也有小伙伴希望我快点出这篇文章,整体来说还是不错的。这次的文章我希望就是说不仅仅是大家学会了如何去使用DotnetSpider,并且能够让大家了解一下如何爬数据,给大家提供一点点思路,所以我会结合实际场景来写这篇文章,不然的话,感觉会太过于枯燥了。

希望大家多多拍砖

七、下期预告

下次文章会涉及文件抓取

顺便说一句,有需要参加PMP或者高项考试的可以联系我,我有一些资料可以提供

2018年5月13日

汽车之家店铺商品详情数据抓取 DotnetSpider实战[二]的更多相关文章

  1. 汽车之家店铺数据抓取 DotnetSpider实战[一]

    一.背景 春节也不能闲着,一直想学一下爬虫怎么玩,网上搜了一大堆,大多都是Python的,大家也比较活跃,文章也比较多,找了一圈,发现园子里面有个大神开发了一个DotNetSpider的开源库,很值得 ...

  2. 汽车之家店铺数据抓取 DotnetSpider实战

    一.背景 春节也不能闲着,一直想学一下爬虫怎么玩,网上搜了一大堆,大多都是Python的,大家也比较活跃,文章也比较多,找了一圈,发现园子里面有个大神开发了一个DotNetSpider的开源库,很值得 ...

  3. 汽车之家汽车品牌Logo信息抓取 DotnetSpider实战[三]

    一.正题前的唠叨 第一篇实战博客,阅读量1000+,第二篇,阅读量200+,两篇文章相差近5倍,这个差异真的令我很费劲,截止今天,我一直在思考为什么会有这么大的差距,是因为干货变少了,还是什么原因,一 ...

  4. Hawk 数据抓取工具 使用说明(二)

    1. 调试模式和执行模式 1.1.调试模式 系统能够通过拖拽构造工作流.在编辑流的过程中,处于调试模式,为了保证快速地计算和显示当前结果(只显示前20个数据,可在调试的采样量中修改),此时,所有执行器 ...

  5. 用Vue来实现音乐播放器(二十一):歌手详情数据抓取

    第一步:在api文件夹下的singer.js中抛出getSingerDetail方法 第二步:在singer-detail.vue组件中引入api文件夹下的singer.js和config.js 第三 ...

  6. Twitter数据抓取的方法(二)

    Scraping Tweets Directly from Twitters Search Page – Part 2 Published January 11, 2015 In the previo ...

  7. Python爬虫:新浪新闻详情页的数据抓取(函数版)

    上一篇文章<Python爬虫:抓取新浪新闻数据>详细解说了如何抓取新浪新闻详情页的相关数据,但代码的构建不利于后续扩展,每次抓取新的详情页时都需要重新写一遍,因此,我们需要将其整理成函数, ...

  8. Phantomjs+Nodejs+Mysql数据抓取(1.数据抓取)

    概要: 这篇博文主要讲一下如何使用Phantomjs进行数据抓取,这里面抓的网站是太平洋电脑网估价的内容.主要是对电脑笔记本以及他们的属性进行抓取,然后在使用nodejs进行下载图片和插入数据库操作. ...

  9. 网页数据抓取工具,webscraper 最简单的数据抓取教程,人人都用得上

    Web Scraper 是一款免费的,适用于普通用户(不需要专业 IT 技术的)的爬虫工具,可以方便的通过鼠标和简单配置获取你所想要数据.例如知乎回答列表.微博热门.微博评论.淘宝.天猫.亚马逊等电商 ...

随机推荐

  1. 【面试必备】Swift&nbsp;面试题及其答案

    原文:Swift Interview Questions and Answers 原作者:Antonio Bello 原作者介绍: Antonio 拥有丰富的编程经验.他开始编程的时候,内存单位还是 ...

  2. 三层登录实例VB.NET版详解---理论加实战篇

    层,百度百科这样解释,首先-重叠起来的东西:重叠起来的东西中的一部分:层次|表层|大气层.其次-重叠:重复:层峦叠嶂|层出不穷.最后-量词,用于可以分出层次的事物,女孩儿强烈的第六感,三层中的层一定是 ...

  3. MacBook 最近发现的一些问题和技巧

    本猫的mba最近键盘莫名会失灵,但用鼠标切换其他用户时时好的,切换回来又不行,体现如下: 1.Spotlight里可以输入,其他不可以 2.cmd+tab可以切换进程 现在只有重启后才可以恢复. 网上 ...

  4. JavaScript进阶(三)常见工具(校验、通用)

    JS常见工具(校验.通用) // 姓名校验 var checkName = function(name) { // 收货人姓名校验(准则:姓名为2-4汉字) var regu = /^[\u4E00- ...

  5. STL算法设计理念 - 谓词,一元谓词demo

    谓词: 一元函数对象:函数参数1个: 二元函数对象:函数参数2个: 一元谓词 函数参数1个,函数返回值是bool类型,可以作为一个判断式 谓词可以使一个仿函数,也可以是一个回调函数. demo 一元谓 ...

  6. Volley网络框架完全解析(缓存篇)

    在上一篇中讲完了Volley框架怎么使用,那么这篇就来讲讲Volley框架的缓存机制 我们看Volley内部源码发现: Volley框架内部自己处理了DiskBasedCache硬盘缓存,但是没有处理 ...

  7. UTL_DBWS - Consuming Web Services in Oracle 10g Onward

    from:http://oracle-base.com/articles/10g/utl_dbws-10g.php In a previous article I presented a method ...

  8. 【46】java对象的三个特征(oop)

    对象的三个特征: 对象的行为: 对象有哪些行为和方法 同一个类的实例,由于支持相同的行为具有家族式的特征,对象的行为是由方法定义的. 对象的状态: 施加方法时.对象的响应 每个对象都保存着描述当前特征 ...

  9. Android高效率编码-细节,控件,架包,功能,工具,开源汇总,你想要的这里都有

    Android高效率编码-细节,控件,架包,功能,工具,开源汇总 其实写博客的初衷也并不是说什么分享技术,毕竟咱还只是个小程序员,最大的目的就是对自我的知识积累,以后万一编码的时候断片了,也可以翻出来 ...

  10. LeetCode(28)-Remove Duplicates from Sorted Array

    题目: Given a sorted array, remove the duplicates in place such that each element appear only once and ...