python+requests+pytest+allure自动化框架

1.核心库

requests request请求

openpyxl excel文件操作

loggin 日志

smtplib 发送邮件

configparser

unittest.mock mock服务

2.目录结构

base

utils

testDatas

conf

testCases

testReport

logs

其他

图片

2.1base

base_path.py 存放绝对路径,dos命令或Jenkins执行时,防止报错

base_requests.py 封装requests,根据method选择不同的方法执行脚本,同时处理请求异常

2.1.1 base_path.py

import os# 项目根路径
_root_path = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0]# 报告路径
report_path = os.path.join(_root_path, 'testReport', 'report.html')# 日志路径
log_path = os.path.join(_root_path, 'logs/')# 配置文件路径
conf_path = os.path.join(_root_path, 'conf', 'auto_test.conf')# 测试数据路径
testdatas_path = os.path.join(_root_path, 'testDatas')# allure 相关配置
_result_path = os.path.join(_root_path, 'testReport', 'result')
_allure_html_path = os.path.join(_root_path, 'testReport', 'allure_html')
allure_command = 'allure generate {} -o {} --clean'.format(_result_path, _allure_html_path)

2.1.2 base_requests.py

import json
import allure
import urllib3
import requests
import warnings
from bs4 import BeautifulSoup
from base.base_path import *
from requests.adapters import HTTPAdapter
from utils.handle_logger import logger
from utils.handle_config import handle_config as hcclass BaseRequests:def __init__(self, case, proxies=None, headers=None, cookies=None, timeout=15, max_retries=3):''':param case: 测试用例:param proxies: The result is displayed in fiddler:{"http": "http://127.0.0.1:8888", "https": "https://127.0.0.1:8888"}:param headers: 请求头:param cookies: cookies:param timeout: 请求默认超时时间15s:param max_retries: 请求超时后默认重试3次'''self.case = caseself.proxies = proxiesself.headers = headersself.cookies = cookiesself.timeout = timeoutself.max_retries = max_retriesself.base_url = hc.operation_config(conf_path, 'BASEURL', 'base_url')def get_response(self):'''获取请求结果'''response = self._run_main()return responsedef _run_main(self):'''发送请求'''method = self.case['method']url = self.base_url + self.case['url']if self.case['parameter']:data = eval(self.case['parameter'])else:data = Nones = requests.session()s.mount('http://', HTTPAdapter(max_retries=self.max_retries))s.mount('https://', HTTPAdapter(max_retries=self.max_retries))urllib3.disable_warnings()  # 忽略浏览器认证(https认证)警告warnings.simplefilter('ignore', ResourceWarning)    # 忽略 ResourceWarning警告res=''if method.upper() == 'POST':try:res = s.request(method='post', url=url, data=data, verify=False, proxies=self.proxies, headers=self.headers, cookies=self.cookies, timeout=self.timeout)except Exception as e:logger.error('POST请求出错,错误信息为:{0}'.format(e))elif method.upper() == 'GET':try:res = s.request(method='get', url=url, params=data, verify=False,proxies=self.proxies, headers=self.headers, cookies=self.cookies, timeout=self.timeout)except Exception as e:logger.error('GET请求出错,错误信息为:{0}'.format(e))else:raise ValueError('method方法为get和post')logger.info(f'请求方法:{method},请求路径:{url}, 请求参数:{data}, 请求头:{self.headers}, cookies:{self.cookies}')# with allure.step('接口请求信息:'):#     allure.attach(f'请求方法:{method},请求路径:{url}, 请求参数:{data}, 请求头:{headers}')# 拓展:是否需要做全量契约验证?响应结果是不同类型时,如何处理响应?return resif __name__ == '__main__':# case = {'method': 'get', 'url': '/article/top/json', 'parameter': ''}case = {'method': 'post', 'url': '/user/login', 'parameter': '{"username": "xbc", "password": "123456"}'}response = BaseRequests(case).get_response()print(response.json())

2.2 utils

(只取核心部分)

handle_excel.py

excel的操作,框架要求,最终读取的数据需要保存列表嵌套字典的格式[{},{}]
其他操作
handle_sendEmail.py

