映画館のサイトから新作上映作品情報をLINEで通知する（Tジョイ、TOHO、イオンシネマ）

# はじめに
映画館によく行く人でも、よく行く映画館でどの作品がいつから始まるのかを全部把握することはなかなか大変です。気づいたら見たかったと思ってた作品を見逃すこともあるかもしれない

ということで、映画館のページの上映スケジュールを参考に、上映開始n日前になったらLINEに通知を飛ばすスクリプトをPythonで書いてみます。今回はシネコンのTジョイ、TOHO、イオンシネマを対象にしました

# 上映作品スケジュール取得
映画館のサイトから上映作品の情報（タイトル、上映開始日、画像URL）をスクレイピングして取得します。スクレイピング用の関数を定義します。TOHOのページではJavascriptの動作が必要なのでSeleniumを使用します

```python
def scrape(url):
    response = requests.get(url)
    response.encoding = response.apparent_encoding
    html = response.text
    return BeautifulSoup(html, 'html.parser')

def scrape_with_chrome(url):
    driver = webdriver.Chrome()
    driver.get(url)
    html = driver.page_source
    driver.quit()
    return BeautifulSoup(html, 'html.parser')
```

## Tジョイ
まずはTジョイからです。品川のTジョイを対象に上映予定の情報を調べます

```python
base_url = 'https://tjoy.jp/tjoy-prince-shinagawa'
url = f'{base_url}/theater_cinema#tab-coming-soon'

soup = scrape(url)
```

Tジョイの上映スケジュールページ用の関数を定義し、作品ごとにループさせます

```python
def parse_films_info_tjoy(soup):
    url = soup.a.get('href')
    img = soup.img.get('src')
    date_str = soup.select_one('.tab-list-date').text.strip()
    try:
        date =  datetime.datetime.strptime(date_str, "%Y/%m/%d")
    except:
        date = ''
    title = soup.select_one('.tab-list-note').text
    # info = soup.select_one('.tab-film-info').text
    
    return {
        'url': url,
        'img': img,
        'date_str': date_str,
        'date': date,
        'title': title,
        # 'info': info,
    }

tjoy_films_info = [parse_films_info_tjoy(soup_) for soup_ in soup.select_one('#comming-soon').select('li')]
```

結果はこのような感じです

![スクリーンショット 2021-01-29 1.49.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/1ece1577-1041-3bd9-8bad-8dfcde624200.png)

これと同じオブジェクトを作っていく作業になります

## TOHO
TOHOでは上映作品スケジュールのページが映画館単位では存在していなかったので、館を指定しない形でリストを作成します。また、スケジュールのページのURLが固定であるかが少し微妙だったので、ホームのページからリンクを飛ぶ形でそのURLを取得することにしました。ここでは、ホームのタブから`'公開予定'`の文字列を含むaタグのリンクをとっています

```python
# 公開予定のページのURLが固定か可変か不明なのでリンクから取得
root_url = 'https://www.tohotheater.jp'
soup = scrape(root_url)
path = [tag.a.get('href') for tag in soup.select_one('.nav.is-home').select('li') if '公開予定' in tag.text][0]
url = f'{root_url}{path}'
soup = scrape_with_chrome(url)
```

各作品の情報を取得する関数を定義し、作品ごとにループ

```python
def parse_films_info_toho(soup, base_url):
    date_str = soup.get('data-koukaiyotei')
    date =  datetime.datetime.strptime(date_str, "%Y%m%d")
    img = soup.select_one('.movies-image-inner').get('style').split('"')[1]
    url = f"{base_url}?sakuhin_cd={img.split('/images_net/movie/')[-1].split('/')[0]}"
    title = soup.text.strip()
    
    return {
        'url': url,
        'img': img,
        'date_str': date_str,
        'date': date,
        'title': title,
    }

toho_films_info = [parse_films_info_toho(soup_, url) for soup_ in soup.select('.movies-item')]
```

こちらもこんな様子で、問題ないでしょう

![スクリーンショット 2021-01-29 2.01.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/22abcacb-4b9a-e285-3d9e-7ec99d579f84.png)

## イオンシネマ
最後にイオンシネマです。ページの設計上、関数の対象範囲が変わってしまったのはあまり良くない実装でしたが半目でコードを書きました（汗）イオンシネマのみURLを引数に取ります

