用JAVA制作一个爬取商品信息的爬虫(爬取大众点评)

时间:2014-09-22 15:47:02   收藏:0   阅读:4678

很多企业要求利用爬虫去爬取商品信息,一般的开发模型如下:

for i=1;i<=最大页号;i++
     列表页面url=商品列表页面url+?page=i(页号)
     列表页面=爬取(列表页面url)
     商品链接列表=抽取商品链接(列表页面)
     for 链接 in 商品链接列表:
          商品页面=爬取(链接)
          抽取(商品页面);   

这样的模型看似简单,但是有一下几个问题:

    1)爬虫没有线程池支持。

    2)没有断点机制。

    3)没有爬取状态存储,爬取商品网站经常会出现服务器拒绝链接(反问次数过多),导致一旦出现

拒绝链接,有部分页面是未爬取状态。而没有爬取状态记录,导致爬虫需要重新爬取,才可获得完整数据。

    4)当抽取业务复杂时,代码可读性差(没有固定框架)


很多企业解决上面问题时,并没有选择nutch、crawler4j这样的爬虫框架,因为这些爬虫都是基于广度遍历的,上面的业务虽然是简单的双重循环,但是不是广度遍历。但是实际上这个双重循环,是可以转换成广度遍历的,当广度遍历的的层数为1的时候,等价于基于url列表的爬取(种子列表)。上面业务中的循环,其实就是基于url列表的爬取。上面的伪代码是双重循环,所以可以拆分成2次广度遍历来完成的。


我们设计两个广度遍历器LinkCrawler和ProductCrawler:
  1)LinkCrawler负责遍历商品列表页面,抽取每个商品详情页面的url,将抽取出的url注入(inject)到ProductCrawler里
  2)ProductCrawler以LinkCrawler注入的url为种子,进行爬取,对每个商品详情页面进行抽取。


这里以WebCollector爬虫框架为例,给出一段爬取大众点评团购的示例:


import java.io.File;
import java.io.IOException;

import java.util.regex.Pattern;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import cn.edu.hfut.dmic.webcollector.crawler.BreadthCrawler;
import cn.edu.hfut.dmic.webcollector.generator.Injector;
import cn.edu.hfut.dmic.webcollector.model.Page;
import cn.edu.hfut.dmic.webcollector.util.Config;
import cn.edu.hfut.dmic.webcollector.util.FileUtils;

/**
 * 爬取大众点评团购信息的爬虫Demo 很多精抽取的爬虫,并不是采用简单的广度遍历算法,而是采用两个步骤完成:
 * 1.用循环遍历商品列表页面,抽取每个商品详情页面的url 2.对每个商品详情页面进行抽取
 * 大多数爬虫往往只支持广度遍历,所以很多人选择自已用循环来进行上面的抽取 操作,这样做往往不能享受到爬虫框架所提供的线程池、异常处理和断点支持等 功能。
 *
 * 其实上面的抽取任务,是可以通过拆分成2次广度遍历来完成的。 当广度遍历的的层数为1的时候,等价于基于url列表的爬取(种子列表)
 * 我们设计两个广度遍历器LinkCrawler和ProductCrawler
 * 1)LinkCrawler负责遍历商品列表页面,抽取每个商品详情页面的url,将抽取出的url注 入(inject)到ProductCrawler里
 * 2)ProductCrawler以LinkCrawler注入的url为种子,进行爬取,对每个商品详情页面进行 抽取。
 *
 * @author hu
 */
public class DazhongDemo {

    public static class LinkCrawler extends BreadthCrawler {

        Injector injector;

        public LinkCrawler(String linkPath, String productPath) {
            setCrawlPath(linkPath);

            /*向ProductCrawler爬虫注入种子的注入器*/
            injector = new Injector(productPath);
            /*LinkCrawler负责遍历商品列表页面,i是页号*/
            for (int i = 1; i < 3; i++) {
                addSeed("http://t.dianping.com/list/hefei-category_1?pageno=" + i);
            }
            addRegex(".*");
        }

        @Override
        public void visit(Page page) {
            Document doc = page.getDoc();
            Elements links = doc.select("li[class^=floor]>a[track]");
            for (Element link : links) {
                /*href是从商品列表页面中抽取出的商品详情页面url*/
                String href = link.attr("abs:href");
                System.out.println(href);
                synchronized (injector) {
                    try {
                        /*将商品详情页面的url注入到ProductCrawler作为种子*/
                        injector.inject(href, true);
                    } catch (IOException ex) {
                    }
                }
            }

        }

        /*Config.topN=0的情况下,深度为1的广度遍历,等价于对种子列表的遍历*/
        public void start() throws IOException {
            start(1);
        }
    }

    public static class ProductCrawler extends BreadthCrawler {

        public ProductCrawler(String productPath) {
            setCrawlPath(productPath);
            addRegex(".*");
            setResumable(true);
            setThreads(5);
        }

        @Override
        public void visit(Page page) {
            /*判断网页是否是商品详情页面,这个程序里可以省略*/
            if (!Pattern.matches("http://t.dianping.com/deal/[0-9]+", page.getUrl())) {
                return;
            }
            Document doc = page.getDoc();
            String name = doc.select("h1.title").first().text();
            String price = doc.select("span.price-display").first().text();
            String origin_price = doc.select("span.price-original").first().text();
            String validateDate = doc.select("div.validate-date").first().text();
            System.out.println(name + "  " + price + "/" + origin_price + validateDate);
        }

        /*Config.topN=0的情况下,深度为1的广度遍历,等价于对种子列表的遍历*/
        public void start() throws IOException {
            start(1);
        }
    }

    public static void main(String[] args) throws IOException {

        /*
         Config.topN表示爬虫做链接分析时,链接数量上限,由于本程序只要求遍历
         种子url列表,不需根据链接继续爬取,所以要设置为0
         */
        Config.topN = 0;
        /*
         每个爬虫的爬取依赖一个文件夹,这个文件夹会对爬取信息进行存储和维护
         这里有两个爬虫,所以需要设置两个爬取文件夹
         */
        String linkPath = "crawl_link";
        String productPath = "crawl_product";

        File productDir = new File(productPath);
        if (productDir.exists()) {
            FileUtils.deleteDir(productDir);
        }

        LinkCrawler linkCrawler = new LinkCrawler(linkPath, productPath);
        linkCrawler.start();

        ProductCrawler productCrawler = new ProductCrawler(productPath);
        productCrawler.start();

    }

}


评论(1
小壁虎 2016-10-28 16:46:21
推荐使用神箭手云爬虫,完全在云上编写和执行爬虫,不需要配置任何开发环境,快速开发快速实现。?

简单几行 javascript 就可以实现复杂的爬虫,同时提供很多功能函数:反反爬虫、 js 渲染、数据发布、图表分析、反防盗链等,这些在开发爬虫过程中经常会遇到的问题都由神箭手帮你解决。
1
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!