Perl日記

日々の知ったことのメモなどです。Perlは最近やってないです。

Scalaでスクレイピング

Scalaスクレイピングしてみた。

ライブラリ

やってみた

はてなホッテントリのタイトルとリンクを抽出。

build.sbt
name := "scalascraping1"

version := "0.0.1"

libraryDependencies += "org.jsoup" % "jsoup" % "1.7.3"
main.scala
import org.jsoup._
import collection.JavaConverters._

object Main {
  def main(args: Array[String]): Unit = {
    val doc = Jsoup.connect("http://b.hatena.ne.jp/hotentry").get
    // asScalaで、JavaのArrayをScalaのcollectionに変換しないと、forで回せない
    for (t <- doc.select("a.entry-link").asScala) {
      println(t.text())
      println(t.attr("href"))
    }
  }
}
実行
$ sbt run
ネット民戦慄! 表現の自由を脅かす”ゾーニング破り”とは? - トゥギャッチ
http://togech.jp/2015/12/14/30710
ビジネス問題解決フレームワーク20選|今日から使えて最速で成長できる
http://career-theory.net/business-flamework-3002
App Storeの弊社デベロッパーアカウント停止に至った 経緯につきまして|株式会...
http://nagisa-inc.jp/news_release/20151214/1631
科学的調理法で作ったお手軽一人鍋がやばかった
http://anond.hatelabo.jp/20151214095821
職員が個人情報68万人分流出させる 大阪・堺市 NHKニュース
http://www3.nhk.or.jp/news/html/20151214/k10010340801000.html
所得低いほど高い喫煙率、歯少なく肥満者多い : 社会 : 読売新聞(YOMIURI ONL...
http://www.yomiuri.co.jp/national/20151214-OYT1T50013.html
...

PHP7のThrowableを利用したキャッチ

PHP7からErrorクラスが新設されて、文法エラーのような例外も捕捉できるようになった。

Errorクラスも既存のExceptionクラスも、Throwableインターフェースを実装しているので、完全なキャッチは以下のようになる、と思う。

<?php

function add(int $a, int $b): int {
    return $a + $b;
}

echo add(123, 234), "\n"; // => 357

//try {
//    echo add('hoge', 'fuga'), "\n";
//}
//catch (Exception $e) {
//    echo "Exception: ", $e->getMessage(), "\n";
//}
//
// 拾えずに「PHP Fatal error:  Uncaught TypeError: Argument 1 passed to add() must be of the type integer, string given〜」が発生する

try {
    echo add('hoge', 'fuga'), "\n";
}
catch (\Exception $e) {
    echo "Exception: ", $e->getMessage(), "\n";
}
catch (\Throwable $e) {
    echo "Throwable: ", $e->getMessage(), "\n";
}

// 「Throwable: Argument 1 passed to add() must be of the type integer, string given〜」で拾える

echo "finish\n"; // この処理も継続して動く

PHPでArrayOptionクラスを作った

PHPのArrayは存在しないキーにアクセスすると、Noticeのエラーが発生する。
なので、isset()やら、array_key_exists()をしてから取りに行くのだけど、どうにも面倒だった。

最近覚えたScalaのgetOrElse()みたいなことができればいいなと思って、作ってみた。
json_decode()した結果を入れると、捗る。

こんな感じで使う

<?php
require "./ArrayOption.php";

$j = json_decode('{"hoge":{"fuga":{"piyo":123}}}', $assoc = true);
$arr = new ArrayOption($j);

echo $arr->hoge->fuga->piyo->get(), "\n"; // 123

echo $arr->hoge->fuga->piyo->getOrElse(234), "\n"; // 123

echo $arr->hoge->fuga->foo->getOrElse(999), "\n"; // 999

var_dump($arr->hoge->get());
//array(1) {
//  'fuga' =>
//  array(1) {
//    'piyo' =>
//    int(123)
//  }
//}

$arr->abcde->get(); //PHP Fatal error:  Uncaught exception 'ArrayOptionNoSuchElementException'

いいところ

get()とgetOrElse()で、キーの存在確認とクオートを打つ手間とプロパティ呼び出しの簡潔さがいい感じである。

悪いところ

毎回オブジェクト作ってるので、それなりに遅いです。

<?php

$arr = ['hoge' => ['fuga' => ['piyo' => ['foo' => ['bar' => 123]]]]];

$count = 100000;

for ($i = 1; $i <= $count; $i++) {
    $k = $arr['hoge']['fuga']['piyo']['foo']['bar'];
}
//=> 0.030924 sec

for ($i = 1; $i <= $count; $i++) {
    $arroption = new ArrayOption($arr);
    $k = $arroption->hoge->fuga->piyo->foo->bar->get();
}
//=> 2.622408

実に84倍の差が出てしまいました。