1. 序章

お気に入りの検索エンジンを使用するたびに、Webクローラーが使用されています。 また、Webサイトからデータを取得して分析するためにもよく使用されます。

このチュートリアルでは、crawler4jを使用して独自のWebクローラーをセットアップして実行する方法を学習します。 scroller4jは、これを簡単に実行できるオープンソースのJavaプロジェクトです。

2. 設定

Maven Central を使用して最新バージョンを検索し、Maven依存関係を取り込みましょう。

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

3. クローラーの作成

3.1. シンプルなHTMLクローラー

まず、https://baeldung.comのHTMLページをクロールする基本的なクローラーを作成します。

クローラークラスで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
}

各クローラークラスでは、 shouldVisitとvisitの2つのメソッドをオーバーライドして実装する必要があります。

作成したEXCLUSIONSパターンを使用して、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);

一時ストレージディレクトリを構成し、クロールスレッドの数を指定して、開始URLをクローラーにシードしました。

また、 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
    }
}

ImageCrawler の実行は、 HttpCrawler の実行と似ていますが、バイナリコンテンツを含めるように構成する必要があります。

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

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

3.3. データの収集

いくつかの基本的な例を見てきたので、 HtmlCrawler を拡張して、クロール中にいくつかの基本的な統計を収集しましょう。

まず、いくつかの統計を保持するための単純なクラスを定義しましょう。

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 を変更して、コンストラクターを介してCrawlerStatisticsインスタンスを受け入れます。

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

新しいCrawlerStatisticsオブジェクトを使用して、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
    }
}

それでは、コントローラーに戻って、HtmlCrawlerCrawlerStatisticsのインスタンスを提供しましょう。

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

3.4. 複数のクローラー

前の例に基づいて、同じコントローラーから複数のクローラーを実行する方法を見てみましょう。

各クローラーは独自の一時ストレージディレクトリを使用することをお勧めします。そのため、実行するクローラーごとに個別の構成を作成する必要があります。

CrawlControllersは単一のRobotstxtServerを共有できますが、それ以外の場合は、基本的にすべてのコピーが必要です。

これまで、 CrawlController.start メソッドを使用してクローラーを実行してきましたが、これはブロッキングメソッドであることに注意してください。 複数を実行するには、CrawlerControlller.startNonBlockingをCrawlController.waitUntilFinishと組み合わせて使用します。

次に、HtmlCrawlerImageCrawlerを同時に実行するコントローラーを作成しましょう。

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

4. 構成

構成できるもののいくつかはすでに見てきました。 それでは、他の一般的な設定について見ていきましょう。

設定は、コントローラーで指定したCrawlConfigインスタンスに適用されます。

4.1. クロール深度の制限

デフォルトでは、クローラーは可能な限り深くクロールします。 深さを制限するために、クロールの深さを設定できます。

crawlConfig.setMaxDepthOfCrawling(2);

シードURLは深さ0であると見なされるため、クロールの深さが2の場合、シードURLを2層超えます。

4.2. フェッチする最大ページ

クローラーがカバーするページ数を制限する別の方法は、クロールするページの最大数を設定することです。

crawlConfig.setMaxPagesToFetch(500);

4.3. 最大発信リンク

各ページから続く発信リンクの数を制限することもできます。

crawlConfig.setMaxOutgoingLinksToFollow(2000);

4.4. 礼儀正しさの遅れ

非常に効率的なクローラーはWebサーバーに負担をかけやすいため、crawler4jにはポライトネス遅延と呼ばれるものがあります。 デフォルトでは、200ミリ秒に設定されています。 次の必要がある場合は、この値を調整できます。

crawlConfig.setPolitenessDelay(300);

4.5. バイナリコンテンツを含める

ImageCrawlerにバイナリコンテンツを含めるためのオプションをすでに使用しました。

crawlConfig.setIncludeBinaryContentInCrawling(true);

4.6. HTTPSを含める

デフォルトでは、クローラーにはHTTPSページが含まれますが、これをオフにすることができます。

crawlConfig.setIncludeHttpsPages(false);

4.7. 再開可能なクロール

長時間実行されているクローラーがあり、それを自動的に再開したい場合は、再開可能なクロールを設定できます。 オンにすると、実行速度が低下する可能性があります。

crawlConfig.setResumableCrawling(true);

4.8. ユーザーエージェント文字列

クローラー4jのデフォルトのユーザーエージェント文字列はcrawler4jです。 それをカスタマイズしましょう:

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

ここでは、いくつかの基本的な構成について説明しました。 より高度な構成オプションやあいまいな構成オプションに関心がある場合は、CrawConfigクラスを確認できます。

5. 結論

この記事では、crawler4jを使用して独自のWebクローラーを作成しました。 HTMLと画像をクロールする2つの簡単な例から始めました。 次に、これらの例に基づいて、統計を収集し、複数のクローラーを同時に実行する方法を確認しました。

完全なコード例は、GitHubから入手できます。