1. 简介

每次使用搜索引擎时,背后都有网络爬虫在默默工作。除了搜索引擎,爬虫也广泛用于网站数据抓取与分析。

本文将介绍如何使用 crawler4j 搭建并运行自己的网络爬虫程序。crawler4j 是一个开源的 Java 项目,能帮助我们快速实现爬虫功能,适合中高级开发者使用。


2. 环境搭建

使用 Maven 构建项目时,可直接从 Maven Central 引入依赖:

<dependency>
    <groupId>edu.uci.ics</groupId>
    <artifactId>crawler4j</artifactId>
    <version>4.4.0</version>
</dependency>

✅ 推荐使用最新版本以获得更好的兼容性与功能支持。


3. 编写爬虫逻辑

3.1. 简单 HTML 页面爬虫

我们从最基础的 HTML 页面爬虫开始,目标是抓取 https://www.baeldung.com 上的所有页面内容。

首先,继承 WebCrawler 类并定义一个排除某些文件类型的正则表达式:

public class HtmlCrawler extends WebCrawler {

    private final static Pattern EXCLUSIONS
      = Pattern.compile(".*(\\.(css|js|xml|gif|jpg|png|mp3|mp4|zip|gz|pdf))$");

    // more code
}

每个爬虫类都必须实现两个方法:shouldVisitvisit

  • shouldVisit 用于判断是否要访问该链接
  • visit 用于处理访问后的页面内容

实现 shouldVisit 方法:

@Override
public boolean shouldVisit(Page referringPage, WebURL url) {
    String urlString = url.getURL().toLowerCase();
    return !EXCLUSIONS.matcher(urlString).matches() 
      && urlString.startsWith("https://www.baeldung.com/");
}

实现 visit 方法:

@Override
public void visit(Page page) {
    String url = page.getWebURL().getURL();

    if (page.getParseData() instanceof HtmlParseData) {
        HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();
        String title = htmlParseData.getTitle();
        String text = htmlParseData.getText();
        String html = htmlParseData.getHtml();
        Set<WebURL> links = htmlParseData.getOutgoingUrls();

        // do something with the collected data
    }
}

最后,配置并启动爬虫:

File crawlStorage = new File("src/test/resources/crawler4j");
CrawlConfig config = new CrawlConfig();
config.setCrawlStorageFolder(crawlStorage.getAbsolutePath());

int numCrawlers = 12;

PageFetcher pageFetcher = new PageFetcher(config);
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
RobotstxtServer robotstxtServer= new RobotstxtServer(robotstxtConfig, pageFetcher);
CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);

controller.addSeed("https://www.baeldung.com/");

CrawlController.WebCrawlerFactory<HtmlCrawler> factory = HtmlCrawler::new;

controller.start(factory, numCrawlers);

⚠️ 注意:CrawlController.start() 是阻塞方法,后续代码会在爬虫执行完成后才执行。


3.2. 图片爬虫 ImageCrawler

默认情况下,crawler4j 不会抓取二进制内容(如图片)。我们可以通过配置来实现抓取 Baeldung 上的所有 JPEG 图片。

定义 ImageCrawler 类:

public class ImageCrawler extends WebCrawler {
    private final static Pattern EXCLUSIONS
      = Pattern.compile(".*(\\.(css|js|xml|gif|png|mp3|mp4|zip|gz|pdf))$");
    
    private static final Pattern IMG_PATTERNS = Pattern.compile(".*(\\.(jpg|jpeg))$");
    
    private File saveDir;
    
    public ImageCrawler(File saveDir) {
        this.saveDir = saveDir;
    }

    // more code

}

实现 shouldVisit 方法:

@Override
public boolean shouldVisit(Page referringPage, WebURL url) {
    String urlString = url.getURL().toLowerCase();
    if (EXCLUSIONS.matcher(urlString).matches()) {
        return false;
    }

    if (IMG_PATTERNS.matcher(urlString).matches() 
        || urlString.startsWith("https://www.baeldung.com/")) {
        return true;
    }

    return false;
}

实现 visit 方法:

@Override
public void visit(Page page) {
    String url = page.getWebURL().getURL();
    if (IMG_PATTERNS.matcher(url).matches() 
        && page.getParseData() instanceof BinaryParseData) {
        String extension = url.substring(url.lastIndexOf("."));
        int contentLength = page.getContentData().length;

        // write the content data to a file in the save directory
    }
}

启动爬虫时需开启二进制内容抓取:

CrawlConfig config = new CrawlConfig();
config.setIncludeBinaryContentInCrawling(true);

// ... same as before
        
CrawlController.WebCrawlerFactory<ImageCrawler> factory = () -> new ImageCrawler(saveDir);
        
controller.start(factory, numCrawlers);

3.3. 数据统计功能

我们可以为爬虫增加统计功能,比如统计抓取页面数量和链接总数。

