#問題
Webサイトへのログイン/ログアウトを自動化したいが、
訳あってSeleniumが使えない。
Robobrowserやmechanicalsoupだとログアウトのリンクが選択できない。
まあ、制約などがなければ普通はSeleniumを使うよね。
さて、困った。
その時の備忘録。
ググってみると、requestsモジュールを使用して
必要なフォームパラメータをpostしてやるとログインできるぜ!
っといった記事を多く目にした。
よし!参考になる!以前自作したWebアプリで実験。
##試した環境など
・Python 3.6.7
・CentOS7
・requestsモジュールのインストール
#ログイン
まずは対象のログインフォームのソースを確認。
<form action="/login" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="SmPba8H6cRcKKH/hK5DAyyONn/LGWD7vPIMM4eJDQH3FIzu55qhYsJXOnub0xQaVguMH4O4qI5a0hAtyHSkZUg==">
<div class="form-group">
<label for="session_email">メールアドレス</label>
<input class="form-control" type="email" name="session[email]" id="session_email">
</div>
<div class="form-group">
<label for="session_password">パスワード</label>
<input class="form-control" type="password" name="session[password]" id="session_password">
</div>
<input type="submit" name="commit" value="ログイン" class="btn btn-primary btn-block" data-disable-with="ログイン">
</form>
pythonのソースはこんな感じ。
import requests
import time
session = requests.session()
session.get(url)
login_data = {
'UTF-8': '✓',
'session[email]': メールアドレス,
'session[password]': パスワード,
}
login = session.post(login url, data=login_data)
time.sleep(2) # 少し時間を置いてみる
print(login.text)
ムム、ログイン出来ない。
ん?フォームにはauthenticity_tokenというのがある。
これも必要かな?
ちなみにChromeデべロッパーツールでもpost時にフォームから何が送られているか確認できる。
ただ、authenticity_tokenはサイトアクセス毎にランダムで変わるね。
さて、どうやってこの隠れキャラを取得しようか?
##requests以外に入れたモジュール
・BeautifulSoup4
Webスクレイピングなんかでよく使われているらしい。
以下の様してみた。
import requests
import time
from bs4 import BeautifulSoup
session = requests.session()
response = session.get(url)
# BeautifulSoupオブジェクト作成(token取得の為)
bs = BeautifulSoup(response.text, 'html.parser')
login_data = {
'UTF-8': '✓',
'session[email]': メールアドレス,
'session[password]': パスワード,
}
# tokenの取得
authenticity_token = bs.find(attrs={'name':'authenticity_token'}).get('value')
# 取得したtokenをpostするパラメータに追加
login_data['authenticity_token'] = authenticity_token
login_data = session.post(login url, data=login_data)
time.sleep(2)
print(login.text)
あれ?ダメでした。。
Chromeデべロッパーツールを確認するとcookie内にセッションIDがあるみたい。
こいつのチェックに弾かれている訳ですな。
じゃあ、cookieを取得してpostしてみよう。
import requests
import time
from bs4 import BeautifulSoup
session = requests.session()
response = session.get(url)
bs = BeautifulSoup(response.text, 'html.parser')
login_datadata = {
'UTF-8': '✓',
'session[email]': メールアドレス,
'session[password]': パスワード,
}
authenticity_token = bs.find(attrs={'name':'authenticity_token'}).get('value')
login_data['authenticity_token'] = authenticity_token
# cookieの取得
response_cookie = response.cookies
print(response_cookie)
#<RequestsCookieJar[<Cookie _hogehoge_session=長いので省略
# post時にcookieを追加
login = session.post(login url, data=login_data, cookies=response_cookie)
time.sleep(2)
print(login.text)
おお!ログイン出来た。
ログアウト
ログアウトのhtmlはどうなっているか確認。
うん、フォームですらない。
<a rel="nofollow" data-method="delete" href="/logout">ログアウト</a>
とりあえすhref="/logout"にフォーカスを当てたらログアウト時のURLがわかるので、
こいつにpostしてみる。
post時に送信されるデータをChromeデべロッパーツールで確認する。
# ログアウト時に送られるデータ
logout_data = {
'_method': 'delete',
'authenticity_token': なにかのtoken
}
またtokenかよ。
htmlを眺めているとheadタグ内に以下のtokenが埋め込まれてた。
<meta name="csrf-token" content="TTAEq+OCmvxSwoj98XrC08g8XtUMOLVc25xzojfu6SqKRCmNeERFgMKMJcD7pILtWvgxwhk2bueQo1ATouh/UA==">
これかな?
とりあえず手動でログアウトしてみてその際にauthenticity_tokenに入っている
tokenと照らし合わせてみると一致していた。
いや〜頼りになるなぁ〜Chromeデべロッパーツール。
なので、こいつをBeautifulSoupで取得してpostしてみる。
# metaに埋め込まれているtokenを取得
csrf_token = bs.find(attrs={'name': 'csrf-token'}).get('content')
# ログアウト時に送られるデータ
logout_data = {
'_method': 'delete',
'authenticity_token': csrf_token # 取得したtokenをセット
}
logout = session.post(logout url, data=logout_data)
time.sleep(2)
print(logout.text)
おお!ログアウト出来た!
cookieはいらない様です。
全ソース
import requests
import time
from bs4 import BeautifulSoup
# セッションの作成
session = requests.session()
response = session.get(url) # ログインしたいurlを引数に入れる
# token取得用にBeautifulSoupを作成
# ちなみにパーサは'html.parser'より'lxml'の方が高速らしいが別途installが必要
bs = BeautifulSoup(response.text, 'html.parser')
# ログイン時にpostするデータを定義
# ボタン要素は無くても問題ない
login_data = {
'UTF-8': '✓',
'session[email]': メールアドレス,
'session[password]': パスワード,
}
# tokenを取得
authenticity_token = bs.find(attrs={'name':'authenticity_token'}).get('value')
# 取得したtokenをログイン時にpostするデータに追加する
login_data['authenticity_token'] = authenticity_token
# cookieの取得
response_cookie = response.cookies
# ログインurl, 送信するデータ、cookieをセットしてpostする
login = session.post(login url, data=login_data, cookies=response_cookie)
time.sleep(2)
# ログイン後のソースを取得
print(login.text)
# ログアウト処理
# ログアウト時に必要なtokenを取得
csrf_token = bs.find(attrs={'name': 'csrf-token'}).get('content')
# ログアウト時にpostするデータを定義
logout_data = {
'_method': 'delete',
'authenticity_token': csrf_token
}
# ログアウトurl, 送信するデータをセットしてpostする
logout = session.post(logout url, data=logout_data)
time.sleep(2)
# ログアウト後のソースを取得
print(logout.text)
そんな訳で初投稿 & 汚いソースではありますが、
requests + (BeautifulSoup)でもログイン、ログアウトが実現できる様です。
ですが正直なところ、セキュアなサイトだと出来ないかもしれませんね。