reCAPTCHAのサイトを毎日自動スクレイピングする (5/7: 2captcha)

1. [要件定義〜python環境構築](https://qiita.com/kamyu1201@github/items/65a86ec1e28ec9969dcb)
1. [サイトのスクレイピング機構を作る](https://qiita.com/kamyu1201@github/items/a07c7d175c051b8ab4c0)
1. [ダウンロードしたファイル(xls)を加工し、最終成果物(csv)を作成するようにする](https://qiita.com/kamyu1201@github/items/925337a317dec3c6f5d0)
1. [S3からのファイルダウンロード / S3へのファイルアップロードをつくる](https://qiita.com/kamyu1201@github/items/078a9c6239811998843e)
1. **2captchaを実装**
1. Dockerコンテナで起動できるようにする
1. AWS batchに登録


# 自動実行へ向けて

前回までで、目的を達成できるプログラムになりました。
が…今回の要件では、これを毎日定期実行する必要があります。

通常 batchシステムを定期実行するにはcronなどでやれば良いのですが、今回はそう簡単には行きません。

1. reCAPTCHAを解除する必要がある(画面が必要、手作業が必要
1. seleniumも、headlessモード(画面なし)では動かない (クリックによるダウンロードが必須だったため)

まずは、reCAPTHCA対策です。
調べてみると、「[2CAPTCHA](https://2captcha.com/)」というロシアのサービスがあることがわかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21288/4bd5b047-6e0b-d4ad-30de-cc9b018de8d7.png)

reCAPTCHAをリモートで人力解除してくれる、というサービスです。
1000回の突破で数百円、という破格の安さ。ちょっと怪しいな…と思ったものの、使ってみることにしました。


# 2captchaへの登録〜課金

2captchaへアカウント登録し、Balanceにお金を入れます。
クレジットカードを登録して使っただけ課金される…というものではなく、入れたお金のぶんだけサービスを使えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21288/292758e0-fac6-d227-4ab1-a62b4990ad76.png)

使い方は他に紹介している方がいるので割愛します。

https://tanuhack.com/pr-2captcha/

ただ、「PayU」は使えなかったので、「PayProGlobal」経由でpaypalを使い、まずは300円課金しました。いまのレートだとこれで3000回くらいいけそうです。


# 2captchaの設置


### 事前準備

まずは、

- 2CaptchaのAPI KEYの取得
- 当該サイトのreCAPTCHAのgoogle_site_keyを取得
- 当該サイトの「textarea#g-recaptcha-response」を見つけておく

の3つをする必要があります。

google_site_keyは、上のサイトでは`data-sitekeyで検索すると一発`とありますが、私の場合はソース上のjavascriptにありました。`recaptcha`とかで検索して見つけた感じです。（逆に、このサービスを使った突破を防ぐには、ここを探しにくくしておく…というのもいいかもしれません）

textareaは#g-recaptcha-responseがすぐに見つかりました。仕組み上、こちらは変えられないでしょうね…。


### textareaを可視にする

上の紹介サイトにあるとおりですが、textareaが不可視になっていると入力ができないため、javascriptを使って可視化します。

また私の対象サイトでは、reCAPTCHAのチェックボックス自体も隠されていました。「ログインボタンを押すとreCAPTCHAが出てくる（解除後、もう一度ログインボタンを押すとログインできる）」という挙動でした。

```python
driver.execute_script('document.querySelector(hoge).style.height = "auto";')
driver.execute_script('document.querySelector(hoge).style.position = "inherit";')
driver.execute_script('document.getElementById("g-recaptcha-response").style.display="";')
```

### 2captchaに解除を依頼

まず、下記のようにして captcha_idを取得します。
これでERRORになったことは無いですが、サービスのメンテナンスなどにあたるとそうなるのでしょう。

```python
#2captchaの準備ができているか確認
url = "http://2captcha.com/in.php?key=" + config.service_key + "&method=userrecaptcha&googlekey=" + config.google_site_key + "&pageurl=" + LOGIN_URL 
resp = requests.get(url) 
if resp.text[0:2] != 'OK': 
    exit('2captcha Service error. Error code:' + resp.text) 
captcha_id = resp.text[3:]
```

次に、そのcaptcha_idを使って解除を依頼します。

```python
#実際に解除を依頼
fetch_url = "http://2captcha.com/res.php?key="+ config.service_key + "&action=get&id=" + captcha_id
print('解除を依頼中…')
for __i in range(1, 10):
    time.sleep(5) # wait 5 sec.
    resp = requests.get(fetch_url)
    if resp.text[0:2] == 'OK':
        break
print('Google response token: ', resp.text[3:])
```

詳細まで調べていませんが、responseで「CHA_NOT_READY」というものが返ることがあるようです。
解除するスタッフの準備ができていないときに起こるのでしょうか。
この場合は困るので、私の場合は何度かやり直すように実装しました。

```python
if resp.text[3:] == 'CHA_NOT_READY':
    print('処理に失敗')
    driver.quit()
    if count == 0:
        exit('Error: 2captcha is not ready')
    else:
        #やり直す
        return getLoginedDriver(config,count-1)
```

無事にトークンが帰ってきたら、textareaに入れてログイン実行です。

```python
# textareaにトークンを入力する
driver.find_element_by_id('g-recaptcha-response').send_keys(resp.text[3:])
time.sleep(INTERVAL)

driver.execute_script('document.querySelector(hoge).style.visibility = "hidden";') #このサイトの場合はログインボタンを押すのにこれが必要だった
submit_button = driver.find_element_by_css_selector(hoge)
submit_button.click()
```

# 実行

やってみたらわかるのですが、すごいなこれ…素敵なサービスに感謝です。

ただ、このサービスの存続にシステムが依存することになるので、やはりできるならスクレイピングなんてやりたくないな、とも改めて思いました。
今回のサイトの運営とはAPIを準備してもらえるよう交渉していますが、うまく行ってほしい。



# 完成

reCAPTCHAが出ないこともあるので、そのケースにも対応できるようにしたら完成です。
これで、runするだけで(reCAPTCHAを解除せずとも) 動くようになりました。

あとはこれをローカルPCではなく、サーバで実行するだけなのですが…
headlessモードで動かないため、もう一山です。


続きはまた。