python发送邮件使用smtp协议,接收邮件使用pop3
需要开启pop3服务功能,这里的password为授权码,启用服务自行百度
handle_logger.py 日志处理

handle_config.py
配置文件处理,这里只将域名可配置化,切换环境时改域名即可

handle_allure.py
allure生成的报告需要调用命令行再打开,这里直接封装命令

handle_cookies.py(略)
在git中补充,处理cookiesJar对象

handle_mock.py(略)
在git中补充,框架未使用到,但是也封装成了方法

param_replace(略)

将常用的参数化操作封装成类

2.2.1 handle_excel.py

import openpyxl
from base.base_path import *class HandleExcel:def __init__(self, file_name=None, sheet_name=None):'''没有传路径时,默认使用 wanadriod接口测试用例.xlsx 文件:param file_name:  用例文件:param sheet_name: 表单名'''if file_name:self.file_path = os.path.join(testdatas_path, file_name)self.sheet_name = sheet_nameelse:self.file_path = os.path.join(testdatas_path, 'wanadriod接口测试用例.xlsx')self.sheet_name = 'case'# 创建工作簿,定位表单self.wb = openpyxl.load_workbook(self.file_path)self.sheet = self.wb[self.sheet_name]# 列总数,行总数self.ncols = self.sheet.max_columnself.nrows = self.sheet.max_rowdef cell_value(self, row=1, column=1):'''获取表中数据,默认取出第一行第一列的值'''return self.sheet.cell(row, column).valuedef _get_title(self):'''私有函数, 返回表头列表'''title = []for column in range(1, self.ncols+1):title.append(self.cell_value(1, column))return titledef get_excel_data(self):''':return: 返回字典套列表的方式 [{title_url:value1, title_method:value1}, {title_url:value2, title_method:value2}...]'''finally_data = []for row in range(2, self.nrows+1):result_dict = {}for column in range(1, self.ncols+1):result_dict[self._get_title()[column-1]] = self.cell_value(row, column)finally_data.append(result_dict)return finally_datadef get_pytestParametrizeData(self):'''选用这种参数方式,需要使用数据格式 列表套列表 @pytest.mark.parametrize('', [[], []]), 如 @pytest.mark.parametrize(*get_pytestParametrizeData)将 finally_data 中的 title 取出,以字符串形式保存,每个title用逗号(,)隔开将 finally_data 中的 value 取出,每行数据保存在一个列表,再集合在一个大列表内:return: title, data'''finally_data = self.get_excel_data()data = []title = ''for i in finally_data:value_list = []key_list = []for key, value in i.items():value_list.append(value)key_list.append(key)title = ','.join(key_list)data.append(value_list)return title, datadef rewrite_value(self, new_value, case_id, title):'''写入excel,存储使用过的数据(参数化后的数据)'''row = self.get_row(case_id)column = self.get_column(title)self.sheet.cell(row, column).value = new_valueself.wb.save(self.file_path)def get_row(self, case_id):'''通过执行的 case_id 获取当前的行号'''for row in range(1, self.nrows+1):if self.cell_value(row, 1) == case_id:return int(row)def get_column(self, title):'''通过表头给定字段,获取表头所在列'''for column in range(1, self.ncols+1):if self.cell_value(1, column) == title:return int(column)if __name__ == '__main__':r = HandleExcel()print(r.get_excel_data())

2.2.2 handle_sendEmail.py

