基于crawler4j、jsoup、javacsv的爬虫实践

1. crawler4j基础

crawler4j是一个基于Java的爬虫开源项目,其官方地址如下:

http://code.google.com/p/crawler4j/

crawler4j的使用主要分为两个步骤:

  1. 实现一个继承自WebCrawler的爬虫类;
  2. 通过CrawlController调用实现的爬虫类。

WebCrawler是一个抽象类,继承它必须实现两个方法:shouldVisit和visit。其中:

  • shouldVisit是判断当前的URL是否已经应该被爬取(访问);
  • visit则是爬取该URL所指向的页面的数据,其传入的参数即是对该web页面全部数据的封装对象Page。

另外,WebCrawler还有其它一些方法可供覆盖,其方法的命名规则类似于Android的命名规则。如getMyLocalData方法可以返回WebCrawler中的数据;onBeforeExit方法会在该WebCrawler运行结束前被调用,可以执行一些资源释放之类的工作。

相对而言,CrawlController的调用就比较格式化了。一般地,它的调用代码如下:

[java]  view plain  copy
  1. String crawlStorageFolder = "data/crawl/root";  
  2. int numberOfCrawlers = 7;  
  3.   
  4. CrawlConfig config = new CrawlConfig();  
  5. config.setCrawlStorageFolder(crawlStorageFolder);  
  6.   
  7. /* 
  8.  * Instantiate the controller for this crawl. 
  9.  */  
  10. PageFetcher pageFetcher = new PageFetcher(config);  
  11. RobotstxtConfig robotstxtConfig = new RobotstxtConfig();  
  12. RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);  
  13. CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);  
  14.   
  15. /* 
  16.  * For each crawl, you need to add some seed urls. These are the first 
  17.  * URLs that are fetched and then the crawler starts following links 
  18.  * which are found in these pages 
  19.  */  
  20. controller.addSeed("http://www.ics.uci.edu/~welling/");  
  21. controller.addSeed("http://www.ics.uci.edu/~lopes/");  
  22. controller.addSeed("http://www.ics.uci.edu/");  
  23.   
  24. /* 
  25.  * Start the crawl. This is a blocking operation, meaning that your code 
  26.  * will reach the line after this only when crawling is finished. 
  27.  */  
  28. controller.start(MyCrawler.class, numberOfCrawlers);     

CrawlController自带多线程功能,start方法中的第二个参数numberOfCrawlers即是同时开启的线程数。

另外,由于CrawlController对WebCrawler特殊的调用方式——反射(上述代码最后一行),因此WebCrawler的实现类必须拥有无参的构造方法,且有参的构造方法不会生效。对WebCrawler的实现类的私有成员的赋值需要通过静态方法来实现,示例参见crawler4j提供的例子:Image Crawler

更多信息请参见crawler4j的代码和示例。

2. jsoup基础

jsoup是一个基于Java的开源HTML解析器,其官网地址如下:

http://jsoup.org/

jsoup最大的特点,或者说,它比使用DOM4J进行HTML解析更好的原因,是它可以采用jQuery选择器的语法。

例如:

[java]  view plain  copy
  1. Document doc = Jsoup.connect("http://en.wikipedia.org/").get();  
  2. Elements newsHeadlines = doc.select("#mp-itn b a");  

上述代码就是获取了http://en.wikipedia.org/页面中id为mp-itn的元素下的<b>标签中的<a>标签,与jQuery选择器的结果一致。

更多jsoup的使用方法请参见jsoup的示例(在主页的右侧Cookbook Content中)。

需要特别说明的是,jsoup中主要有三种操作对象:Document、Elements及Element。其中:

  • Document继承自Element类,它包含页面中的全部数据,通过Jsoup类的静态方法获得;
  • Elements是Element的集合类;
  • Element是页面元素的实体类,包含了诸多操作页面元素的方法,其中除了类似于jQuery选择器的select方法外,还有大量类似于JS和jQuery的对DOM元素进行操作的方法,如getElementById,text,addClass,等等。

3. javacsv基础

javacsv是一个基于Java的开源CSV文件读写工具,其官方地址如下:

http://www.csvreader.com/java_csv.php

CSV文件的读写其实很简单,可以自己实现,网上也有诸多示例。使用javacsv的原因在于其代码简洁易用。

