はじめてのdocker+cli+Kotlin~南ことりの画像を収集~

## はじめに
・JAVAは未経験
・qiita初投稿
・kotlinは[本](https://www.amazon.co.jp/Kotlin%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF-%E6%96%B0%E3%81%97%E3%81%84Android%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E9%95%B7%E6%BE%A4-%E5%A4%AA%E9%83%8E/dp/4865940391/ref=sr_1_1?ie=UTF8&qid=1528020466&sr=8-1&keywords=kotlin%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF)を1冊読んで手を動かして遊んだ程度
・普段はPHPやrails
・理解度が浅いため、全体的にふんわりしています。ご指摘等頂ければ幸いです


## dockerで環境を作る。
まずはkotlinがコンパイル出来るよう、環境を作っていきます。
[docker-hub](https://hub.docker.com/r/zenika/alpine-kotlin/)のdockerfileをベースにしますが
`gradle`が足りないので入れます(gradleについては後述)
[gradle公式](https://hub.docker.com/_/gradle/)dockerfileを参考にしました

alpine-linuxは軽量なため、コマンドが最小限です。
curlなども入っていないので必要なものはapkで入れています。


```dockerfile

FROM openjdk:8-jdk-alpine
RUN apk update \
    && apk upgrade \
	&& apk add coreutils curl	


ARG BUILD_DATE
ARG VCS_REF

# Set Appropriate Environmental Variables
ENV GRADLE_HOME /usr/lib/gradle
ENV GRADLE_VERSION 4.7
ENV GRADLE_FOLDER=/root/.gradle

ARG GRADLE_DOWNLOAD_SHA256=fca5087dc8b50c64655c000989635664a73b11b9bd3703c7d6cabd31b7dcdb04
RUN set -o errexit -o nounset \
	&& echo "Installing build dependencies" \
	&& apk add --no-cache --virtual .build-deps \
		ca-certificates \
		openssl \
		unzip \
	\
	&& echo "Downloading Gradle" \
	&& wget -O gradle.zip "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
	\
	&& echo "Checking download hash" \
	&& echo "${GRADLE_DOWNLOAD_SHA256} *gradle.zip" | sha256sum -c - \
	\
	&& echo "Installing Gradle" \
	&& unzip gradle.zip \
	&& rm gradle.zip \
	&& mkdir /opt \
	&& mv "gradle-${GRADLE_VERSION}" "${GRADLE_HOME}/" \
	&& ln -s "${GRADLE_HOME}/bin/gradle" /usr/bin/gradle \
	\
	&& apk del .build-deps \
	\
	&& echo "Adding gradle user and group" \
	&& addgroup -S -g 1000 gradle \
	&& adduser -D -S -G gradle -u 1000 -s /bin/ash gradle \
	&& mkdir /home/gradle/.gradle \
	&& chown -R gradle:gradle /home/gradle \
	\
	&& echo "Symlinking root Gradle cache to gradle Gradle cache" \
	&& ln -s /home/gradle/.gradle /root/.gradle

LABEL org.label-schema.build-date=$BUILD_DATE \
      org.label-schema.description="Kotlin docker images built upon official openjdk alpine images" \
      org.label-schema.name="alpine-kotlin" \
      org.label-schema.schema-version="1.0.0-rc1" \
      org.label-schema.usage="https://github.com/Zenika/alpine-kotlin/blob/master/README.md" \
      org.label-schema.vcs-url="https://github.com/Zenika/alpine-kotlin" \
      org.label-schema.vcs-ref=$VCS_REF \
      org.label-schema.vendor="Zenika" \
      org.label-schema.version="1.2-jdk8"

RUN apk add --no-cache bash && \
    apk add --no-cache -t build-dependencies wget && \
    cd /usr/lib && \
    wget https://github.com/JetBrains/kotlin/releases/download/v1.2.41/kotlin-compiler-1.2.41.zip && \
    unzip kotlin-compiler-*.zip && \
    rm kotlin-compiler-*.zip && \
    rm kotlinc/bin/*.bat && \
    apk del --no-cache build-dependencies

ENV PATH $PATH:/usr/lib/kotlinc/bin


RUN apk del coreutils curl
# コンテナのワークディレクトリの指定
WORKDIR /app
CMD ["kotlinc"]



VOLUME  $GRADLE_FOLDER
```

```yml:docker-compose.yml
version: '2'

services:

  kotlin:
    build: .
    command: bash -c "gradle run"
    volumes:
    - .:/app
```

これで`docker-compose build`後、
`docker-compose up`をすれば
`gradle run`を実行してくれます


### 2 gradleを書く
gradleはビルドの自動化ツールです。

Android開発者なら`build.gradle`を見たことがあると思います。
「ライブラリとか追加するときに編集する奴」程度の認識で
あまりgradleの恩恵を分かっていませんでした


今回はAndroidではなくコマンドライン上での実行になるため、
`kotlinc`コマンド でコンパイルし、`kotlin`や`java`で実行をしますが
ライブラリ等の依存関係管理がとても面倒です。
ここでgradleが登場します。

`gradle`を使えばライブラリの管理やビルド、実行を全て行ってくれます。
(厳密にはそれ以外にもなんでも出来るみたいです)
`build.gradle`に依存関係やmainクラス名を書きます。

その後`gradle run`を叩けばでビルドと実行をやってくれます
`android studio`で開発するときもエディタが自動で認識して
コーディングヒントを与えてくれます
gradleの理解が曖昧だったためここに一番時間がかかりましたが、結局こうなりました。

```gradle
build.gradle

apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = "ScrapingKt"
buildscript {
    ext.kotlin_version = '1.2.41'
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile "org.jsoup:jsoup:1.10.3"
    compile "org.apache.httpcomponents:httpclient:4.5"
}
allprojects {
    repositories {
        jcenter()
    }
}
```

### 4 動くか確認
くそコードで動くか確認。
build.gradleでスクレイピング用ライブラリ`Jsoup`を足しているので
それが使えるかの確認です。

```kotlin:src/main/kotlin/scraping.kt
import org.jsoup.Jsoup

//Jsoupが使えているかのテスト。引数とかはまだ特に関係ない。
class Hoge(val url: String) {
    fun test() {
        val doc = org.jsoup.Jsoup.parse("<div id='a'>aiueo</div><div id='b'>bbb</div>")
        println(doc.select("#a"))
    }
}


fun main(args: Array<String>) {
    val foo = Hoge("test")
    foo.test()

}
```

`docker-compose up`を叩き、```<div id='a'>aiueo</div>```とコンソールに出れば成功です

### 3 スクレイピングする。
準備が整ったのでスクレイピングしていきます。
まずはkotlinうんぬんの前に、
「どのURLにどういう値を投げれば、何が返ってくるのか」
を
ChromeのDeveloper Toolで確認
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/261659/5d677fc2-3f21-f963-4e7b-03b2625c0065.png)
`https://search.yahoo.co.jp/image/paginationjs`というAPIが関係してそう。


GETなのでブラウザのURLに直接入れて確認。
投げる値を変更したり、余計そうなのを無くしてみたり。
`.crumb`
`.ncrumb`
が必須みたい？

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/261659/d8d2f95f-6cd6-a4e8-3ea7-7477bf711b43.png)

てかjsonで帰ってくるけど、中身はほぼhtmlなのかー。(エスケープ処理はしている?)

エスケープシーケンスの\を取り除いてjsoupに流し込めば、セレクタ使ってほしいものがとれそう。



やっかいなのは。
`.crumb`
`.ncrumb`
最初はGET値決め打ちで通信出来たけど、時間がたつと失敗してしまう。
`.ncrumb`が時間とともに変わってるくさい。
じゃあこいつらはどこで生成されてるんだ、どうせ`hidden`だろうと思ったが
`https://search.yahoo.co.jp/image/search`中でjavascriptにより生成されている
セレクタ使えないじゃんーってことで正規表現でゴリゴリ取得。
ちなみにUser Agent見てるらしく、適当だとこの部分だけ生成してくれない。
かなり苦戦。


APIの仕様が分かれば、後はセレクタで欲しいものを指定して、
ループ処理をするだけ。
ファイル保存関連はググればJAVAのコードがたくさん出てきます

### 最終的に
最終的に[こんな感じ](https://github.com/ginjake/kotlin_get_kotori/)になりました
`docker-compose up`
をすると南ことりちゃんの画像が自動でsaveディレクトリに保存されるようになりました
### 以下感想

#### エディタについて
最初はatomでやってましたが、
最終的にはandroid-studioで作業しました。
この神IDEを無料で使えるのが強みの1つだと思います。
JAVAのコードを貼ると自動でkotlinに変えてくれるので、JAVA未経験マンにとってはとても助かりました

#### スクレイピング
結局はセレクタ使うので、ある程度ベースができればjavascriptやpythonでのスクレピングとあんまり変らないかも
Javaの広大な知見がそのまま使えるのが非常に助かります、

#### 画像の質
ディープラーニング用の画像を集める　という目的もあったのですが
個々の画像の精度に関してはyahoo画像検索よりもpinterestのほうが良さそうでした。
[pinterestからディープラーニング用二次元画像を集める](https://github.com/ginjake/img_get_from_pinterest)
とは言えyahoo画像検索の画像数は大量のため、絞り込み条件をうまく使えば
実用的だなぁと思いまいた

#### 最後に
・kotlin、名前に惹かれて勉強したけどかなり良い言語
・Android Studio神
・間違い等あると思いますのでご指摘お待ちしております
・rootが取れなくてインテリアになった[【作ってみた】スクフェス専用コントローラー](http://www.nicovideo.jp/watch/sm23836865)