iphoneからpython使ってスケジュールアプリを操作する


#モチベーション
最近、iphoneでyoutubeのライブ配信を見るのにはまっています。
アプリ版は事前にアラートで通知を出してくれてとても便利ですね。

当たり前にあるアラート機能ですが、もしもこの機能がないと、
視聴者は毎回番組表をチェックしにいかないといけません。

「不規則にタイムスケジュールが変動する」ようなコンテンツでは、
youtubeのようなアラーム機能がないと見逃してしまうことが多々あります。

私がよくみる情報サイトがまさにそんな感じで、
毎月1000円払っている有料コンテンツなのですが、スケジュールがあまりにもコロコロ変わってしまって、
見逃しすぎてしまうので、自力でアラートをだすようにしてみます。

#希望
以下のポイントだけは押さえたいと思います。

```
1.iphoneで簡単に操作できる
2.アラートが出る。
```

具体的には以下の流れでざっくりやってみます。

```
1.サイトのタイムスケジュールから時間を取得
2.googleカレンダーにスケジュールを追加
3.iphoneのカレンダーアプリで通知
```

GASでやろうと思っていたのですが、
前から興味があった「google colab」でやってみます。
https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja

google colabはクラウド上でpythonを実行できるサービスです。
ブラウザがあれば動かせるので、iphone/ipadでも動かせます。

pipもできるし、apt-getとかも使えるのでとても便利そうです。


#コード
以下コード。
いまのところ定期的に手動でスクリプトを叩かないといけない運用ですが、
そのうちGASでやりたいですね。


```test.py
import re
import datetime
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
#ローカル環境なら要る
# import chromedriver_binary
from bs4 import BeautifulSoup
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from google.auth.transport.requests import Request
import os.path
import pickle
import time

# 0.固定値
CST_CALENDAR_ID = 'googleカレンダーのカレンダーIDを入力'
CST_LOCATION = "https://wwww.~~~~~~~~~~~~~~"

# 1.カレンダーに追加するサイトの解析
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
#ローカル環境なら要る
#options.add_argument(r"--user-data-dir=C:\Users\(ユーザー名)\AppData\Local\Google\Chrome\User Data")
driver = webdriver.Chrome('chromedriver', options=options)
driver.implicitly_wait(10)
driver.get(CST_LOCATION)
html = driver.page_source.encode('utf-8')
soup = BeautifulSoup(html, "html.parser")
arr_OA = soup.find_all('div')
arr_lap = []
all_events = []
today = datetime.date.today()
calYYYY = int(format(today, '%Y'))

flg_ToshiMatagi = False
for info_body in arr_OA:
    bs_date = info_body.find("div", class_='pgmOaTime')
    bs_title = info_body.find("p", class_='pgmTtl')
    if bs_date is not None:
        bs_date_all = bs_date.find_all("span")

        tex_date = ""
        tex_yobi = ""

        for text_tmp in bs_date_all:
            strText = text_tmp.get_text()
            # 日付抽出(想定データ： [12/25(土)]) 年号がない
            if re.findall(r'\d+\/\d+', strText) != []:
                tmp_date = re.findall(r'\d+\/\d+', strText)
                if len(tmp_date) == 1:
                    tex_date = tmp_date[0]
                continue
            # 時間抽出(想定データ： [24:00～24:30])　
            if re.findall(r'\d+:\d+～\d+:\d+', strText) != []:
                tmp_yobi = re.findall(r'\d+:\d+～\d+:\d+', strText)
                if len(tmp_yobi) == 1:
                    tex_yobi = tmp_yobi[0]
                continue
        # タイトル
        tex_title = bs_title.get_text()

        if tex_date is None or tex_yobi is None: continue
        if tex_date == "" or tex_yobi == "": continue

        # Googleカレンダー向けにフォーット変換
        if (tex_title in arr_lap) == False:
            arr_lap.append(tex_title)
            calDate = re.findall(r'\d+', tex_date)
            calTime = re.findall(r'\d+:\d+', tex_yobi)

            # めちゃくちゃ特殊な対応（やらなくてもいい）
            calPerTime = re.findall(r'\d+', calTime[0])
            calBgnHour = calPerTime[0]
            # 24,25,26時... -> 23:50
            if int(calBgnHour) >= 24:
                calTime[0] = "23:50"
                calTime[1] = "23:50"
            # 年またぎ
            cur_month = int(calDate[0])
            if cur_month == 1 and bfr_month == 12:
                calYYYY = calYYYY + 1
            bfr_month = int(cur_month)

            startDate = '{0}-{1}-{2}T{3}:00'.format(calYYYY, calDate[0], calDate[1], calTime[0])
            endDate = '{0}-{1}-{2}T{3}:30'.format(calYYYY, calDate[0], calDate[1], calTime[0])

            print(startDate,endDate,tex_title)
            if startDate is None or endDate is None: continue
            if startDate == "" or endDate == "": continue

            event = {
                'summary': tex_title,
                'location': CST_LOCATION,
                'description': tex_title,
                'start': {
                    'dateTime': startDate,
                    'timeZone': 'Japan',
                },
                'end': {
                    'dateTime': endDate,
                    'timeZone': 'Japan',
                },
            }

            all_events.append(event)

event = {
                'summary': "全てのスケジュールが完了しました。スケジュールの更新を行ってください。",
                'location': CST_LOCATION,
                'description': tex_title,
                'start': {
                    'dateTime': startDate,
                    'timeZone': 'Japan',
                },
                'end': {
                    'dateTime': endDate,
                    'timeZone': 'Japan',
                },
            }
all_events.append(event)

# 2.googleカレンダー追加
# https://developers.google.com/calendar/quickstart/python?authuser=1
# Step 1で作成したjsonファイルの内容をコードにハードコーディングしファイルを出力する。
# Colabは保存したファイルが消えるのでColab向けの特殊な対応
JSONstring = 'JSON内容'
with open('credentials.json', mode='w') as f:
    f.write(JSONstring)

SCOPES = ['https://www.googleapis.com/auth/calendar']

flow = InstalledAppFlow.from_client_secrets_file(
    'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)

for event in all_events:
    print(event)
    time.sleep(10)
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('calendar', 'v3', credentials=creds)
    # creds = GoogleCredentials.get_application_default()
    with open('token.pickle', 'wb') as token:
        pickle.dump(creds, token)
    event1 = service.events().insert(calendarId=CST_CALENDAR_ID,
                                    body=event).execute()

```