javacsv的使用示例参见其官方示例:

http://www.csvreader.com/java_csv_samples.php

需要说明的是,读写CSV文件时,若存在中文,请尽量使用FileReader(读)及FileWriter(写),而非FileInputStream和FileOutputStream,以免出现乱码。

4. 爬虫实践

下面的实践的目标是爬取搜车网的全部二手车信息,并作为CSV文件输出。代码如下:

Maven pom.xml

[html]  view plain  copy
  1. <dependency>  
  2.     <groupId>edu.uci.ics</groupId>  
  3.     <artifactId>crawler4j</artifactId>  
  4.     <version>3.5</version>  
  5.     <type>jar</type>  
  6.     <scope>compile</scope>  
  7. </dependency>  
  8.   
  9. <dependency>  
  10.     <groupId>org.jsoup</groupId>  
  11.     <artifactId>jsoup</artifactId>  
  12.     <version>1.7.3</version>  
  13. </dependency>  
  14.   
  15. <dependency>  
  16.     <groupId>net.sourceforge.javacsv</groupId>  
  17.     <artifactId>javacsv</artifactId>  
  18.     <version>2.0</version>  
  19. </dependency>  

MyCrawler.java

[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileWriter;  
  3. import java.io.IOException;  
  4. import java.util.regex.Pattern;  
  5.   
  6. import org.jsoup.Jsoup;  
  7. import org.jsoup.nodes.Document;  
  8. import org.jsoup.nodes.Element;  
  9. import org.jsoup.select.Elements;  
  10.   
  11. import com.csvreader.CsvWriter;  
  12.   
  13. import edu.uci.ics.crawler4j.crawler.Page;  
  14. import edu.uci.ics.crawler4j.crawler.WebCrawler;  
  15. import edu.uci.ics.crawler4j.parser.HtmlParseData;  
  16. import edu.uci.ics.crawler4j.url.WebURL;  
  17.   
  18. public class MyCrawler extends WebCrawler {  
  19.   
  20.     private final static Pattern FILTERS = Pattern  
  21.             .compile(".*(\\.(css|js|bmp|gif|jpe?g|ico"  
  22.                     + "|png|tiff?|mid|mp2|mp3|mp4"  
  23.                     + "|wav|avi|mov|mpeg|ram|m4v|pdf"  
  24.                     + "|rm|smil|wmv|swf|wma|zip|rar|gz))$");  
  25.   
  26.     private final static String URL_PREFIX = "http://www.souche.com/pages/onsale/sale_car_list.html?";  
  27.     private final static Pattern URL_PARAMS_PATTERN = Pattern  
  28.             .compile("carbrand=brand-\\d+(&index=\\d+)?");  
  29.   
  30.     private final static String CSV_PATH = "data/crawl/data.csv";  
  31.     private CsvWriter cw;  
  32.     private File csv;  
  33.   
  34.     public MyCrawler() throws IOException {  
  35.         csv = new File(CSV_PATH);  
  36.   
  37.         if (csv.isFile()) {  
  38.             csv.delete();  
  39.         }  
  40.   
  41.         cw = new CsvWriter(new FileWriter(csv, true), ',');  
  42.         cw.write("title");  
  43.         cw.write("brand");  
  44.         cw.write("newPrice");  
  45.         cw.write("oldPrice");  
  46.         cw.write("mileage");  
  47.         cw.write("age");  
  48.         cw.write("stage");  
  49.         cw.endRecord();  
  50.         cw.close();  
  51.     }  
  52.   
  53.     /** 
  54.      * You should implement this function to specify whether the given url 
  55.      * should be crawled or not (based on your crawling logic). 
  56.      */  
  57.     @Override  
  58.     public boolean shouldVisit(WebURL url) {  
  59.         String href = url.getURL().toLowerCase();  
  60.         if (FILTERS.matcher(href).matches() || !href.startsWith(URL_PREFIX)) {  
  61.             return false;  
  62.         }  
  63.   
  64.         String[] strs = href.split("\\?");  
  65.         if (strs.length < 2) {  
  66.             return false;  
  67.         }  
  68.   
  69.         if (!URL_PARAMS_PATTERN.matcher(strs[1]).matches()) {  
  70.             return false;  
  71.         }  
  72.           
  73.         return true;  
  74.     }  
  75.   
  76.     /** 
  77.      * This function is called when a page is fetched and ready to be processed 
  78.      * by your program. 
  79.      */  
  80.     @Override  
  81.     public void visit(Page page) {  
  82.         String url = page.getWebURL().getURL();  
  83.   
  84.         if (page.getParseData() instanceof HtmlParseData) {  
  85.             HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();  
  86.             String html = htmlParseData.getHtml();  
  87.   
  88.             Document doc = Jsoup.parse(html);  
  89.             String brand = doc.select("div.choose_item").first().text();  
  90.   
  91.             Elements contents = doc.select("div.list_content");  
  92.               
  93.             if (contents.size() == 20 && !url.contains("index=")) {  
  94.                 return;  
  95.             } else {  
  96.                 System.out.println("URL: " + url);  
  97.             }  
  98.               
  99.             for (Element c : contents) {  
  100.                 Element info = c.select(".list_content_carInfo").first();  
  101.                 String title = info.select("h1").first().text();  
  102.   
  103.                 Elements prices = info.select(".list_content_price div");  
  104.                 String newPrice = prices.get(0).text();  
  105.                 String oldPrice = prices.get(1).text();  
  106.   
  107.                 Elements others = info.select(".list_content_other div");  
  108.                 String mileage = others.get(0).select("ins").first().text();  
  109.                 String age = others.get(1).select("ins").first().text();  
  110.   
  111.                 String stage = "unknown";  
  112.                 if (c.select("i.car_tag_zhijian").size() != 0) {  
  113.                     stage = c.select("i.car_tag_zhijian").text();  
  114.                 } else if (c.select("i.car_tag_yushou").size() != 0) {  
  115.                     stage = "presell";  
  116.                 }  
  117.   
  118.                 try {  
  119.                     cw = new CsvWriter(new FileWriter(csv, true), ',');  
  120.                     cw.write(title);  
  121.                     cw.write(brand);  
  122.                     cw.write(newPrice.replaceAll("[¥万]"""));  
  123.                     cw.write(oldPrice.replaceAll("[¥万]"""));  
  124.                     cw.write(mileage);  
  125.                     cw.write(age);  
  126.                     cw.write(stage);  
  127.                     cw.endRecord();  
  128.                     cw.close();  
  129.                 } catch (IOException e) {  
  130.                     e.printStackTrace();  
  131.                 }  
  132.             }  
  133.         }  
  134.     }  
  135. }  

Controller.java

[java]  view plain  copy
  1. import edu.uci.ics.crawler4j.crawler.CrawlConfig;  
  2. import edu.uci.ics.crawler4j.crawler.CrawlController;  
  3. import edu.uci.ics.crawler4j.fetcher.PageFetcher;  
  4. import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig;  
  5. import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;  
  6.   
  7. public class Controller {  
  8.     public static void main(String[] args) throws Exception {  
  9.             String crawlStorageFolder = "data/crawl/root";  
  10.             int numberOfCrawlers = 7;  
  11.   
  12.             CrawlConfig config = new CrawlConfig();  
  13.             config.setCrawlStorageFolder(crawlStorageFolder);  
  14.   
  15.             /* 
  16.              * Instantiate the controller for this crawl. 
  17.              */  
  18.             PageFetcher pageFetcher = new PageFetcher(config);  
  19.             RobotstxtConfig robotstxtConfig = new RobotstxtConfig();  
  20.             RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);  
  21.             CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);  
  22.   
  23.             /* 
  24.              * For each crawl, you need to add some seed urls. These are the first 
  25.              * URLs that are fetched and then the crawler starts following links 
  26.              * which are found in these pages 
  27.              */  
  28.             controller.addSeed("http://www.ics.uci.edu/~welling/");  
  29.             controller.addSeed("http://www.ics.uci.edu/~lopes/");  
  30.             controller.addSeed("http://www.ics.uci.edu/");  
  31.   
  32.             /* 
  33.              * Start the crawl. This is a blocking operation, meaning that your code 
  34.              * will reach the line after this only when crawling is finished. 
  35.              */  
  36.             controller.start(MyCrawler.class, numberOfCrawlers);      
  37.     }  
  38. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值