Heroku

フリー

  • 無料アカウントは550時間
  • アドオンを使えないので定時実行、スリープ等できない。
  • APSchedulerで定時実行できるがバックグラウンドで常時動いているので時間がなくなる。

クレジットカードを登録すると

  • 450時間プラスで合計1000時間利用可能
  • アドオンが利用できる(Heroku Scheduler、process-scheduler)

プロセスタイプ

  • web、worker、urgentworker、clock等

  • webはhttpアクセスが受信できる特別なプロセス。web以外なら英数字で任意の名前で登録可能

コマンド

ログイン

heroku login

ログアウト

heroku logout

アプリ作成

heroku create アプリ名

ログの確認

heroku logs --app アプリ名

heroku logs -t

ステータス確認

heroku ps --app アプリ名

コマンド実行

heroku run python xxxxx.py

環境変数

heroku config --app アプリ名

タイムゾーンの変更

heroku config:add TZ=Asia/Tokyo  --app アプリ名

アプリ一覧

heroku list

git clone

heroku git:clone -a アプリ名

heroku maintenance:on
git push heroku master
heroku maintenance:off

HerokuでChrome Headlessでスクレイピング

git init
git add .
git commit -m "my first commit"

heroku create xxxxx

git push heroku master

Chromeとchromedriverをbuildpacksに追加

heroku create --buildpack https://github.com/heroku/heroku-buildpack-python.git
heroku buildpacks:set https://github.com/heroku/heroku-buildpack-chromedriver.git
heroku buildpacks:set https://github.com/heroku/heroku-buildpack-google-chrome.git

フォント

./fonts ディレクトリにフォントファイルを置いて git push

https://www.google.com/get/noto/#sans-jpan

# --- coding: utf-8 ---
"""
えひめ医療情報ネットの今治市地区の当番医案内から今日の医療機関をツイート
"""

import datetime
import os
import re

from bs4 import BeautifulSoup

import twitter
from apscheduler.schedulers.blocking import BlockingScheduler
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

sched = BlockingScheduler()


@sched.scheduled_job('cron', hour=7)
def scheduled_job():
    # Ubuntu
    options = Options()
    options.binary_location = '/app/.apt/usr/bin/google-chrome'
    options.add_argument('--headless')
    options.add_argument('--disable-gpu')
    driver = webdriver.Chrome(chrome_options=options)

    # ブラウザ操作
    driver.get("http://www.qq.pref.ehime.jp/qq38/qqport/kenmintop/")
    driver.find_element_by_css_selector(
        "div.group2 > input.each-menu-citizen__button-hover").click()
    driver.find_element_by_id("id_blockCd000004").click()
    driver.find_element_by_name("forward_next").click()

    # スクリーンショット
    # driver.save_screenshot("ss.png")

    html = driver.page_source
    driver.quit()

    soup = BeautifulSoup(html, 'html.parser')

    table = soup.find_all(
        'table', class_='comTblGyoumuCommon', summary='検索結果一覧を表示しています。')

    shimanami = ['吉海町', '宮窪町', '伯方町', '上浦町', '大三島町', '関前']

    # 今日の日付
    today = datetime.date.today()

    # 指定日
    # today = datetime.date(2017, 10, 2)

    for i in table:

        # 日付取得
        date = i.td.get_text(strip=True).split()
        hiduke = datetime.datetime.strptime(date[0], '%Y年%m月%d日')

        if hiduke.date() == today:

            mae = []
            result = []

            for hospital in i.find_all('tr', id=re.compile('1|2|3')):

                temp = hospital.get_text('|', strip=True).split('|')

                # ID 病院名 住所 昼 昼TEL 夜 夜TEL 診療科目 受付時間
                if hospital['id'] == '1':
                    byouin = [9] + temp[1:]

                elif hospital['id'] == '2':
                    byouin = [9] + mae[1:7] + temp

                elif hospital['id'] == '3':
                    byouin = [9] + temp

                # 夜間の電話がないところは空白挿入
                if byouin[5] != 'TEL(夜)':
                    byouin.insert(5, 'TEL(夜)')
                    byouin.insert(6, None)

                # 昼間と夜間が同じ病院の場合は結合
                if len(byouin) > 9:
                    jikan = '\n'.join(byouin[8:]).replace('17:30\n17:30~', '')
                    byouin[8] = jikan

                # 診療科目のID
                # 0:救急
                # 2:内科
                # 4:小児科
                # 8:島嶼部
                # 9:その他

                if byouin[7] == '内科':
                    byouin[0] = 2

                elif byouin[7] == '小児科':
                    byouin[0] = 4

                elif byouin[7] == '指定なし':

                    # 住所が島嶼部の場合は、診療科目を島嶼部に変更
                    for j in shimanami:
                        if j in byouin[2]:

                            byouin[0] = 8
                            byouin[7] = '島嶼部'

                            break

                    # それ以外の場合は救急、診療科目をNoneに変更
                    else:
                        byouin[0] = 0
                        byouin[7] = None

                # id="2"の時用に直前の病院を保存
                mae = byouin[:10]

                # 結果を保存
                result.append(byouin[:10])

            # ID、時間の順でソート
            hospital_list = sorted(result, key=lambda x: (x[0], x[8]))

            # 文章作成
            # 日付・曜日
            twit_date = ['{0[0]} {0[1]}'.format(date)]

            # 陸地部
            twit_riku = [('【{0[7]}】\n'.format(x)
                          if x[7] else '') + '{0[1]}\n{0[8]}'.format(x)
                         for x in hospital_list if x[0] < 8]

            # 島嶼部・その他
            twit_sima = [('【{0[7]}】\n'.format(x)
                          if x[7] else '') + '{0[1]}\n{0[8]}'.format(x)
                         for x in hospital_list if x[0] > 7]

            # 全文
            twit_all = '\n\n'.join(twit_date + twit_riku + twit_sima).strip()

            print(twit_all)

            api = twitter.Api(
                consumer_key=os.environ["CONSUMER_KEY"],
                consumer_secret=os.environ["CONSUMER_SECRET"],
                access_token_key=os.environ["ACCESS_TOKEN_KEY"],
                access_token_secret=os.environ["ACCESS_TOKEN_SECRET"])

            # 140文字以内か
            if len(twit_all) < 140:
                # 全文ツイート
                api.PostUpdate(twit_all)

            else:
                # 島嶼部他ツイート
                api.PostUpdate('\n\n'.join(twit_date + twit_sima).strip())
                # 陸地部ツイート
                api.PostUpdate('\n\n'.join(twit_date + twit_riku).strip())

            break


