oinume journal

Scratchpad of what I learned

gcloudコマンドを使わずにGoogle Cloud Storageにファイルをアップロードする

概要

gcloudコマンドを使わずにPythonとgoogle-cloudを使ってGoogle Cloud Storage(GCS)に頑張ってファイルをアップロードしてみる。google-cloudはPython向けのGoogle Cloud Platformの何かを操作するためのクライアントライブラリ。GCPの以下の機能の操作がサポートされている。

  • GA
    • Google Cloud Datastore
    • Google Cloud Storage
    • Google Cloud Translation
    • Stackdriver Logging
  • Beta
    • Google BigQuery
    • Google Cloud Natural Language
    • Google Cloud Speech
    • Google Cloud Video Intelligence
    • Google Cloud Vision
  • Alpha
    • Google Cloud Bigtable
    • Google Cloud Bigtable
    • Google Cloud DNS
    • Google Cloud Pub/Sub
    • Google Cloud Resource Manager
    • Google Cloud Runtime Configuration
    • Google Cloud Spanner
    • Stackdriver Error Reporting
    • Stackdriver Monitoring

gcloudコマンドはPython 2.7を必要とするんだけど、Python 2.7をどうしてもインストールできない環境で役立つ話だと思う。自分はHerokuでPython 3.6.x を使っていて、gcloudコマンドのためにPython 2.7をインストールできなくて困っていたのだけど、このgoogle-cloudライブラリを使うようにしてみた。

セットアップ

前提として

  • Python 3.4 以上
  • pip

がインストールされていること。

$ pip install google-cloud

でgoogle-cloudをインストールする。

認証

google-cloudのライブラリを使うにあたり、credentialが必要になる。おそらく一番ラクなのは

  • GCPのConsoleでService Accountを作って
  • そのcredential情報をJSONファイルとして取得し
  • GOOGLE_APPLICATION_CREDENTIALS 環境変数で指定する

ことだと思う。詳しい話は google-authのドキュメントに書いてある。その他の認証のやり方についても詳しく書いてある。

Pythonのスクリプトを実行する前に以下のコマンドで credential 情報のJSONファイルを指定すれば、Pythonのスクリプトでは特に何もしなくてOKだ。

$ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json

GCSにファイルをアップロードする

それでは本題。google-cloudのstorageモジュールを使ってGCSにファイルをアップロードしてみる。

  • get_bucketでバケットを取得して
  • blobオブジェクトを作ってアップロードする

感じ。

from google.cloud import storage
import sys


def upload(bucket_name, f):
    client = storage.Client()
    bucket = client.get_bucket(bucket_name)
    new_blob = bucket.blob(f)
    new_blob.upload_from_filename(f)


if __name__ == '__main__':
    if len(sys.argv) < 3:
        print('gcs.py <bucket> <file>', file=sys.stderr)
        sys.exit(1)
    upload(sys.argv[1], sys.argv[2])

まとめ

gcloudコマンドは早くPython3対応するかGoで書き直されて欲しい。

プログラマのためのGoogle Cloud Platform入門 サービスの全体像からクラウドネイティブアプリケーション構築まで

プログラマのためのGoogle Cloud Platform入門 サービスの全体像からクラウドネイティブアプリケーション構築まで

Better Heroku Schedulerを探したらCustom clock processesにたどり着いた

これはHeroku Advent Calendar 2016の11日目の記事です。

HerokuではHeroku Schedulerというcronのようなサービスがあるのですが、使い込むうちに「Daily, Hourly, Every 10 minutesの単位でしかジョブを動かせない」という制限がつらくなってきたので、より良い代替であるCustom clock processesを試してみたという話。

Custom clock processesとは?

clockというプロセスを立ち上げっぱなしにして、そのプロセス内でスケジューリングされたジョブを実行する仕組み。Heroku Schedulerは実行される時だけプロセスが立ち上がるけど、clockの場合は常にプロセスが常駐する。

各言語のScheduler

HerokuのCustom clock processesのドキュメントで触れられているのはJavaとPythonの2種類だけど、各言語ごとにだいたいcron likeなSchedulerが用意されている。今回は自分が慣れているPythonのAPSchedulerを試してみた。

APSchedulerの設定

まずPythonのモジュールのインストールのために、requirements.txtというファイルを用意する。

echo APScheduler > requirements.txt

次にProcfileに以下を追記する。

clock: python clock.py

ここで指定されているclock.pyがジョブの設定が書かれたファイルになる。どのように記述するべきか、詳しくは公式のドキュメントに書かれている。以下は1分毎にhello worldを出力する設定。

from apscheduler.schedulers.blocking import BlockingScheduler
import subprocess

scheduler = BlockingScheduler()