import smtplib
from utils.handle_logger import logger
from email.mime.text import MIMEText    # 专门发送正文邮件
from email.mime.multipart import MIMEMultipart  # 发送正文、附件等
from email.mime.application import MIMEApplication  # 发送附件class HandleSendEmail:def __init__(self, part_text, attachment_list, password, user_list, subject='interface_autoTestReport', smtp_server='smtp.163.com', from_user='hu_chunpu@163.com', filename='unit_test_report.html'):''':param part_text: 正文:param attachment_list: 附件列表:param password: 邮箱服务器第三方密码:param user_list: 收件人列表:param subject: 主题:param smtp_server: 邮箱服务器:param from_user: 发件人:param filename: 附件名称'''self.subject = subjectself.attachment_list = attachment_listself.password = passwordself.user_list = ';'.join(user_list)    # 多个收件人self.part_text = part_textself.smtp_server = smtp_serverself.from_user = from_userself.filename = filenamedef _part(self):'''构建邮件内容'''# 1) 构造邮件集合体:msg = MIMEMultipart()msg['Subject'] = self.subjectmsg['From'] = self.from_usermsg['To'] = self.user_list# 2) 构造邮件正文:text = MIMEText(self.part_text)msg.attach(text)  # 把正文加到邮件体里面# 3) 构造邮件附件:for item in self.attachment_list:with open(item, 'rb+') as file:attachment = MIMEApplication(file.read())# 给附件命名:attachment.add_header('Content-Disposition', 'attachment', filename=item)msg.attach(attachment)# 4) 得到完整的邮件内容:full_text = msg.as_string()return full_textdef send_email(self):'''发送邮件'''# qq邮箱必须加上SSLif self.smtp_server == 'smtp.qq.com':smtp = smtplib.SMTP_SSL(self.smtp_server)else:smtp = smtplib.SMTP(self.smtp_server)# 登录服务器:.login(user=email_address,password=第三方授权码)smtp.login(self.from_user, self.password)logger.info('--------邮件发送中--------')try:logger.info('--------邮件发送成功--------')smtp.sendmail(self.from_user, self.user_list, self._part())except Exception as e:logger.error('发送邮件出错,错误信息为:{0}'.format(e))else:smtp.close()    # 关闭连接if __name__ == '__main__':from base.base_path import *part_text = '附件为自动化测试报告,框架使用了pytest+allure'attachment_list = [report_path]password = ''user_list = ['']HandleSendEmail(part_text, attachment_list, password, user_list).send_email()

2.2.3 handle_logger.py