sched.start()

qiita.com

Herokuでphantomjsを使う|プログラムメモ

Ubuntu設定からHerokuでPythonのTwitter Bot作成

# heroku cli インストール
wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh

# python3.6インストール
sudo add-apt-repository ppa:fkrull/deadsnakes
sudo apt update
sudo apt install python3.6

wget https://bootstrap.pypa.io/ez_setup.py
sudo -H python3.6 ez_setup.py

# pipインストール
wget https://bootstrap.pypa.io/get-pip.py
sudo -H python3.6 get-pip.py

python3.6 -m pip install --user pipenv

pipenv --python 3.6

# 3.6だとエラーがでるので3.5をメインにする
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 2
sudo update-alternatives --config python3


heroku login

cd tamagawa

pipenv install python-twitter
pipenv install html5lib
pipenv install beautifulsoup4
pipenv install apscheduler

pipenv run pip freeze > requirements.txt
echo "python-3.6.2" > runtime.txt
echo "clock: python clock.py" > Procfile

git config --global user.name hogehoge
git config --global user.email xxxxxx@gmail.com

rm -rf .git

git init
git add .
git commit -m "my first commit"

heroku create hogehoge

git push heroku master

heroku config:set CONSUMER_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
heroku config:set CONSUMER_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
heroku config:set ACCESS_TOKEN_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
heroku config:set ACCESS_TOKEN_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

heroku config:add TZ=Asia/Tokyo

heroku ps:scale clock=1

Python3

devcenter.heroku.com

How to Install Python 3.6.1 in Ubuntu 16.04 LTS | UbuntuHandbook

www.python.jp

pipenv

qiita.com

utgwkk.hateblo.jp

heroku

qiita.com

hwhw.hatenablog.com

review-of-my-life.blogspot.jp

qiita.com

devcenter.heroku.com

github.com

azuuun-memorandum.hatenablog.com

bottleとfeedgeneratorでRSSを作成

sudo -H pip3 install bottle
sudo -H pip3 install feedgenerator
sudo -H pip3 install pytz
import datetime
import pytz
import os
import re
from urllib.parse import urljoin
from urllib.request import urlopen

from bs4 import BeautifulSoup

import feedgenerator
from bottle import route, run


def get_refuge(url):

    html = urlopen(url).read()
    soup = BeautifulSoup(html, 'html.parser')

    title = soup.select_one('#main_container > h1, h3').get_text(strip=True)

    date_pattern = re.compile(
        '(\d{4})年(\d{1,2})月(\d{1,2})日[  ](\d{1,2})時(\d{1,2})分')
    result = date_pattern.search(title)

    if result:
        d = [int(i) for i in result.groups()]
        tz = pytz.timezone("Asia/Tokyo")
        pubdate = tz.localize(datetime.datetime(*d))

    description = soup.select_one('#main_container > p').get_text(strip=True)

    return title, url, description, pubdate


