欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

教您使用java爬虫gecco抓取JD全部商品信息(二)

程序员文章站 2022-05-23 22:24:02
...

教您使用java爬虫gecco抓取JD全部商品信息(一)

抓取商品列表信息

AllSortPipeline已经将需要进一步抓取的商品列表信息的链接提取出来了,可以看到链接的格式是:http://list.jd.com/list.html?cat=9987,653,659&delivery=1&JL=4_10_0&go=0。因此我们建立商品列表的Bean——ProductList,代码如下:

@Gecco(matchUrl="http://list.jd.com/list.html?cat={cat}&delivery={delivery}&page={page}&JL={JL}&go=0", pipelines={"consolePipeline", "productListPipeline"})
public classProductListimplementsHtmlBean{

    private static final long serialVersionUID = 4369792078959596706L;

    @Request
    private HttpRequest request;

    /**
     * 抓取列表项的详细内容,包括titile,价格,详情页地址等
     */
    @HtmlField(cssPath="#plist .gl-item")
    private List<ProductBrief> details;
    /**
     * 获得商品列表的当前页
     */
    @Text
    @HtmlField(cssPath="#J_topPage > span > b")
    private int currPage;
    /**
     * 获得商品列表的总页数
     */
    @Text
    @HtmlField(cssPath="#J_topPage > span > i")
    private int totalPage;

    public List<ProductBrief> getDetails(){
        return details;
    }

    publicvoidsetDetails(List<ProductBrief> details){
        this.details = details;
    }

    publicintgetCurrPage(){
        return currPage;
    }

    publicvoidsetCurrPage(int currPage){
        this.currPage = currPage;
    }

    publicintgetTotalPage(){
        return totalPage;
    }

    publicvoidsetTotalPage(int totalPage){
        this.totalPage = totalPage;
    }

    public HttpRequest getRequest(){
        return request;
    }

    publicvoidsetRequest(HttpRequest request){
        this.request = request;
    }

}

currPage和totalPage是页面上的分页信息,为之后的分页抓取提供支持。ProductBrief对象是商品的简介,主要包括标题、预览图、详情页地址等。

public classProductBriefimplementsHtmlBean{

    private static final long serialVersionUID = -377053120283382723L;

    @Attr("data-sku")
    @HtmlField(cssPath=".j-sku-item")
    private String code;

    @Text
    @HtmlField(cssPath=".p-name> a > em")
    private String title;

    @Image({"data-lazy-img", "src"})
    @HtmlField(cssPath=".p-img > a > img")
    private String preview;

    @Href(click=true)
    @HtmlField(cssPath=".p-name > a")
    private String detailUrl;

    public String getTitle(){
        return title;
    }

    publicvoidsetTitle(String title){
        this.title = title;
    }

    public String getPreview(){
        return preview;
    }

    publicvoidsetPreview(String preview){
        this.preview = preview;
    }

    public String getDetailUrl(){
        return detailUrl;
    }

    publicvoidsetDetailUrl(String detailUrl){
        this.detailUrl = detailUrl;
    }

    public String getCode(){
        return code;
    }

    publicvoidsetCode(String code){
        this.code = code;
    }

}

这里需要说明一下@Href(click=true)的click属性,click属性形象的说明了,这个链接我们希望gecco继续点击抓取。对于增加了click=true的链接,gecco会自动加入下载队列中,不需要在手动调用SchedulerContext.into()增加。

编写ProductList的业务逻辑

ProductList抓取完成后一般需要进行持久化,也就是将商品的基本信息入库,入库的方式有很多种,这个例子并没有介绍,gecco支持整合spring,可以利用spring进行pipeline的开发,大家可以参考gecco-spring这个项目。本例子是进行了控制台输出。ProductList的业务处理还有一个很重要的任务,就是对分页的处理,列表页通常都有很多页,如果需要全部抓取,我们需要将下一页的链接入抓取队列。

@PipelineName("productListPipeline")
public classProductListPipelineimplementsPipeline<ProductList> {

    @Override
    publicvoidprocess(ProductList productList){
        HttpRequest currRequest = productList.getRequest();
        //下一页继续抓取
        int currPage = productList.getCurrPage();
        int nextPage = currPage + 1;
        int totalPage = productList.getTotalPage();
        if(nextPage <= totalPage) {
            String nextUrl = "";
            String currUrl = currRequest.getUrl();
            if(currUrl.indexOf("page=") != -1) {
                nextUrl = StringUtils.replaceOnce(currUrl, "page=" + currPage, "page=" + nextPage);
            } else {
                nextUrl = currUrl + "&" + "page=" + nextPage;
            }
            SchedulerContext.into(currRequest.subRequest(nextUrl));
        }
    }

}

 

JD的列表页通过page参数来指定页码,我们通过替换page参数达到分页抓取的目的。至此,所有的商品的列表信息都已经可以正常抓取了。