定义统计类:

public class CrawlerStatistics {
    private int processedPageCount = 0;
    private int totalLinksCount = 0;
    
    public void incrementProcessedPageCount() {
        processedPageCount++;
    }
    
    public void incrementTotalLinksCount(int linksCount) {
        totalLinksCount += linksCount;
    }
    
    // standard getters
}

修改 HtmlCrawler 接收统计对象:

private CrawlerStatistics stats;
    
public HtmlCrawler(CrawlerStatistics stats) {
    this.stats = stats;
}

修改 visit 方法加入统计逻辑:

@Override
public void visit(Page page) {
    String url = page.getWebURL().getURL();
    stats.incrementProcessedPageCount();

    if (page.getParseData() instanceof HtmlParseData) {
        HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();
        String title = htmlParseData.getTitle();
        String text = htmlParseData.getText();
        String html = htmlParseData.getHtml();
        Set<WebURL> links = htmlParseData.getOutgoingUrls();
        stats.incrementTotalLinksCount(links.size());

        // do something with collected data
    }
}

在控制器中初始化统计对象并传入爬虫:

CrawlerStatistics stats = new CrawlerStatistics();
CrawlController.WebCrawlerFactory<HtmlCrawler> factory = () -> new HtmlCrawler(stats);

3.4. 多爬虫并行运行

crawler4j 支持通过一个控制器启动多个爬虫,例如同时抓取 HTML 和图片。

每个爬虫建议使用独立的存储目录:

File crawlStorageBase = new File("src/test/resources/crawler4j");
CrawlConfig htmlConfig = new CrawlConfig();
CrawlConfig imageConfig = new CrawlConfig();
        
// Configure storage folders and other configurations
        
PageFetcher pageFetcherHtml = new PageFetcher(htmlConfig);
PageFetcher pageFetcherImage = new PageFetcher(imageConfig);
        
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcherHtml);

CrawlController htmlController
  = new CrawlController(htmlConfig, pageFetcherHtml, robotstxtServer);
CrawlController imageController
  = new CrawlController(imageConfig, pageFetcherImage, robotstxtServer);
        
// add seed URLs
        
CrawlerStatistics stats = new CrawlerStatistics();
CrawlController.WebCrawlerFactory<HtmlCrawler> htmlFactory = () -> new HtmlCrawler(stats);
        
File saveDir = new File("src/test/resources/crawler4j");
CrawlController.WebCrawlerFactory<ImageCrawler> imageFactory
  = () -> new ImageCrawler(saveDir);
        
imageController.startNonBlocking(imageFactory, 7);
htmlController.startNonBlocking(htmlFactory, 10);

htmlController.waitUntilFinish();
imageController.waitUntilFinish();

✅ 使用 startNonBlockingwaitUntilFinish 可实现多个爬虫并行执行。


4. 爬虫配置详解

crawler4j 提供了丰富的配置项,以下是一些常用设置。

4.1. 限制爬取深度

crawlConfig.setMaxDepthOfCrawling(2);

默认不限制深度,设置为 2 表示从种子页面出发最多抓取两层。

4.2. 限制抓取页面数

crawlConfig.setMaxPagesToFetch(500);

设置最大抓取页数,适用于控制爬虫规模。

4.3. 限制每页抓取链接数

crawlConfig.setMaxOutgoingLinksToFollow(2000);

避免抓取过多链接,影响性能或目标网站负载。

4.4. 礼貌延迟(Politeness Delay)

crawlConfig.setPolitenessDelay(300);

默认是 200ms,增加该值可以减轻服务器压力。

4.5. 是否抓取二进制内容

crawlConfig.setIncludeBinaryContentInCrawling(true);

用于抓取图片、PDF 等文件。

4.6. 是否抓取 HTTPS 页面

crawlConfig.setIncludeHttpsPages(false);

默认是 true,关闭可避免抓取加密内容。

4.7. 支持断点续爬

crawlConfig.setResumableCrawling(true);

适合长时间任务,但会略微降低性能。

4.8. 自定义 User-Agent

crawlConfig.setUserAgentString("baeldung demo (https://github.com/yasserg/crawler4j/)");

避免被网站识别为默认爬虫,提升爬取成功率。


5. 总结

本文通过几个实际示例,介绍了 crawler4j 的基本使用方式,包括:

  • 抓取 HTML 页面
  • 抓取图片资源
  • 数据统计
  • 多爬虫并行
  • 各种配置项

crawler4j 是一个轻量级但功能强大的爬虫框架,适合 Java 开发者快速实现数据采集任务。合理使用其配置项和多线程机制,可以构建高效稳定的爬虫系统。

如需进一步了解,建议查看 crawler4j GitHub 项目 及其核心类如 CrawlConfigWebCrawler 等源码文档。


原始标题:A Guide to Crawler4j | Baeldung