読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

全文検索エンジンを作ろうと思って、まずはクローラーを作ってみた

日記 勉強

何を使ったのかと言うと、crawler4j を使って、対象のサイトをかたっぱしから動き回る実装をしてみた。

github.com

ただし、ディレイとか入れなくて DOS 攻撃になるんじゃねーかとか不安もあるので、応用するときは自己責任で。

※ スレッド数指定でマジでサーバに負荷かけられてしまうのでマジ自己責任で

使い方は簡単で、libraryDependency に crawler4j を突っ込むところから始める。

libraryDependencies ++= Seq(
  "edu.uci.ics" % "crawler4j" % "4.1", // crawler
  "com.typesafe" % "config" % "1.3.0"
)

そしたら、src/main/resources/application.conf ファイルを作成して、動作設定を記述する。

crawler {
  baseUrl = "http://white-azalea.hatenablog.jp"
  ignoreRegex = ".*(\\.(css|xml|js|gif|jpg|png|mp3|mp3|zip|gz))$"
  cache = "./cache"
  threads = 1
}
  • baseUrl : 検索開始位置、兼、検索対象URL (このサイト以外は検索しない)
  • ignoreRegex : 画像やCSS,XMLや明らかなバイナリはクロールしない。
  • cache : キャッシュ保存ディレクトリ
  • threads : 何スレッドでクロールするのか

で、クロールする対象の処理をぺたり

package models

import java.util.regex.Pattern

import edu.uci.ics.crawler4j.crawler.{CrawlController, CrawlConfig, Page, WebCrawler}
import edu.uci.ics.crawler4j.fetcher.PageFetcher
import edu.uci.ics.crawler4j.parser.HtmlParseData
import edu.uci.ics.crawler4j.robotstxt.{RobotstxtServer, RobotstxtConfig}
import edu.uci.ics.crawler4j.url.WebURL

class SearchCrawler extends WebCrawler {

  val fileFilter = Pattern.compile(Configure.ignoreRegex);

  override def shouldVisit(page: Page, webURL: WebURL): Boolean = {
    // 調べてないけど、page にはリンク元ページが格納されてると思われ。
    // webURL の指定しているページをクロールするかどうかの判定処理。
    val currentUrl = webURL.getURL.toLowerCase
    !fileFilter.matcher(currentUrl).matches() && currentUrl.startsWith(Configure.baseUrl)
  }

  override def visit(page: Page): Unit = {
    // クロールした結果を受け取るハンドラ
    import scala.collection.JavaConversions._

    val currentUrl = page.getWebURL
    if (page.getParseData.isInstanceOf[HtmlParseData]) {
      val data = page.getParseData.asInstanceOf[HtmlParseData]
      val allTextData = data.getText
      val allLink = data.getOutgoingUrls

      println(s"CurrentURL  : $currentUrl")
      println(s"CurrentText : ${allTextData.length}")
      println(s"Links :")

      allLink.foreach(url => {
        println(s"Link : ${url.getURL}, ${url.getAnchor}, ${url.getTag}")
      })
    }
  }
}

object StartCrawler {

  val crawlConfig = {
    val conf = new CrawlConfig
    conf.setCrawlStorageFolder(Configure.cacheDir)
    conf
  }

  val controller = {
    val fetcher = new PageFetcher(crawlConfig)
    val robotTextConf = new RobotstxtConfig
    val robotTextServer = new RobotstxtServer(robotTextConf, fetcher)
    new CrawlController(crawlConfig, fetcher, robotTextServer)
  }

  def start(): Unit = {
    controller.addSeed(Configure.baseUrl)
    controller.start(classOf[SearchCrawler], Configure.threads)
  }
}

呼び出し方は…わかるよね?

出力した例が下記。

CurrentURL  : http://white-azalea.hatenablog.jp/entries/2011/05/16
CurrentText : 9908
Links :
Link : http://white-azalea.hatenablog.jp/archive/category/Linux, Linux (8), a
Link : https://blog.st-hatena.com/images/theme/hatena-star-quote-star.png?version=0deb8348676c873485aaafc310112165, null, img
Link : https://blog.st-hatena.com/images/common/meta-icon-global.png, null, link
Link : http://d.hatena.ne.jp/keyword/iPhone, iPhone, a
Link : http://white-azalea.hatenablog.jp/archive/category/Jetty, Jetty (2), a
Link : http://white-azalea.hatenablog.jp/archive/category/Javascript, Javascript (3), a
Link : http://white-azalea.hatenablog.jp/archive/category/Cocoa, Cocoa (5), a
Link : http://white-azalea.hatenablog.jp/archive/category/C%E8%A8%80%E8%AA%9E, C言語 (2), a
Link : http://d.hatena.ne.jp/keyword/Android, Android, a
Link : http://white-azalea.hatenablog.jp/archive/category/Akka, Akka (2), a
Link : http://white-azalea.hatenablog.jp/archive/category/AS3, AS3 (4), a
Link : http://white-azalea.hatenablog.jp/archive/category/F%23, F# (3), a
Link : http://white-azalea.hatenablog.jp/archive/category/%E6%97%A5%E8%A8%98, 日記, a
Link : http://white-azalea.hatenablog.jp/entry/2015/05/01/180029, play framework で react.js やろうとしてみたメモ, a
Link : http://white-azalea.hatenablog.jp/entry/2015/04/30/203148, HTML5+JS でメニューをそこそこカッコよく出してみる, a
Link : http://white-azalea.hatenablog.jp/archive/category/Java, Java (17), a
Link : http://white-azalea.hatenablog.jp/archive/category/Windows, Windows (4), a
Link : http://white-azalea.hatenablog.jp/archive/category/Twitter, Twitter (3), a
...