分析登陆页面
1. 使用 Chrome 无痕模式访问去哪儿网登陆网页
https://user.qunar.com/passport/login.jsp
直接使用 Chrome 开发者工具分析,发现cookie 返回 QN1
2. 访问去哪儿网发现登陆需要验证码,所以分析下验证码接口
,发现验证码的URL是固定的
https://user.qunar.com/captcha/api/image?k={en7mni(z&p=ucenter_login&c=ef7d278eca6d25aa6aec7272d57f0a9a
而且发现访问验证码后,返回的 cookie 有所改变。返回QN1、QN25
3. 尝试错误登陆,分析登陆接口
发现请求的网址为
https://user.qunar.com/passport/loginx.jsp
查看请求页面的 from data 数据,以及传入的 cookie 数据 QN1、QN25、QN271、_i、_vi、_fid
直接使用验证码返回的 cookie 去访问请求页面,发现不能登录成功,可能有:
- cookie 不完整;
- 除了 cookie 还有其他 request headers 校验;
尝试分析剩下的 cookie数据 QN271、_i、_vi、_fid的来源
查找QN271
直接在Chrome 开发者工具 全局搜索 Ctrl+Shift+F,搜索 QN271 的值
发现QN271是在JS里面的,而且第三条搜索结果的URL很干净
https://rmcsdf.qunar.com/js/df.js?org_id=ucenter.login&js_type=0
可以看下里面的构造(数据太长,未截取关键部位)
点左下角的Pretty print 格式化js文件,发现 QN271存在里面,可以直接使用 正则 获取其值
# secScript.setAttribute("src", "https://rmcsdf.qunar.com/js/device.js?orgId=ucenter.login&sessionId=a7a856be-c704-4aa4-89e6-76f14da844a5&auto=false");
sessionId = re.findall(r'sessionId=(.*?)&',response.text)[0]
查找_i 和 _vi
用上面同样的方法进行查找,并未能找到 _i 的值
一般来说 _i 这个值是服务器返回是,那么可以尝试搜索 set-cookie-name:_i(一般来说应先尝试搜索这个)
发现有返回 _i 和 _vi 的过程
而URL是:
https://user.qunar.com/passport/addICK.jsp?ssl
查找 fid
用 set-cookie-name:的方法尝试查找 fid 的值
很幸运能找到该cookie 值
但是URL却是:
https://rmcsdf.qunar.com/api/device/challenge.json?callback=callback_1531563394135&sessionId=a7a856be-c704-4aa4-89e6-76f14da844a5&domain=qunar.com&orgId=ucenter.login
callback: callback_1531563394135
sessionId: a7a856be-c704-4aa4-89e6-76f14da844a5
domain: qunar.com
orgId: ucenter.login
需要分析该URL的组成
callback 一般是固定值,请求返回后调用哪个js
sessionId 是非固定值,重新刷新页面,其值会改变
domain 跟 orgId 是固定值
所以难点是找出 sessionId 的规律
但难得可贵的是,这个sessionId值是跟上面 查找QN271 中的 sessionId 是一致的
所以 cookie 添加思路为:
- 访问登录页面,获得QN1
https://user.qunar.com/passport/login.jsp - 访问验证码页面,获得QN1,QN25
https://user.qunar.com/captcha/api/image?k={en7mni(z&p=ucenter_login&c=ef7d278eca6d25aa6aec7272d57f0a9a - 访问 addICK 页面,获得_i,_vi
https://user.qunar.com/passport/addICK.jsp?ssl - 访问 df.js 页面,获得 sessionId,即获得 QN271(并非直接存储在 session)
https://rmcsdf.qunar.com/js/df.js?org_id=ucenter.login&js_type=0 - 访问由 sessionId 组成的 URL,获得fid,并往 session 会话中添加 QN271 {'QN271':cookie_QN271}
'https://rmcsdf.qunar.com/api/device/challenge.json?callback=callback_1511693290383&'
'sessionId={}&domain=qunar.com&orgId=ucenter.login'.format(cookie_QN271)
技巧:cookie 添加完成后可以尝试再次登录,访问过程中可以 设置断点debug查看 cookie 是否添加完整
代码实现:
import requests
import re
"""
此代码共分为4部分:初始化模块,获取添加cookies模块,验证码模块,登录模块
"""
def start_get_session():
session_ = requests.session() # 得到一个session
return session_
def get_base_cookies(session_):
session_.get('https://user.qunar.com/passport/login.jsp') # 获得cookie参数,原始的QN1
get_image(session_) # 通过调用get_image函数,得到验证码图片,同时获得cookie参数QN1,QN25
session_.get('https://user.qunar.com/passport/addICK.jsp?ssl') # 获得cookie参数_i,_vi
"""
由于获取fid参数的url需要cookie参数SESSIONID得到,所以没办法直接得到fid,需要先得到SESSIONID这个参数,再得到fid参数
"""
# 经过查找发现SESSIONID在这个js文件中,所以先得到它
response = session_.get('https://rmcsdf.qunar.com/js/df.js?org_id=ucenter.login&js_type=0')
# 查找SESSIONID
cookie_SE = re.findall(r'&sessionId=(.*?)&', response.text)[0] # 通过正则得到SESSIONID
# 获取fid
session_.get('https://rmcsdf.qunar.com/api/device/challenge.json?callback=callback_1511693290383&'
'sessionId={}&domain=qunar.com&orgId=ucenter.login'.format(cookie_SE))
session_.cookies.update({'QN271': cookie_SE}) # 通过比对发现参数QN271和SESSIONID相同,所以直接加入cookies中
def get_image(session_):
response = session_.get(
'https://user.qunar.com/captcha/api/image?k={en7mni(z&'
'p=ucenter_login&c=ef7d278eca6d25aa6aec7272d57f0a9a') # 获得二维码的response
with open('./img/code.png', 'wb') as f:
f.write(response.content) # 把二维码存进同级img文件夹下命名为code
def login(session_, username, password, vcode):
data ={
'loginType': 0,
'username': username,
'password': password,
'remember': 1,
'vcode': vcode,
}
url = 'https://user.qunar.com/passport/loginx.jsp'
response = session_.post(url , data) # 通过post请求方式,模拟登录
print(response.text)
if __name__ == '__main__': # 主程序,程序入口
session = start_get_session() # 实例化一个session
get_base_cookies(session) # 调用函数,在session中添加cookie
username = input('请输入账号:')
password = input('请输入密码:')
vcode = input('请输入验证码:')
login(session, username, password, vcode) # 调用登录函数
改进:
账户密码可以添加到环境变量中,从环境变量中获取,或者放到.env文件,然后读取;
验证码可以考虑OCR识别或者打码