これはクローラー/Webスクレイピング Advent Calendar 2017の10日目の記事です。
はじめに
アドベントカレンダーに参加登録した時は、こんな感じでScrapydの紹介記事を書こうと思っていました。
そう。当日この軽めのスクレイピングプラットフォームを求めてScrapydを試してみたを見かけるまでは。。。
この記事の「2017年12月09日に更新」って昨日やん。。。ネタが。。。
ということで、同じこと書いてもつまらないので、Scrapydを実際にこんな感じで使っている
というのを書きます。Scapyd自体の紹介は軽めのスクレイピングプラットフォームを求めてScrapydを試してみたをご覧くださいw
docker-composeを使う
開発はローカル、本番はGCEインスタンスを立てていますが、どちらもdocker-composeを使っています。ディレクトリ構成はこんな感じです。
.
├── .env # ローカル用
├── db_init_local
│ └── db_init_local.sql # ローカルで使うmysqlコンテナ初期化用
├── docker-compose.yml # 本番(GCE)用
├── docker-compose_local.yml # ローカル用
├── nginx # nginxイメージ用。
│ ├── Dockerfile
│ └── default.template
└── scrapyd # scrapydイメージ用。
├── Dockerfile
├── requirements.txt # scrapyプロジェクトで必要なものすべて
├── scrapyd.conf # bind_address = 0.0.0.0のみ記載
└── start.sh
要点を列挙するとこんな感じです。
-
変数は
.env
から読み込む。ローカルでは上に記載したディレクトリ一覧にある.envを、GCEインスタンスではAnsibleのtemplateを使って配置した.envファイルを利用しています。 -
DBの接続情報を環境変数で渡す。.envからの読み込み。
-
JSを使ったサイトもスクレイピングできるようにseleniumコンテナも利用
-
ずっと起動しておいてもらいたいので
restart: unless-stopped
を指定 -
./scrapyd/Dockerfile
ではCMD ["./start.sh"]
し、その中でscrapyd
コマンドを実行しています。本番環境では、CloudSQLを利用するためのプロキシをstart.shの中で起動しているためです。 -
Nginxの設定ファイルは後述するようにenvsubstで書き換え。
-
これを下記のめちゃめちゃ長いコマンドで実行していますw
- 長くなっているのはdocker-compose自体をdocker runで動かしているため。
- これはContainer-Optimized OSというコンテナ実行用に最適化されたOSを利用しているからです。
docker run --rm -d --name compose -v /var/run/docker.sock:/var/run/docker.sock -v "{{ docker_home }}:/rootfs{{ docker_home }}" -w="/rootfs{{ docker_home }}" docker/compose:1.13.0 up --build --force-recreate
version: '3'
services:
scrapyd:
environment:
DB_HOST: ${DB_HOST}
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
build:
context: ./scrapyd
image: scrapyd
volumes:
- ${LOGS}:/usr/src/app/logs
- ${EGGS}:/usr/src/app/eggs
- ${DBS}:/usr/src/app/dbs
links:
- selenium
restart: unless-stopped
selenium:
image: selenium/standalone-chrome # 公式イメージ
ports:
- "4444:4444"
restart: unless-stopped
nginx:
environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME}
build:
context: ./nginx
links:
- scrapyd
ports:
- "80:80"
restart: unless-stopped
- scrapyd/Dockerfile
- 特に変わったことはしていないです。
FROM python:3.6
WORKDIR /usr/src/app
COPY ./scrapyd.conf ./requirements.txt ./start.sh ./
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 6800 # Scrapydのデフォルト値
CMD ["./start.sh"]
- nginx/Dockerfile
- nginxの設定ファイルでは環境変数をそのままでは利用できません。そこで、envsubstを使うことで、.envに記載したNGINX_SERVER_NAMEをnginxの設定で利用しています。
FROM nginx:latest
COPY default.template /etc/nginx/conf.d/default.template
CMD ["/bin/bash", "-c", "envsubst '$$NGINX_SERVER_NAME' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
ローカル用
ローカルでは下記のようにmysqlコンテナも利用しています。
実行コマンドは docker-compose -f docker-compose_local.yaml up
です。
- ローカルではCloudSQLを使わないので
command: scrapyd
で起動時の処理を上書き - mysqlコンテナは
/docker-entrypoint-initdb.d
に配置したsqlファイルやスクリプトを元にDBの初期化をします。必要なテーブルはここに置いたsqlで作成しています。
version: '3'
services:
scrapyd:
++ command: scrapyd
environment:
DB_HOST: ${DB_HOST}
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
build:
context: ./scrapyd
image: scrapyd
volumes:
- ${LOGS}:/usr/src/app/logs
- ${EGGS}:/usr/src/app/eggs
- ${DBS}:/usr/src/app/dbs
links:
- selenium
++ - mysqldb
selenium:
image: selenium/standalone-chrome
ports:
- "4444:4444"
nginx:
environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME}
build:
context: ./nginx
links:
- scrapyd
ports:
- "80:80"
++ mysqldb:
++ image: mysql
++ ports:
++ - "3306:3306"
++ environment:
++ MYSQL_ROOT_PASSWORD: "password"
++ MYSQL_USER: ${DB_USER}
++ MYSQL_PASSWORD: ${DB_PASSWORD}
++ MYSQL_DATABASE: ${DB_NAME}
++ volumes:
++ - ./db_init_local:/docker-entrypoint-initdb.d
その他の運用について
- Scrapyプロジェクトはリポジトリを分け、masterへのpush時に自動でScrapydサーバーにデプロイするようにしています。
- クローリングの実行はJenkinsの定期実行で
curl
を投げています。- こちらの記事にあるscrapyd-clientですが、現在のバージョンv1.1.0だとまだまだ機能が貧弱です。(こちらの記事でもmasterから持ってきてますね)
- いまのmasterにあるものがデプロイされたらやっと使えるかなという状況です。
- ScrapydはAnsibleでGCE上にデプロイしています。クローラ自体は日々動かしていますが、前回このコマンドを打ったのがいつか忘れてるくらいには安定的に稼働してくれています。