```python
def parse_films_info_aeon(base_url):
    base_url = base_url.replace('/cinema/', '/cinema2/')
    url1, url2 = f'{base_url}movie/comingsoon.html', f'{base_url}movie/comingsoon2.html'

    info = []
    for url in [url1, url2]:
        soup = scrape(url)
        for daily_tag in soup.select('.cDateBlock'):
            date_str = daily_tag.select_one('.startDate').text
            this_year, this_month = datetime.datetime.now().year, datetime.datetime.now().month
            month, day = int(date_str.split('月')[0]), int(date_str.split('月')[1].split('日')[0])            
            year = this_year + 1 if (this_month == 12 and month == 1) else this_year
            date = datetime.datetime.strptime(f"{year}/{month}/{day}", "%Y/%m/%d")
            
            for movie_tag in daily_tag.select('.cinemaBlock'):
                url = f"https://www.aeoncinema.com{movie_tag.a.get('href')}"
                img = f"https://www.aeoncinema.com{movie_tag.img.get('src')}"
                title = movie_tag.select_one('.cbTitle').text
                info.append({'url': url,
                             'img': img,
                             'date_str': date_str,
                             'date': date,
                             'title': title})
    return info

base_url = 'https://www.aeoncinema.com/cinema/chofu/'
aeon_films_info = parse_films_info_aeon(base_url)
```

![スクリーンショット 2021-01-29 2.05.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/d838804c-dd18-2214-0529-39315fdaaf68.png)

# n日前に上映開始する作品を探す
以上で実装した関数を用いて、指定した映画館の上映スケジュールを結合します。その中から、今日が上映n日前にあたる作品を絞り込む処理を行います

```python
# 自分が行くところのページのURLを入れる
theaters = {'tjoy': ['https://tjoy.jp/tjoy-prince-shinagawa'],
            # 'toho': [],
            'aeon': ['https://www.aeoncinema.com/cinema/chofu/']
}

# Tジョイ
films = []
for url in theaters['tjoy']:
    url = f'{url}/theater_cinema#tab-coming-soon'

    soup = scrape(url)
    films += [parse_films_info_tjoy(soup_) for soup_ in soup.select_one('#comming-soon').select('li')]
    
# TOHO
root_url = 'https://www.tohotheater.jp'
soup = scrape(root_url)
path = [tag.a.get('href') for tag in soup.select_one('.nav.is-home').select('li') if '公開予定' in tag.text][0]
url = f'{root_url}{path}'
soup = scrape_with_chrome(url)
films += [parse_films_info_toho(soup_, url) for soup_ in soup.select('.movies-item')]

# イオンシネマ
for url in theaters['aeon']:
    films += parse_films_info_aeon(url)
```

ここでは6日後に上映開始の作品を調べました

```python
days_before = 6
today = datetime.datetime.now().today()
films_released_at_day = [film for film in films if (film['date'] != '') and (int((film['date'] - today).days) == days_before)]
```

# 上映予定作品の情報をLINE Notifyで通知
通知にはLINE Notifyを用いました。以下を参考に設定できます、とてもかんたんです

https://qiita.com/iitenkida7/items/576a8226ba6584864d95

トークンはコードで使用するのでコピペしておきましょう

LINE Notifyを招待したグループが出来たら、次の関数で通知を送ることが出来ます。画像を送信するため、ioモジュールをimportしています

```python
import io

def notify_on_line(message, img_url):
    url = "https://notify-api.line.me/api/notify"
    token = "YOURAWESOMEACCESSTOKEN"
    headers = {"Authorization" : "Bearer "+ token}

    payload = {"message":  message}
    files = {"imageFile": io.BytesIO(requests.get(img_url).content)}
    post = requests.post(url ,headers = headers ,params=payload,files=files)
    
for film in films_released_at_day:
    message = f"{film['url']}\n{film['title']}\n{film['date_str']}上映開始"
    notify_on_line(message, film['img'])
```

これを実行すると次の画面のようなメッセージが、作品の数飛んできます。一日一回、cronやawsやherokuなりで実行させれば最新作を見逃すこともなくなるでしょう。

![Image_20210129_021528.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/ee0bbdb5-323f-d0f6-7f93-22f4e25d1235.jpeg)

早くレイトショー見に行きたい