引言
前面在Locust学习笔记3——模拟登录案例(非加密) 讲了登录接口,但是这个登录的接口并不需要动态参数以及加密签名等校验,而且也没有继续讲登录后的操作。现在这篇文章主要讲上下接口关联的场景,比如接口A返回的数据,作为接口B的请求参数。
之前讲的登录接口是Vue的接口,现在我以学信网为例:https://account.chsi.com.cn/passport/login,讲一下参数关联的场景。
获取参数
使用抓包工具Fiddler,我们登录学信网后,可以看到这样的请求参数信息:
可以看出,每次登录成功,请求参数中都会有lt参数和execution参数,并且每次都不一样,
lt 参数是每次打开浏览器,访问登录首页时服务端会返回一个新的数据
execution 参数是表示网站刷新次数,可以刷新下再登录,就变成其他了
也就是这两个参数是动态的,那么这两个参数是从哪里获取呢?
我们退出学信网,直接请求登录页面(get请求:https://account.chsi.com.cn/passport/login),查看返回的html内容:
获取动参代码:
利用lxml爬虫框架获取html页面中的属性对应的值
import requests from lxml import etree import urllib3 urllib3.disable_warnings() s = requests.session() def get_execution_lt(): """ 获取登录的动态参数 """ url = 'https://account.chsi.com.cn/passport/login' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"} s.headers.update(headers) r = s.get(url=url,verify=False) dom = etree.HTML(r.content.decode('utf-8')) # 返回的是list对象 result = {} try: # print(dom.xpath('//*[@id="fm1"]/input[2]')[0].get('value')) result['lt'] = dom.xpath('//input[@name="lt"]')[0].get('value') result['execution'] = dom.xpath('//input[@name="execution"]')[0].get('value') print(result) except: print("动态参数lt、execution获取失败!") return result if __name__ == '__main__': login_setup = get_execution_lt()
运行结果:
{'lt': 'LT-124381-CEGJ5JiOi1gm1qYeOXsxPcUiYMRDTi-cas', 'execution': 'e3493856-74e4-4108-89c5-527389da290d_ZXlKaGJHY2lPaUpJVXpVeE1pSjkuYUhCU2ExQjFkV3BHYW10SmVGQkplVFpTU0ZkR1ptaE1UVzl1VG5RdkszRkVTVkJFVDIxWFpXdFhSVWhzVEZaeldUaDZUakU1TVhsaVJUUXJhVGRxTVdsa1JEVk9OSFU1Tkc5NlZYaFVhak15WW5Kd2NHcGtNMnhqUjFnNVMzWndMMkZ5YzA1TlRWUkdaMmMyUTBKeE5rOU1XSFpyYUVkS1JuQnNZbUZaZDJnd0sxRm5WSEU1YjNsblJVTk1NVXhwVDBGSEwwY3dPQzl2UWxrMGVYWkplRzQ1VTJ4NVpYUnpWakpGYTJjNWJHSk5jMFV5T1doSlExZDVOSFJvVEdOaWVUTnlUVFUyVWxGaFoxUjJkVmxuYkROdGFWSktSMVZXY2xCdlZuaFZNUzlJVmxWQlpTODFXVzl0VkRoNVlrVk1OSGx6Y2sxTU1VcEhNemxDYlZWMVRXZFdVa2t3UmsxSU1rUTFkMnRFYVhoRlFXWlNZWFJMTjBwTE56Z3hOV3hzV0ZWRFVtOUljak4xU21KR2QwWTNaRkZoZW1NNVVqY3ZPRk5uUmxONE1tWnJlbUpMVFZveVZHcDBVMHR2T1cxV2JEQnplbU16YkhoNmJ6TnlTRXRpYjI5WE5UbHVkREZJWkdjelVIZENWbVJ1T0VaRE1Wb3pVa3cxWm1GbFUzSlpRMFp4WkZCaFVHRlJNMjVsZG10U2IxUXJSV0ZUT1dwdGNqWXdWVlZyZW5OelNFeEZZVE5KZW5GNldHTnpjR1JGYkZsVmJVaENXRGhRYUhSSWEyWjVTQzg1YTNaSFZHMXlMell2TURVck5FOUlVSFZWWms5MmJXMUxiekZ3Vm1VMFlXSTFXWE5KTjNaMVRXTTNORGRIUTBWSFNGSXlVSFJPZERreVV6bE9XRFV6UTJ4b2FteHBVekpaYVhOa2IyNU9aRzB3V0ZoeFJIRlJVM1p3WkZCSGRqQTFOazlwWjNGS2VXeE5XV0l6YWxOYVdscHdlVGhxUWxZdmNFTm1hblIxYVhGUFpEWkdha1l4UkVOdGIxazJNalJPUXpGVlRGUlFhVkZDU25oNkwzaFNXU3M0U0RKd1JHaGtRWFJKWjBVNVJYVlFOekY0UjBOMVdIWjBTRzlxY0haeGNXaGxUa00yVGxkSFltZDNialUzZFdocFFsbHRkakZwTTJVMGJqSjBZWGhOWWs1VWN5OXllSEl3TVVOWFNDOWpNSFJvVmtJMk5XUktjRGRIYWpGRFYwSjNWRXQxVWt0MWVtdHFTakJJV1dKcldFcHNibkpHUldzNWIwNVdSbVpuZEVkbVFWTTRkWGxJVFVkWFJuQnBUVmhzU0cxbWRVOVFkVGhGWlRReWNtOU5iemd4TVdaMGFHNWFUSGhaY2tzemF6TktkMDB3VFV3clpXZFNPVkpKUlhKcE5WWTVVbTVDUjNaUU5FUTJRMFJUWlZsVlprNXVNbUpUZUZaWFNXbGhNMGh6YTI1SmNrZG5Va2huY0dKQmJXcERMMjFXZEVsdGREWXhlRlZHVjBabGFXbHdORVpCVDBGNU1EZHZiamxHTmxvMFpIVnZjbGRvVVcxRU4yWkljMGgyTTFSVFpGcG5Ra1ZDZVhSaVFUSmxaR05TYUV3MWFraElTMmc0ZEZaS1VIcE5hVzFzT1VOcWJsSlhWalJOYlRObWJqWk9VbWxYZDJ4SWMzUk5hRmcxTVM5NUwzWnNiREZqUkZsQ0wySktRaTh3ZFdGRGNFcFRNalZHYTA1U1drbGlSVGR5VEVKRFZraHlWMG8zZERacmJIcFNjUzlpZUdaV2RsSmlTSHBWWWt3dlFrUmtXVWR5THk5T1ZVZDROems1Y21SbVJrcGFUMUF5TDNKSmVHWnRZM1Y2TWswMVNHRTVUMHB1YkRkaVNIcDFVVTFYU25ac1pXTkljakpOWVV0eVoyY3dSRVV5V2k5Q2FVNXBjSE4wWldjNVlrb3ZiRGxzZUhKNE9DOXVUeXM0WTBWSVlsaGFjVlJOYkN0TmNIb3JObWxDVUVGSmFpdHZUM2h5VUM5M1IweFJTbElyT0N0Qk1IQnNRWFF3WVhOdVExWk9kelZhVDJSRFdqTlpjblZJVTJsUmIycDVkbFJTYjNnM09VdFNSRVoxVkRsRU9XRmxjamxNYWtwV1dXVlFjVEJuYXpWMVRVSjFRMHBNVlRKa1EzRkZaRlJrUlhSbVJTdDRRbG8yVG5sS1luUkhZMlJDUlVFeGVESjZNMVJHTkVzelUxTldVVTFQYjA0eWVVSllVVzF2UVhWMVlUZGFRWGhLYVVGb1JXNWFUR1J3VDI1YWVHeDNSazVCY3pKUlZuRkVjemx2TW5FcmRuRk9TWEF5Wm14cGRFTlZOR3hzYkZkSkwzRktTRTVuWlVSVVZrOVROeTlwU1VzcmFWVlBVbVZHZFhaamRXSXhUR1JIYTFnME1EQm9XV2MwU0hab1VtMUJZMjlJVDJkNlpWcGxZa3RzUVNzMGJXcENiMFJuTUZSSlZuSnljRFkyYzFwRFpWRk9ZVlZqY1c1TGMyUm9ZVko0YlZaVlFVa3lLekJSTDB0UVIwcExWekZhVjB4cmIzcFRiMnh5UzBOQ2VrcHphVmRMWkV3d1kxTm1WbVYyVDJGMlQyVlJWVFpQUzNOTlprTlhiV3h0Y0RkeWJ6TlNkV1ZDVmtwV00xVjFUelZHTkZsRmRWWlRlRXB5Y1c0MloybFZieXRNUVN0bmNsaEZNVWRSYkZaa1UwcDNRa2xMTXpJeVpERmtSa2RKVjJkSlJVTlZPRTB5TjFjMmVVd3dOakJzYW1RNU9ESk9kR3czT1ZKNVEzb3lTVTV3YTJkcmF6aHRZMWRyYldaTlVYUmhRWFJNYmsxSksxQkNTbk4xTjNFNFpVaFRVMVYyTDBwUWNWcHpkRkpIYW5WbVVYTkpSSGQ0YlRZdlJXcE5Za1o0T1RONVNHeDFiMWRMWTI1VmVHdHVSVE5QZVRWUFpXZFRTWFo2ZW0xWlQxcGFaRmQxVTI1dVMxWlpWbXQzWTFjeFJYWlpSRGh2VlV0VWJuTnZZMVp2ZVVzM1FVVktja1phY1RWTVUxRjVibFpUTUU1Wk9XWXdkRlZVYjIxM1N6bG1LMFp2TVZOYVQwSjJjMk15UnpaWE5XMTFibEp3WkhObFYwdFlZM0oxSzBWck5IQTBhMGgyZFhoMFVtNUpURlZqY3pNMFIyY3ZhVGh1ZW5wWWFFTkdiSE5yTUZGS2QwNWtXWHBMVWtoVk1HRnNha3gzT0VOalNUUTBjVzh3Y2poa1dVWTBTM0EwYlM4Mk0yWkZaallyTXpWVmQxRnNWWFpwZHpOb1ltbENiakUzTUZSWVUxcGllRWhqWlVreVNUZHllSE5ZTTBVMFdWcFFaVUZ3T1ZkRlZsWkRMMk5XUTJwVlVXMWhZVUV6VERGaFJsZ3JNRE15VG1aeWNuQmtNbEZpT0RCbWNHRmFWV2cyUzJKTmVFWjBUR0ZhTVhKa2MxYzNTREZCT0VKRmNUQm9NMVJWVTNVM2VuWnZZeXN4VFhCMGNYRjFhVVJQZFRCWU5UaDRXVkl2Y1ROdVFVMUhlVGRUYUVnd1dWbFJUbVZaTm1GVFowRjNVMjlXVDJ0Q1pqZFhTRkEzU3psUVVsTjRjRXR6WjBvMFdTdEpjbmxMWlUxUVVWQTBZMVpGWmk4dk1ITk1hME4wTldsRlBRLktsZmpWWWw0SHJxemVuUkpZd1BRblpVeDFfZERTbDhLb2duaHlDVXdfd1NyQXZZMHF0ZXhfWnFIUkE4bG1pelp5djNDN1F1NW9xWFZISXhkemYtVzFn'} Process finished with exit code 0
备注:上面可以看出,我使用了requests.session()方法,作用是创建一个session会话,当你使用这个session登录之后,再次使用session请求系统其他页面的时候,默认使用session会话之前使用的cookie参数。例如JSESSIONID
requests.session()会自动保存,这样就实现cookie关联,get_execution_lt()这个函数其实已经获取到了cookies参数。
登录源码
import requests from lxml import etree import urllib3 urllib3.disable_warnings() import json s = requests.session() def get_execution_lt(): """ 获取登录的动态参数 """ url = 'https://account.chsi.com.cn/passport/login' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"} s.headers.update(headers) r = s.get(url=url,verify=False) dom = etree.HTML(r.content.decode('utf-8')) # 返回的是list对象 result = {} try: # print(dom.xpath('//*[@id="fm1"]/input[2]')[0].get('value')) result['lt'] = dom.xpath('//input[@name="lt"]')[0].get('value') result['execution'] = dom.xpath('//input[@name="execution"]')[0].get('value') print(json.dumps(result,sort_keys=True,indent=10)) except: print("动态参数lt、execution获取失败!") return result # print(login_setup,111111) def login_action(): # 登录 url2 = 'https://account.chsi.com.cn/passport/login' body = { "username":'15099925895', "password":'a8413809', "lt":login_setup['lt'], "execution":login_setup['execution'], "_eventId":'submit' } h2 = { "Content-Type": "application/x-www-form-urlencoded" } s.headers.update(h2) # import json,urllib.parse # data_dic = eval(body) # data_urlencode = urllib.parse.urlencode(data_dic).encode(encoding='UTF-8') r2 = s.post(url=url2,data=body) try: assert '退出' in r2.text print("登录成功") except: print("登录失败") def get_account(): # 登录后请求 r3 = s.get(url='https://account.chsi.com.cn/account/account!show') try: assert '查看个人信息_学信网' in r3.text print("查询成功") except: print("查询失败") if __name__ == '__main__': login_setup = get_execution_lt() login_step = login_action() check_account = get_account()
运行结果:
Locust参数关联源码
由于公司项目的网站涉及公司保密协议,暂时用学信网来写这个案例,具体如下代码:
__author__ = 'Leo' from locust import HttpLocust,TaskSet,task,between from lxml import etree import json import urllib3 urllib3.disable_warnings() class LoginTest(TaskSet): """ 定义用户行为类 """ def get_execution_lt(self): """ 获取登录的动态参数 """ url = '/passport/login' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"} self.client.headers.update(headers) r = self.client.get(url=url,verify=False) dom = etree.HTML(r.content.decode('utf-8')) # 返回的是list对象 result = {} try: # print(dom.xpath('//*[@id="fm1"]/input[2]')[0].get('value')) result['lt'] = dom.xpath('//input[@name="lt"]')[0].get('value') result['execution'] = dom.xpath('//input[@name="execution"]')[0].get('value') print(json.dumps(result,sort_keys=True,indent=10)) except: print("动态参数lt、execution获取失败!") return result def login_action(self,account,password): # 登录 login_setup = self.get_execution_lt() url2 = '/passport/login' body = { "username":account, "password":password, "lt":login_setup['lt'], "execution":login_setup['execution'], "_eventId":'submit' } h2 = { "Content-Type": "application/x-www-form-urlencoded" } self.client.headers.update(h2) # import json,urllib.parse # data_dic = eval(body) # data_urlencode = urllib.parse.urlencode(data_dic).encode(encoding='UTF-8') r2 = self.client.post(url=url2,data=body,verify=False) try: assert '退出' in r2.text print("登录成功") except: print("登录失败") def on_start(self): account = '15099925895' password = 'a8413809' self.login_action(account,password) @task(1) def get_account(self): # 登录后请求 r3 = self.client.get(url='https://account.chsi.com.cn/account/account!show') try: assert '查看个人信息_学信网' in r3.text print("查询成功") except: print("查询失败") class WebSiteUser(HttpLocust): task_set = LoginTest host = "https://account.chsi.com.cn" wait_time = between(3,6) if __name__ == '__main__': import os os.system('locust -f locust_xuexin.py')
运行结果:
界面:
总结
到此,以登录后请求其他接口的案例已经讲完,在实际项目中可以根据这个思路来压测需要登录后的接口。有兴趣的朋友可以加入测开交流群进行沟通与学习,一起成长!