HTMLスクレイピング in Scala その1

スクレイピングして遊ぶために、文字コードを判別してHTMLソースを取得するコードをScalaで書いてみました。

実装

HtmlScraping.scala

import scala.io.Source
import scala.util.matching.Regex

object Html{
    def getSource(url: String): List[String] = {
        val src = Source.fromURL(url, "ISO-8859-1").getLines.toList

        var charset: String = null
        val regex = new Regex("""charset[ ]*=[ ]*[0-9a-z|\-|_]+""")
        for(line <- src){
            val lower = line.toLowerCase
            if(lower.contains("content") && lower.contains("charset")){
                charset = regex.findFirstIn(lower).get
                charset = charset.split("=")(1).trim
            }
        }
        return if(charset == null) src
            else Source.fromURL(url, charset).getLines.toList
    }

    def main(args: Array[String]) = {
        val src = getSource(args(0))
        println(src)
    }
}

REPLで実行してみる

scala> :load .\HtmlScraping.scala

Loading .\HtmlScraping.scala...
import scala.io.Source
import scala.util.matching.Regex
defined module Html

試しに charset=utf-8 のYahooさんのページ
scala> Html.getSource("http://www.yahoo.co.jp")

res0: List[String] = List(<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitio
nal//EN" "http://www.w3.org/TR/html4/loose.dtd">, <html>, <head>, <meta http-equ
iv="content-type" content="text/html; charset=utf-8">, <meta http-equiv="content
-style-type" content="text/css">, <meta http-equiv="content-script-type" content
="text/javascript">, <meta name="description" content="日本最大級のポータルサイ
ト。検索、オークション、ニュース、メール、コミュニティ、ショッピング、など80以上
のサービスを展開。あなたの生活をより豊かにする「ライフ・エンジン」を目指していき
ます。">, <title>Yahoo! JAPAN</title>, <base href="http://www.yahoo.co.jp/_ylh=X
3oDMTB2OHNyYWxqBF9TAzIwNzkxODE5OTkEdGlkAzEzBHRtcGwDdGFibGU-/">, <style type="tex
t/css">, <!--, body{word-break:break-all;font:12px/1.22 "Osaka", "MS Pゴシッ
ク", Arial, sans-serif;*font-size:small;*font:x-small;}, table{font-size:inherit
;font:100%;}, pre,co...

取得できました。
試した限りではeuc-jp, shift_jis のページも無事取得出来ました。

Source.fromURL


文字コードが固定されているのであれば、一行で取得したソースを表示するコードが書けます。

scala> Source.fromURL("http://hogehoge.jp/", "utf-8").foreach(c => print(c))

ただし、実際には様々な文字コードのページが氾濫しているのでクローラなどを開発する際には文字コード判別は無視できないでしょう。
本格的に文字コードを判別するにはもっと複雑な処理が必要なのですが、今回は最初に"ISO-8859-1"で読み込みHTMLに記述されたContent-typeのcharsetを取得し、そのcharsetを使って再度 Source.fromURL を実行してソースを取得するという実装にしています。もっと良い方法があれば教えてください!

生文字リテラル


正規表現のパターンの記述には生文字リテラルを使用してみました。
PHPで言うヒアドキュメントのようなものです。

val regex = new Regex("""charset[ ]*=[ ]*[0-9a-z|\-|_]+""")

生文字リテラルには以下の特徴があります。

・エスケープ文字を持たない
・複数行の文字列をコードの中で記述することが出来る。

これでJavaなどで正規表現を使うときによくやっていた 二重の¥マーク だらけの煩雑な記述を回避できます。


次回は実際にHTMLをスクレイピングするコードを書こうと思います。