商品的基本信息抓取完成后,就要针对每个商品的详情页进行抓取,可以看到详情页的地址格式一般如下:http://item.jd.com/1861098.html。
我们建立商品详情页的Bean:
@Gecco(matchUrl="http://item.jd.com/{code}.html", pipelines="consolePipeline") public class ProductDetail implements HtmlBean { private static final long serialVersionUID = -377053120283382723L; /** * 商品代码 */ @RequestParameter private String code; /** * 标题 */ @Text @HtmlField(cssPath="#name > h1") private String title; /** * ajax获取商品价格 */ @Ajax(url="http://p.3.cn/prices/get?skuIds=J_[code]") private JDPrice price; /** * 商品的推广语 */ @Ajax(url="http://cd.jd.com/promotion/v2?skuId={code}&area=1_2805_2855_0&cat=737%2C794%2C798") private JDad jdAd; /* * 商品规格参数 */ @HtmlField(cssPath="#product-detail-2") private String detail; public JDPrice getPrice() { return price; } public void setPrice(JDPrice price) { this.price = price; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public JDad getJdAd() { return jdAd; } public void setJdAd(JDad jdAd) { this.jdAd = jdAd; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } }
@RequestParameter可以获取@Gecco里定义的url变量{code}。
@Ajax是页面中的ajax请求,JD的商品价格和推广语都是通过ajax请求异步获取的,gecco支持异步ajax请求,指定ajax请求的url地址,url中的变量可以通过两种方式指定。
一种是花括号{},可以获取request的参数类似@RequestParameter,例子中获取推广语的{code}是matchUrl="http://item.jd.com/{code}.html"中的code;
一种是中括号[],可以获取bean中的任意属性。例子中获取价格的[code]是变量private String code;。
商品的价格是通过ajax获取的,ajax一般返回的都是json格式的数据,这里需要将json格式的数据抽取出来。我们先定义价格的Bean:
public class JDPrice implements JsonBean { private static final long serialVersionUID = -5696033709028657709L; @JSONPath("$.id[0]") private String code; @JSONPath("$.p[0]") private float price; @JSONPath("$.m[0]") private float srcPrice; public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public float getSrcPrice() { return srcPrice; } public void setSrcPrice(float srcPrice) { this.srcPrice = srcPrice; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } }
我们获取的商品价格信息的json数据格式为:[{"id":"J_1861098","p":"6488.00","m":"7488.00"}]。可以看到是一个数组,因为这个接口其实可以批量获取商品的价格。json数据的数据抽取使用@JSONPath注解,语法是使用的fastjson的JSONPath语法。
JDad的抓取类似,下面是Bean的代码:
public class JDad implements JsonBean { private static final long serialVersionUID = 2250225801616402995L; @JSONPath("$.ads[0].ad") private String ad; @JSONPath("$.ads") private Listads; public String getAd() { return ad; } public void setAd(String ad) { this.ad = ad; } public List getAds() { return ads; } public void setAds(List ads) { this.ads = ads; } }
目前爬虫抓取页面内容针对ajax请求有两种主流方式:
这两种方式都有各自的优缺点,gecco通过扩展都支持,本人还是更倾向于使用第二种方式。
下面说说怎么分析页面中的ajax请求,还是要利用chrome的开发者模式,network选项可以看到页面中的所有请求:
可以看到请求的地址是:http://p.3.cn/prices/get?type=1&area=1_2805_2855&pdtk=&pduid=836516317&pdpin=&pdbp=0&skuid=J_1861098&callback=cnp。我们去掉其他参数只留下商品的代码,发现一样可以访问,http://p.3.cn/prices/get?skuid=J_1861098就是我们要请求的地址。
全部源代码可以在gecco的github上下载,代码位于src/test/java/com/geccocrawler/gecco/demo/jd包下。如果使用过程中发现任何bug欢迎Pull request,或者通过Issue提问,当然也可以在博客中留言。