疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

用Java制作一个爬取商品信息的爬虫


 

   

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

  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();

  }

  }