HerokuでGoogleログインしてスクレイピング(無料)

# HerokuでSeleniumとHeadless ChromeをPythonで動作させたい

大学の学内サイトの情報を自動通知したいと思ってやりました。googleログインの後、スクレイピングを行いたい人向け。
※ローカルで動作していることを前提にしています。


### 作業前のローカル環境
- Windows10
- Python 3.7.3
- Git 2.9.0

※ローカル環境ではコマンドプロンプトやpowershellで実行しています

## Heroku環境構築
### herokuの準備
herokuへアカウント登録を完了させ、Heroku CLIをインストールします。なお、公式のチュートリアルを参考にしてください。

[Heroku ダウンロード](https://signup.heroku.com/)
[Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli)

herokuコマンドが使用できるようになったらコマンドラインからログインします。

```cmd
>heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/browser/<herokuの番号>
Logging in... done
Logged in as <herokuのアカウント>
```
コマンドを実行すると、ブラウザが立ち上がるので、ログインしてください。正常にログインが完了されれまこのような表示になるはずです。
### Pythonサンプルをダウンロード
herokuが用意しているサンプルアプリを適当な場所でcloneしてください。公式のチュートリアルをある程度進めている前提です。

```cmd
git clone https://github.com/heroku/python-getting-started.git
```
### gitの準備
heroku にはgit pushを利用してデプロイするため、gitを入れてください。

```cmd
cd python-getting-started
git init
git add .
git commit -m "コメント"
```
### Python環境のheroku appを作成
herokuにPython環境のappを作成しましょう。アプリ名は誰かと被っていなければ使用できます。指定をしないと、勝手に作成されます。

```cmd
heroku create <アプリ名>
```
作成できたら、pushしてみましょう。

```cmd
git push heroku master
```
### heroku appのbuildpackにchromedriverとchromeを追加
ブラウザでherokuにログインし、アプリを開きます。歯車のマークをクリックします。Buildpacksという場所に"heroku/python"とあれば成功です。
Add buildpackをクリックし、
>https://github.com/heroku/heroku-buildpack-chromedriver.git

を追加します。これでchromedriverが使えるようになります。再度Add buildpackをクリックし、
>https://github.com/heroku/heroku-buildpack-google-chrome.git

も追加します。これでchromeも追加されました。pushされたタイミングで設定されます。
### heroku appの実行
herokuでのコマンド実行は"heroku run"です。その後ろにコマンドを続けて書きます。

```cmd
heroku run python <ファイル名>.py
```
これでheroku上でPythonファイルの実行ができます。
また、herokuサーバ上で処理を行いたい場合は、

```cmd
>heroku run bash
$
```
bashを起動することで、herokuのサーバ上で実行ができます。exitを実行するまでスリープしないため、デバッグやファイルの一時保存などのチェックができます。
## 開発前段階
### gitの使い方 ※git初心者向け
Pythonファイルの更新等を行い、herokuとの同期をとるときは3つのコマンドをセットで覚えましょう。

```cmd
git add .
git commit -m "コメント"
git push heroku master
```
### ライブラリのインポート
herokuではpip installを行っても管理者権限がないため、意味がありません。ちなみにsudoも使用できません。requirements.txtにインポートしたいライブラリを書くことで、サーバがスリープから解除されたタイミングで自動的にインストールされるようです。
例としてこのように書きます。

```requirements.txt
numpy
requests
selenium
subprocess
```
ちなみに、バージョンを指定したい場合はこのように記載します。

```requirements.txt
numpy==1.14.1
```
## 本題
### Pythonにおけるheadless-chrome
基本的な流れはこのように書きます。

```python
#インポート部分
import *

def main():
    # chromedriverのPATHを指定(herokuにおけるパスを指定しています)
    driver_path = '/app/.chromedriver/bin/chromedriver'
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    #driverに設定 ※optionsを指定しないとheadlessにならないので注意
    driver = webdriver.Chrome(options=options, executable_path=driver_path)
    driver.get("参照したいURL")
    time.sleep(1) #1秒間待つ
    print(driver.current_url) #現在のURLを出力
    
    # 処理開始
    # 情報取得・加工
    # 処理終了

    # 終了
    driver.quit()
    
if __name__ == '__main__':
    main()
```
インポート部分は適当なものを書いてください。osやtime等の組み込まれているものを除き、前述のとおり、requirements.txtに書いていないものは、インポートできませんので注意です。

### Googleログインについて
一番簡単な方法を記載します。id指定やname指定、xpath指定でも可能ですが、知識がない方はこの方法でやってみてください。idやname、xpath指定のやり方は別の記事で書きます。アクティブなフィールドを取得し、そこにIDやパスを入力しています。

```python
    #ログイン情報
    login_id = "メールアドレス"
    login_pw = "パスワード"
    
    #メールアドレス入力
    el_id = driver.switch_to.active_element
    el_id.send_keys(login_id)
    el_id.send_keys(Keys.ENTER)        
    time.sleep(1)

    #パスワード入力
    el_pw = driver.switch_to.active_element
    el_pw.send_keys(login_pw)
    el_pw.send_keys(Keys.ENTER)
    time.sleep(1)
```
実行が遅い場合、入力の前に画面遷移が起こる場合があるため、time.sleep()は適時挟んで書いてください。
### 情報の取得
どのように要素を取得したらよいか困ったときはここを参考にしてください。
[selenium API クイックリファレンス](https://www.seleniumqref.com/api/webdriver_gyaku.html)
今回実装した一部を抜粋します。

```python
message = [] #通知用のリスト
try:
    #要素が取得できた場合
    data = driver.find_elements_by_class_name("検索するname属性")
except:
    #取得できない場合の例外処理
    data = "None"
for i in range(len(data)):
    check = data[i].text
    #ある文字列が含まれている場合のみ追加
    if check.find('対象文字列') > 0:
        message.append(check)
```
## なぜかGoogleログインできない
### 筆者の場合
自分の場合は以下のような状態でした。
>https://teratail.com/questions/195950

解決法の前に、current_urlで現在のURL情報を取得してみてください。
パスワード入力画面の後、変なURLに飛ばされている場合は、スクリーンショットを撮ってみてください。どういう状況かわかるはずです。
### 解決法
不正アクセスだと処理されている可能性があります。
#### ①googleアカウントの本人確認を完了してください
 - 再設定用の電話番号
 - 再設定用のメールアドレス
 ※URLに"az"という文字列があれば認証ページに遷移しています

#### ②パスワードを入力し、画面遷移後、ソースコードに以下を追加してください

```python
driver.find_element_by_xpath('//*[@id="authzenNext"]').click()
```
#### ③画面に認証用の番号が表示され、登録した携帯電話で「画面に表示されている番号を押してください」と表示されます。
※プログラムが終了すると、1からやり直しになるので、time.sleep()をしてください
#### ④以下のコードを追加してください

```python
#番号が表示されているページをスクリーンショット
driver.save_screenshot('result.png')
#コマンドライン上で保存したSSをフリーのアップロードサイトへ
#アップロード後、公開されているURLがresultに保存されます
result = subprocess.check_output(["curl", "--upload-file", "./result.png",  "https://transfer.sh/"])
#bytesからutf-8へデコード
result = result.decode('utf-8')
#アップロードされているURL表示
print(result)
#自分が認証番号を押すまで待機
time.sleep(120) #120秒
```
定期実行を考えている場合は、LINEやメール等でURLを送信するのもいいかもしれません。なお、携帯電話は認証画面が表示されてから、他の画面へ遷移すると、認証できませんので、注意してください。
#### ⑤今までのコードの後、スクレイピング処理に入ってください
ここまでくれば大丈夫です。ローカルと同じように処理を進めてください。
### チェックの仕方
CLI環境のherokuで、本当に正しい結果ができているか、疑問に思った際はこのようなものを使ってみてください。

```python
#再読み込み
driver.refresh()

#現在のURL取得
driver.current_url 

#スクリーンショットの保存
##ウィンドウサイズの指定
driver.set_window_size(1250, 1036)
##スクリーンショットを撮る
driver.save_screenshot('ファイル名')
```
## さいごに
ここでソースコードを公開しています。
>https://github.com/7vvXi/portfolio/releases/tag/v3.0

参考になれば幸いです。