@scheduler.scheduled_job('interval', minutes=1)
def timed_job():
    print("Run notifier")
    subprocess.run("hello world", shell=True, check=True)

scheduler.start()

最後に、APSchedulerを動かすのにはPythonが必要なので、buildpackを追加。

heroku buildpacks:add https://github.com/heroku/heroku-buildpack-python

あとはclock.pyをコミットしてディプロイすればOK。

ローカルで試したい場合

ローカルで試してみたい場合は以下のようにAPSchedulerをインストールして、clock.pyを直接呼び出せばOK。

pip install -r requirements.txt
python ./clock.py

Custom clock processesとFree Dyno Hours

Heroku Schedulerは実行する際にプロセスが起動されて、プロセスが動いていた時間だけ無料分のFree Dyno Hoursを消費するのだけど、custom clock processesはworkerのようにclockプロセスが常駐する形になるので、起動していた時間分だけFree Dyno Hoursを消費するようなので注意が必要。webと併用していると倍速でFree Dyno Hoursを消費するので気をつけましょう。

まとめ

Heroku Schedulerよりきめ細かい時限式のジョブを定義したいときはCustom clock processesを使おう!

Parsing MySQL's URL in Python3

Just add urllib.parse.uses_netloc.append("mysql") if you want to parse URL such as mysql://root:pass@localhost/demo.

#!/usr/bin/env python

import urllib.parse
urllib.parse.uses_netloc.append("mysql")

if __name__ == "__main__":
    url_str = "mysql://root:pass@localhost/demo"
    url = urllib.parse.urlparse(url_str)
    print("URL={0}".format(url_str))
    print("host={0}, user={1}, password={2}, database={3}".format(
        url.hostname, url.username, url.password, url.path[1:]))
$ python3 url_parse.python
URL=mysql://root:pass@localhost/demo
host=localhost, user=root, password=pass, database=demo

DMM英会話でお気に入りの先生の空きレッスンが登録されたらメールで通知するヤツ作った

2016/11/25追記

後継のlekcijeというサービスを作ったのでぜひこちらをご利用下さい。

www.lekcije.com

動機

  • どのSkype英会話でもそうだと思うんだけど、人気のある先生はすぐ予約が埋まっちゃう
  • でもスケジュールをいちいち検索してチェックするのはダルい

というわけで作りました。

使った技術

github.com

ソースコードは↑。"Deploy to Heroku" buttonをつけておいたので、Herokuアカウントがあれば誰でもこのアプリケーションを使うことができる。やり方はREADMEを見てもらえればと。

概要

  • Heroku Schedulerで10分おきにお気に入りの先生のスケジュールのページをスクレイピング
  • 前回のスケジュール(DB)と今回スクレイピングしたスケジュールを比較
    • 新しく空きレッスンがある場合はメールで通知
  • スクレイピングしたスケジュールをDBに登録
  • スケジュールをチェックしたい先生のIDを入力する画面を作るのが辛かったので、その辺は環境変数経由でIDを渡すという手抜きな構造
    • 超ヒマになったら画面作りたい

結果

人気の先生の予約がめっちゃ取りやすくなったし、副次的な効果としてキャンセルで空いたレッスンにもすぐ気付けるようになったので、DMM英会話やってる人は使ってみたらいいと思います。

どんどん話すための瞬間英作文トレーニング (CD BOOK)

どんどん話すための瞬間英作文トレーニング (CD BOOK)

余談

これを作り始めてからGitHubに草が生えるようになった。

commits of github.com

今回は小規模だったためORMapperなどのライブラリ・フレームワークを使わないで実装したけど、会社だけでコードを書いているといかに自分がフレームワークやライブラリに依存したコードしか書けないかを思い知らされて良かったです。

Logging into dmm.com by Selenium

I use Selenium to log into dmm.com because dmm.com requires JavaScript enabled browser. Here is a sample code. You can see Firefox is launched and automatically logging into dmm.com if you use Firefox instead of PhantomJS.

login_dmm.py

#!/usr/bin/env python
# encoding: utf-8

import os
from selenium import webdriver

driver = webdriver.PhantomJS()
#driver = webdriver.Firefox()
driver.get("https://www.dmm.com/my/-/login/=/path=DRVESVwZTldRDlBRRFdIUwwIGFVfVEs_")
login_id = driver.find_element_by_id("login_id")
login_id.send_keys(os.environ["DMM_LOGIN_ID"])
password = driver.find_element_by_id("password")
password.send_keys(os.environ["DMM_LOGIN_PASSWORD"])
driver.find_element_by_tag_name("form").submit()
print(driver.page_source)

driver.quit()

Run

$ brew install phantomjs
$ pip install selenium
$ DMM_LOGIN_ID=your_id DMM_LOGIN_PASSWORD=your_password python ./login_dmm.py