import sys
import logging
from time import strftime
from base.base_path import *class Logger:def __init__(self):# 日志格式custom_format = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s'# 日期格式date_format = '%a, %d %b %Y %H:%M:%S'self._logger = logging.getLogger()  # 实例化self.filename = '{0}{1}.log'.format(log_path, strftime("%Y-%m-%d")) # 日志文件名self.formatter = logging.Formatter(fmt=custom_format, datefmt=date_format)self._logger.addHandler(self._get_file_handler(self.filename))self._logger.addHandler(self._get_console_handler())self._logger.setLevel(logging.INFO)  # 默认等级def _get_file_handler(self, filename):'''输出到日志文件'''filehandler = logging.FileHandler(filename, encoding="utf-8")filehandler.setFormatter(self.formatter)return filehandlerdef _get_console_handler(self):'''输出到控制台'''console_handler = logging.StreamHandler(sys.stdout)console_handler.setFormatter(self.formatter)return console_handler@propertydef logger(self):return self._logger'''
日志级别:
critical    严重错误,会导致程序退出
error        可控范围内的错误
warning        警告信息
info        提示信息
debug        调试程序时详细输出的记录
'''
# 实例
logger = Logger().loggerif __name__ == '__main__':import datetimelogger.info(u"{}:开始XXX操作".format(datetime.datetime.now()))

2.2.4 handle_config.py

import configparser# 配置文件类
class HandleConfig:def operation_config(self, conf_file, section, option):cf = configparser.ConfigParser()    # 实例化cf.read(conf_file)value = cf.get(section, option)    # 定位return valuehandle_config = HandleConfig()
if __name__ == '__main__':from base.base_path import *base_url = handle_config.operation_config(conf_path, 'BASEURL', 'base_url')print(base_url)

2.2.5 handle_allure.py

import subprocess
from base.base_path import *class HandleAllure(object):def execute_command(self):subprocess.call(allure_command, shell=True)handle_allure = HandleAllure()

2.3testDatas

excel测试用例文件,必须是.xlsx结尾,用例结构如下:

图片

2.4conf

放置配置文件 .conf结尾

2.5 testCases

conftest.py

fixture功能,用例前置后置操作
构造测试数据
其他高级操作
注意邮件中的password和user_list需要换成自己测试的邮箱及服务密码
test_wanAndroid.py 测试用例脚本

参数化: pytest.mark.parametrize(‘case’,[{},{}])
接口关联:
将关联的参数配置成全局变量
在用例执行前使用全局变量替换参数
使用 is_run 参数指明有参数化的用例,并取出,再赋值给全局变量
cookies:
和接口关联的处理方式一样处理cookies
步骤
收集用例
执行用例
断言
构造测试报告
发送邮件

2.5.1 conftest.py

import pytest
from base.base_path import *
from utils.handle_logger import logger
from utils.handle_allure import handle_allure
from utils.handle_sendEmail import HandleSendEmail'''
1. 构造测试数据??
2. fixture 替代 setup,teardown
3. 配置 pytest
'''def pytest_collection_modifyitems(items):"""测试用例收集完成时,将收集到的item的name和nodeid的中文显示在控制台上"""for item in items:item.name = item.name.encode("utf-8").decode("unicode_escape")item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")# print(item.nodeid)@pytest.fixture(scope='session', autouse=True)
def send_email():logger.info('-----session级,执行wanAndroid测试用例-----')yieldlogger.info('-----session级,wanAndroid用例执行结束,发送邮件:-----')"""执行alllure命令 """handle_allure.execute_command()# 发邮件part_text = '附件为自动化测试报告,框架使用了pytest+allure'attachment_list = [report_path]password = ''user_list = ['']HandleSendEmail(part_text, attachment_list, password, user_list).send_email()

2.5.2 test_wanAndroid.py

import json
import pytest
import allure
from base.base_requests import BaseRequests
from utils.handle_logger import logger
from utils.handle_excel import HandleExcel
from utils.param_replace import pr
from utils.handle_cookies import get_cookieshandle_excel = HandleExcel()
get_excel_data = HandleExcel().get_excel_data()
ID = ''
COOKIES = {}
PAGE = ''class TestWanAndroid:@pytest.mark.parametrize('case', get_excel_data)def test_wanAndroid(self, case):global IDglobal COOKIES# 参数替换case['url'] = pr.relevant_parameter(case['url'], '${collect_id}', str(ID))if case['is_run'].lower() == 'yes':logger.info('------执行用例的id为:{0},用例标题为:{1}------'.format(case['case_id'], case['title']))res = BaseRequests(case, cookies=COOKIES).get_response()res_json = res.json()# 获取登录后的cookiesif case['case_id'] == 3:COOKIES = get_cookies.get_cookies(res)if case['is_depend']:try:ID = res_json['data']['id']# 将使用的参数化后的数据写入excelhandle_excel.rewrite_value('id={}'.format(ID), case['case_id'], 'depend_param')except Exception as e:logger.error(f'获取id失败,错误信息为{e}')ID = 0# 制作 allure 报告allure.dynamic.title(case['title'])allure.dynamic.description('<font color="red">请求URL:</font>{}<br />''<font color="red">期望值:</font>{}'.format(case['url'], case['excepted']))allure.dynamic.feature(case['module'])allure.dynamic.story(case['method'])result=''try:assert eval(case['excepted'])['errorCode'] == res_json['errorCode']result = 'pass'except AssertionError as e:logger.error('Assert Error:{0}'.format(e))result = 'fail'raise efinally:# 将实际结果格式化写入excelhandle_excel.rewrite_value(json.dumps(res_json, ensure_ascii=False, indent=2, sort_keys=True), case['case_id'], 'actual')# 将用例执行结果写入excelhandle_excel.rewrite_value(result, case['case_id'], 'test_result')def test_get_articleList(self):'''翻页,将page参数化'''global PAGEpassdef test_mock_demo(self):'''使用mock服务模拟服务器响应'''passif __name__ == '__main__':pytest.main(['-q', 'test_wanAndroid.py'])

2.6 testReport

存放html测试报告,安装插件pip install pytest-html

存放allure测试报告,插件安装pip install allure-pytest

2.7 logs

存放日志文件

2.8 其他文件

run.py 主运行文件

pytest.ini 配置pytest的默认行为,运行规则等

requirements.txt 依赖环境

3.总结

allure有很多有趣的操作,甚至控制用例执行行为,有兴趣可以拓展,也可以看下之前的博客

实现框架的难点在接口依赖

  • 自动生成 pip freeze
  • 安装 pip -r install requirements.txt

接口自动化应避免复杂的接口依赖,复杂的依赖只会造成测试的不可控性

注意频繁的操作excel会消耗性能

有兴趣可以将本框架集合在Jenkins中

本文的demo接口均采用至本站,感谢作者提供的免费接口
https://www.wanandroid.com/

项目git地址:…(git加密了,后续补上))

行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 320231853,里面有各种软件测试+开发资料和技术可以一起交流学习哦。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据仓库的ELT/ETL

ETL 和 ELT 有很多共同点&#xff0c;从本质上讲&#xff0c;每种集成方法都可以将数据从源端抽取到数据仓库中&#xff0c;两者的区别在于数据在哪里进行转换。 01 ETL ETL – 抽取、转换、加载 从不同的数据源抽取信息&#xff0c;将其转换为根据业务定义的格式&#xff0…

【C 数据结构】静态链表

文章目录 【 1. 基本原理 】1.1 静态链表中的节点1.2 备用链表 【 2. 静态链表的创建 】2.1 实例1 - 创建静态链表&#xff0c;指定值2.2 实例2 - 创建静态链表&#xff0c;默认值 【 3. 静态链表 添加元素 】【 4. 静态链表 删除元素 】【 5. 静态链表 查找元素 】【 6. 静态链…

华为ensp中PPPOE (点对点协议)原理和配置命令

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月12日6点30分 PPPoE&#xff08;PPP over Ethernet&#xff09;是一种将PPP协议封装到以太网帧中的链路层协议。它可以使以太网网络中的多台主机连接到远端的宽带接…

每日一题---OJ题: 旋转数组

片头 嗨! 小伙伴们,咱们又见面啦,今天我们一起来学习一道OJ题---旋转数组 emmm,看上去好像没有那么难,我们一起来分析分析 比如: 数组里面有7个元素,分别为 1, 2, 3, 4, 5, 6, 7 , 现在我们将数组中的元素向右轮转3个位置 第一次轮转:将最后一个元素"7"放在第一个…

Centos7 k8s 集群 - Rook Ceph 安装

环境准备 基础环境 系统名称操作系统CPU内存硬盘Kubernete 版本Docker版本IPmasterCentos74c4gsdb 20G1.17.023.0.1192.168.1.128node01Centos74c4gsdb 20G1.17.023.0.1192.168.1.129node02Centos74c4gsdb 20G1.17.023.0.1192.168.1.130node03Centos74c4gsdb 20G1.17.023.0.1…

ssm+vue的实验室课程管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的实验室课程管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

RabbitMQ消息模型之Simple消息模型

simple消息模型 生产者 package com.example.demo02.mq.simple;import com.example.demo02.mq.util.ConnectionUtils; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection;import java.io.IOException;/*** author Allen* 4/10/2024 8:07 PM* versi…

绝地求生:AUG爆裂弹球黑货箱:街机动漫风格大家会喜欢吗?

大好&#xff0c;我闲游盒&#xff01; 4.10更新后&#xff0c;AUG的新成长型也出来了&#xff0c;更新后我觉得AUG变好用了一点&#xff0c;不知道大家有没有感觉出来&#xff1f; 宝箱概率 本期主角 AUG-爆裂弹球&#xff08;紫色配粉红色&#xff09; 本次的AUG我才升到5级…

从 SQLite 3.4.2 迁移到 3.5.0(二十)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite---调试提示&#xff08;十九&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 ​ SQLite 版本 3.5.0 &#xff08;2007-09-04&#xff09; 引入了一个新的操作系统接口层&#xff0c; 与所有先前版本的 SQLi…

通过WebShell登录SQL Server主机并使用SSRS报表服务

背景信息 RDS SQL Server提供了WebShell功能&#xff0c;允许用户通过Web界面登录到RDS SQL Server实例的操作系统中&#xff0c;并在该操作系统中执行命令、上传下载文件等操作。WebShell功能方便用户对RDS SQL Server实例的管理和维护&#xff0c;特别是在无法使用SSH客户端的…

社交网络的未来图景:探索Facebook的发展趋势

随着科技的不断进步和社会的快速变迁&#xff0c;社交网络作为连接人与人之间的重要纽带&#xff0c;扮演着日益重要的角色。而在众多社交网络中&#xff0c;Facebook作为老牌巨头&#xff0c;一直在探索着新的发展路径&#xff0c;引领着社交网络的未来图景。本文将深入探索Fa…

SQLite---调试提示(十九)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite Android 绑定&#xff08;十八&#xff09; 下一篇&#xff1a;从 SQLite 3.4.2 迁移到 3.5.0&#xff08;二十&#xff09; ​ 以下是 SQLite 开发人员跟踪、检查和了解 核心 SQLite 库。 这些技术旨在帮助理解 核…

open c UF_MODL_create_simple_hole 识别放置平面 UF_MODL_ask_face_data

在BLOCK上创建一个简单孔 UF_FEATURE_SIGN sign UF_NULLSIGN;double block_orig[3] { -25.0,-25.0,0.0 };char* block_len[3] { "50","50","30" };tag_t blk_obj;UF_MODL_create_block1(sign, block_orig, block_len, &blk_obj);tag_t bo…

ubuntu如何截图? ubuntu中截屏的三种方法

文章目录 1.ubuntu主要用途2.ubuntu如何截图&#xff1f;2.1 方法一&#xff1a;键盘按键快捷键截屏 2.2 方法二&#xff1a;系统自带软件2.3 方法三&#xff1a;第三方软件 Reference 1.ubuntu主要用途 1、桌面操作系统&#xff1a;Ubuntu可用作个人电脑或笔记本电脑的操作系…

Doris 内网安装部署,基于 CentOS 7

实测 CentOS 7.6 和 7.9都可用&#xff0c;CentOS安装包为&#xff1a;标准安装盘DVD版&#xff0c;如果系统安装的是精简版&#xff0c;需要挂载DVD版或者自行下载依赖。 参考文档 快速开始 - Apache Doris Doris 下载地址&#xff1a;2.1.1 ( Latest ) -> x64 ( avx2 )…

计算两个时间段的差值

计算两个时间段的差值 运行效果&#xff1a; 代码实现&#xff1a; #include<stdio.h>typedef struct {int h; // 时int m; // 分int s; // 秒 }Time;void fun(Time T[2], Time& diff) {int sum_s[2] { 0 }; for (int i 0; i < 1; i) { // 统一为秒数sum_s[…

git 删除本地分支 删除远程仓库中的分支

语法&#xff1a; 删除本地分支 git branch -D <分支名>删除远程分支 git push <remote名称> <分支名> --delete 示例&#xff1a; 删除本地分支 git branch -D feature/test_listview删除远程分支 git push origin feature/test_listview --delete 两个…

【考研数学】《660》+《880》高分搭配方法

&#x1f4dd;《660题》和《880题》高效刷题方法 1️⃣做题要有针对性&#xff0c;不要为了做题而做题 &#x1f4aa;660和880题虽然多&#xff0c;但是你不用全都做完&#xff0c;你可以把它当成是题源&#xff0c;里面的每一道题都很经典&#xff0c;如果搞懂一道&#xff…

Qlik Sense : Crosstable在数据加载脚本中使用交叉表

什么是Crosstable&#xff1f; 交叉表是常见的表格类型&#xff0c;特点是在两个标题数据正交列表之间显示值矩阵。如果要将数据关联到其他数据表格&#xff0c;交叉表通常不是最佳数据格式。 本主题介绍了如何逆透视交叉表&#xff0c;即&#xff0c;在数据加载脚本中使用 L…

WinRAR再爆0 day漏洞,0 day漏洞该如何有效预防

WinRAR再爆0 day漏洞&#xff0c;已被利用超过4个月。 Winrar是一款免费的主流压缩文件解压软件&#xff0c;支持绝大部分压缩文件格式的解压&#xff0c;全球用户量超过5亿。Group-IB研究人员在分析DarkMe恶意软件时发现WinRAR在处理ZIP文件格式时的一个漏洞&#xff0c;漏洞…