def scraping(url, css_select):

    html = urlopen(url).read()
    soup = BeautifulSoup(html, 'html.parser')

    result = [
        get_refuge(urljoin(url, i.get('href')))
        for i in soup.select(css_select)
    ]

    return result


@route('/urge/')
def index_urge():

    # 避難準備情報、避難勧告、避難指示情報

    urge = scraping('http://www.city.imabari.ehime.jp/bousai/kankoku/',
                    '#main_container > p > a')

    feed = feedgenerator.Rss201rev2Feed(
        title="今治市の避難情報",
        link="http://www.city.imabari.ehime.jp/bousai/kankoku/",
        description="避難準備情報、避難勧告、避難指示情報",
        language="ja", )

    for i in urge:
        feed.add_item(title=i[0], link=i[1], description=i[2], pubdate=i[3])

    return feed.writeString('utf-8')

@route('/shelter/')
def index_shelter():

    # 避難所情報

    shelter = scraping('http://www.city.imabari.ehime.jp/bousai/hinanjo/',
                    '#main_container > div > p > a')

    feed = feedgenerator.Rss201rev2Feed(
        title="今治市の避難所情報",
        link="http://www.city.imabari.ehime.jp/bousai/hinanjo/",
        description="避難所情報",
        language="ja", )

    for i in shelter:
        feed.add_item(title=i[0], link=i[1], description=i[2], pubdate=i[3])

    return feed.writeString('utf-8')


run(host=os.getenv("IP", "0.0.0.0"), port=int(os.environ.get("PORT", 8080)))

feedgeneratorでRSS生成

import feedgenerator

feed = feedgenerator.Rss201rev2Feed(
    title="Poynter E-Media Tidbits",
    link="http://www.poynter.org/column.asp?id=31",
    description=
    "A group Weblog by the sharpest minds in online media/journalism/publishing.",
    language="en", )

feed.add_item(
    title="Hello",
    link="http://www.holovaty.com/test/",
    description="Testing.")

with open('test.rss', 'w') as fp:
    feed.write(fp, 'utf-8')

英語だと問題ないが

import feedgenerator

feed = feedgenerator.Rss201rev2Feed(
    title="あいうえお",
    link="http://www.poynter.org/column.asp?id=31",
    description=
    "かきくけこ",
    language="ja", )

feed.add_item(
    title="Hello",
    link="http://www.holovaty.com/test/",
    description="さしすせそ")

with open('test.rss', 'w') as fp:
    feed.write(fp, 'utf-8')

print(feed.writeString('utf-8'))

日本語だとファイルに保存すると文字化け

printはきちんと日本語表示されている

どうもWindows10のPython3.6.2だとutf7で書き出されているみたい

UbuntuのPython3.5.2だとutf8で出力された

Windowsでutf8にするにはどうやればいいんだろう

今治市の避難準備情報、避難勧告、避難指示情報と避難所情報をスクレイピング

import datetime
import re
from urllib.parse import urljoin
from urllib.request import urlopen

from bs4 import BeautifulSoup


def get_refuge(url):

    html = urlopen(url).read()
    soup = BeautifulSoup(html, 'html.parser')

    title = soup.select_one('#main_container > h1, h3').get_text(strip=True)

    date_pattern = re.compile(
        '(\d{4})年(\d{1,2})月(\d{1,2})日[  ](\d{1,2})時(\d{1,2})分')
    result = date_pattern.search(title)

    if result:
        d = map(int, result.groups())

        pubdate = datetime.datetime(*d)

    description = soup.select_one('#main_container > p').get_text(strip=True)

    return title, description, url, pubdate


def scraping(url, css_select):

    html = urlopen(url).read()
    soup = BeautifulSoup(html, 'html.parser')

    result = [
        get_refuge(urljoin(url, i.get('href')))
        for i in soup.select(css_select)
    ]

    return result


if __name__ == '__main__':

    # 避難準備情報、避難勧告、避難指示情報

    urge = scraping('http://www.city.imabari.ehime.jp/bousai/kankoku/',
                    '#main_container > p > a')
    for i in urge:
        print('\n'.join(i[:3]))
        print('-' * 30)

    # 避難所情報

    shelter = scraping('http://www.city.imabari.ehime.jp/bousai/hinanjo/',
                       '#main_container > div > p > a')
    for i in shelter:
        print('\n'.join(i[:3]))
        print('-' * 30)