問題情景
混淆群內的小夥伴遇到這麼個問題,Mailivery 這個網站登入後,明明提交的表單(郵箱和密碼也正確)、請求頭等等都沒問題,為啥一直重定向到登入頁面呢?唉,該出手時就出手啊,我也看看咋回事吧!
- url: https://app.mailivery.io/login
登入引數分析
顯而易見,需要:郵箱(有郵箱校驗)、密碼
開啟開發者工具,隨意輸入郵箱和密碼登入,檢視登入介面的請求和方法:
請求網址:https://app.mailivery.io/login
請求方法:POST
狀態程式碼:302 Found
302?!重定向嗎?在登入不成功後重定向到了登入網站,要求重新登入。目測前後端不分離專案(阿巴阿巴),如果登入成功,肯定會攜帶登入成功時設定的 Cookie 重定向到主頁或者相關主皮膚。
檢視一下登入介面提交頭:
POST /login HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 341
Content-Type: application/x-www-form-urlencoded
Cookie: XSRF-TOKEN=省略; mailivery_session=省略; ......
DNT: 1
Host: app.mailivery.io
Origin: https://app.mailivery.io
Pragma: no-cache
Referer: https://app.mailivery.io/login
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
沒有啥加密引數,emm……,很簡單,的確是看著很簡單,注意 Content-Type 是 application/x-www-form-urlencoded
。
再看看提交的引數:
_token=lCsu2Ruuw33uHlHkRlKwZG3C2tw7TQBjUoTo1yjz&paid_user_Khe2xqZLA4Tkq3py=&submitted_in_seconds=eyJpdiI6IjJ4OXdFK3ZPZklKUnNadXVtRTk0L3c9PSIsInZhbHVlIjoiNHg5NzJmUjM3UnlDOU1tanlnUHpWdz09IiwibWFjIjoiMDYxYmRkODU0YmY1ZjY0MDk4OWMzNmM5YWU5MjNmZDM4NTg5NzQ1MmM3MzBjNzQ3YjYxNTg0MjliYjFjYzM3OCIsInRhZyI6IiJ9&email=xxx%40foxmail.com&password=123
格式化看一下:
_token: lCsu2Ruuw33uHlHkRlKwZG3C2tw7TQBjUoTo1yjz
paid_user_Khe2xqZLA4Tkq3py:
submitted_in_seconds: eyJpdiI6IjJ4OXdFK3ZPZklKUnNadXVtRTk0L3c9PSIsInZhbHVlIjoiNHg5NzJmUjM3UnlDOU1tanlnUHpWdz09IiwibWFjIjoiMDYxYmRkODU0YmY1ZjY0MDk4OWMzNmM5YWU5MjNmZDM4NTg5NzQ1MmM3MzBjNzQ3YjYxNTg0MjliYjFjYzM3OCIsInRhZyI6IiJ9
email: xxx%40foxmail.com
password: 123
表單提交嘛,多多少少也是帶點驗證引數,比如這裡的:_token
、paid_user_Khe2xqZLA4Tkq3py
、submitted_in_seconds
。
經過多次登入提交,發現 _token
與 submitted_in_seconds
的值是變動的,而 paid_user_Khe2xqZLA4Tkq3py
變動的是 paid_user_
之後的部分。並且這些引數與視窗的 Cookie
相關,如果不一致,將發生 416 錯誤,提示頁面過期。看來這就是個反爬點了,還記得我說的嗎,這個網站是前後端不分離,那麼這些引數肯定也隱藏於表單,在元素中搜尋特色濃重的 submitted_in_seconds
。
非常的好,可以看到我們這三個引數的來源了,捋一捋:
訪問 login 網站(GET) 得到三個引數,此時響應頭所設定的 Cookie 無登入效力
對 login 網站(POST) 攜帶三個引數,根據結果重定向:
如果登入失敗,此時響應頭所設定的 Cookie 也是無登入效力的,攜帶 Cookie 要求咱們繼續登入
如果登入成功,此時響應頭所設定的 Cookie 就是具備登入效力的,攜帶 Cookie 直接上主頁
接下來在 Python 中實現一下,建立 login.py
與 dashboard.py
login.py 實現登入的介面:
import requests
url = "https://app.mailivery.io/login"
_token = ''
paid_user = ''
submitted_in_seconds = ''
email = 'xxx%40163.com'
password = 'xxxxxxx'
payload=f'_token={_token}&{paid_user}=&submitted_in_seconds={submitted_in_seconds}&email={email}&password={password}'
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Host': 'app.mailivery.io',
'Origin': 'https://app.mailivery.io',
'Referer': 'https://app.mailivery.io/login',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Cookie': 'XSRF-TOKEN=eyJpdiI6Ikk1WjFjMFRsdVhKdzBuMjBWamNVVEE9PSIsInZhbHVlIjoiblAzS0ZBOEUyK1lXWGV0ZXhuT1Y5MldLc290d2ZzL0E3dTQxT3BDMmNGR3d6aG0vamhUekozeVFCUVVVczRJSWxxQTM1ZGpvOU5KTm11bFp4NEsvWGlObUJ6V1A3WWc1WFJXcUlPYWYzYTgrSGNMZ2VtM0s1R0tGUlJ4Z0ZMSy8iLCJtYWMiOiI2NGUxYmNhMjEwMmE3ZDNmOTc4OTcxMWVlZGY3ODIyNDZhODBiYzUxZjVhMWE2YWZkMWVhOGM2YjA4MmQzYmY0IiwidGFnIjoiIn0%3D; mailivery_session=eyJpdiI6IjVwL1QwUHNhMTlTdGUyZ0ozUzY3aGc9PSIsInZhbHVlIjoiYlNuSXI2d2tKWjMrYjFpVmx1Ym5uTEVOUGhXZjFKRGhVV1VMeXRHQ1BpRWFsV0ZnYVFkNDd4Vm9wdXY1ZElqVWpVL2xhSytNdnBDYS9NNHRBWmNzRDF4ZjJtWFhPTHFJRFBLVnNYSmFPMW9HSkEweVVpQTZZVjhJU2k5WSswR0oiLCJtYWMiOiJkYzg0ZmY2ODEwZmEyMzczNzU5NGU4YzMwYjA2MDRlZTc0ZWJiNDc4ZDBhMDU4OTgyM2E3NDMzZDM3NmRmNTcxIiwidGFnIjoiIn0%3D',
'Content-Type': 'application/x-www-form-urlencoded',
'Connection': 'keep-alive'
}
response = requests.request("POST", url, headers=headers, data=payload, allow_redirects=False, proxies={
'http': None,
'https': None
})
print(response.headers)
print(response.status_code)
注意,我說過,Cookie 和三個引數是有緊密聯絡的,上述 Cookie 是透過對登入頁面 GET 時得到的,三個引數同理。
三個引數的值我已省略,特別特別要注意的是:請求時,allow_redirects
這個值最好設定為 False,因為重定向底層和瀏覽器不同,如果為 True,你會發現你的 Cookie 一直都是失效的。因為產生了如下的重定向過程:
郵箱密碼正確的情況:
login -> dashboard -> login
郵箱密碼錯誤的情況:
login -> login
引數未校驗的情況:
login -> 頁面過期
為什麼最終又到 login 了???
我猜測可能是反爬點,動態載入未載入好,太快了,Cookie 或者三個引數在後端都還是未準備好的狀況。
我們只需要取得跳轉到 dashboard 前響應頭中的 Set-Cookie 的值,也就是 XSRF-TOKEN
與 mailivery_session
。
因此我們停止使用 requests 的重定向。
接下來看一下 dashboard.py 這個檔案,主要測試一下取得 Cookie 是否有效:
import requests
url = "https://app.mailivery.io/campaign/dashboard"
payload={}
xsrf_token = 'xxx'
mailivery_session = 'xxx'
headers = {
'Cookie': f'XSRF-TOKEN={xsrf_token}; mailivery_session={mailivery_session}',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
'Accept': '*/*',
'Host': 'app.mailivery.io',
'Connection': 'keep-alive'
}
response = requests.request("GET", url, headers=headers, data=payload, proxies={
'http': None,
'https': None
})
print(response.text)
全套模擬
我們最終的效果是:自動獲取三個引數和初始的 Cookie,然後根據郵箱和密碼登入,再攜帶 Cookie 訪問 Dashboard。
-
自動獲取這個很簡單,透過 lxml 中的 etree 解析,Cookie 透過 requests 響應頭得到。
-
然後去登入,也很簡單,攜帶好引數和上一級得到的 Cookie,如果登入密碼這些沒錯,將得到有效的 Cookie。
-
最後攜帶上一級得到的有效 Cookie 進行登入。
如果說你覺得到此結束了,那很抱歉,你再進行上述的實現後,將發現 Cookie 無效!!!
略帶一筆,自動獲取的表單引數和初始 Cookie 如果有問題,請求時將響應 416。如果沒問題,你在取到後立馬進行登入,請求將響應 200。200 就一定是好事嗎?我已經說過了,要重定向到 dashboard 頁面,那麼應該是 302 才對!所以我們在取引數到登入這個請求中進行延時,等待後端緩過神,比如 3 秒?4秒?都是可以的。或者你根據響應狀態碼增加重試機制,嘗試到出現 302 的情況。
完整程式碼就不提供啦,自行琢磨吧~