【干货】接口自动化框架

总体工程结构

在这里插入图片描述

一、case_datas目录

该目录存放Excel格式的接口用例,文件中包含接口的请求头、请求路径、请求方式、请求体、预期结果等信息
在这里插入图片描述
1、case_id
根据自己项目的要求可以自定义命名

2、title
请求接口的名称

建议根据模块+接口名称来自定义命名;如:“项目文档-验证文件夹A编辑文档A文档编码长度大于200功能”

3、headers
请求接口的请求头,可根据接口的实际情况填写;

一般接口的请求头为:{“Content-Type”: “application/json;charset=UTF-8”}

上传接口的请求头为:{},PS:目前EDC的上传接口请求头为空,若填写请求头接口会报错

4、path
请求接口的URL,可根据各自项目接口的实际情况填写

5、method
请求接口的请求方式,可根据接口的实际情况填写,常见的请求方式有:get、post、delete、put

6、files
(1)若请求接口不需要上传文件,则该项默认填写“{}”

(2)若请求接口需要上传文件,则需要填写该项;填写规则如下:

{"file_name":"文件名称","file_path":"/文件名称","content_type":"文档内容类型"}

说明:

file_name:需要上传文件的名称,包含后缀

file_path:/需要上传文件的名称,包含后缀;注意必须加“/”

content_type:文档内容类型,查看方式如下所示:

上传pdf格式文件:
在这里插入图片描述

上传excel格式文件:
在这里插入图片描述
上传word格式文件:
在这里插入图片描述
7、json
请求接口的请求体数据格式

(1)若请求接口没有请求体,则该项默认要填写“{}”;如:get接口

(2)若请求接口存在请求体,可根据接口文档中的实际情况填写

8、expected
请求接口的预期结果

必须是key-value的格式,可以断言状态码,也可以断言响应结果中的内容;多个预期结果使用英文逗号分隔

9、extractor(参数提取)
处理接口间的依赖关系

(1)获取当前接口响应结果中的参数,可参考:【干货】接口自动化测试Jsonpath的使用

(3)接口间有依赖关系时,下游接口接收上游接口返回的参数作为下游接口的入参,格式为:#参数#,参考下图示例:
在这里插入图片描述

二、common目录

common目录下存放公共方法,主要包括:
1、excel_handler.py:读取/写入excel的方法

import openpyxl     # 使用pip install openpyxl命令安装

class ExcelHandler:
    def __init__(self, fpath):
        self.fpath = fpath

    def read(self, sheet_name):
        """读取数据"""
        # 打开文件
        wb = openpyxl.open(self.fpath)
        # 获取表格
        ws = wb[sheet_name]
        data = list(ws.values)
        # 关闭文件
        wb.close()
        header = data[0]
        all_data = []
        for row in data[1:]:
            row_dict = dict(zip(header, row))
            all_data.append(row_dict)
        return all_data

    def write(self, sheet_name, data, row, column):
        """写入excel数据"""
        wb = openpyxl.load_workbook(self.fpath)
        # 获取表格
        ws = wb[sheet_name]
        ws.cell(row=row, column=column).value = data
        # 通过workbook 保存和关闭
        wb.save(self.fpath)
        wb.close()

2、logger_handler.py:日志记录的方法

import logging
import logging.handlers
import os
import time
from config.path import logs_path     # 日志存放目录


class LoggerUtil(logging.Logger):
    def __init__(self,
                 name='root',
                 logger_level='DEBUG',
                 stream_handler_level='DEBUG',
                 file_handler_level='INFO',
                 fmt_str="[%(asctime)s] [%(levelname)s] [%(filename)s] [line %(lineno)s] %(message)s"):
        # 获取日志收集器 logger
        super().__init__(name, logger_level)
        # self == 收集器
        # 修改log保存位置
        timestamp = time.strftime("%Y-%m-%d", time.localtime())
        logfilename = '%s.log' % timestamp
        logfilepath = os.path.join(logs_path, logfilename)
        # 根据日志文件大小拆分文件;若日志大于1024 * 1024 * 50时,则重新生成一个日志文件名称
        rotatingFileHandler = logging.handlers.RotatingFileHandler(filename=logfilepath,
                                                                   maxBytes=1024 * 1024 * 50,
                                                                   backupCount=5,
                                                                   encoding='utf-8')
        fmt = logging.Formatter(fmt_str)
        # 日志处理器
        handler = logging.StreamHandler()
        handler.setLevel(stream_handler_level)
        self.addHandler(handler)
        handler.setFormatter(fmt)
        rotatingFileHandler.setFormatter(fmt)
        # 文件处理器
        rotatingFileHandler.setLevel(file_handler_level)
        self.addHandler(rotatingFileHandler)

logger = LoggerUtil()

3、yaml_handler.py:操作yaml文件的方法

import yaml     # 使用pip insatll PyYAML命令安装


def read_yaml(fpath):
    with open(fpath, encoding='utf-8') as f:
        data = yaml.load(f, Loader=yaml.SafeLoader)
    return data

4、oracle_handler.py:操作oracle数据库的方法

import cx_Oracle     # 使用pip install cx-Oracle命令安装
from common.logger_handler import logger     # 打印日志


class OracleHandler:
    def __init__(self,
                 host='',
                 port='',
                 user='',
                 password='',
                 database='ORCL',
                 ):
        self.conn = cx_Oracle.connect(user=user,
                                      password=password,
                                      dsn='{}:{}/{}'.format(host, port, database))
        logger.info("连接Oracle数据库成功.")

    def query_one(self, sql):
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        data = self.cursor.fetchone()
        logger.info("查询Oracle一条数据.")
        self.cursor.close()
        return data

    def query_all(self, sql):
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        data = self.cursor.fetchall()
        logger.info("查询Oracle多条数据.")
        self.cursor.close()
        return data

    def query(self, sql, one=False):
        # 结果是个list
        if one:
            return self.query_one(sql)
        return self.query_all(sql)

    def delete_cursor(self, sql):
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        logger.info("成功删除Oracle数据.")
        self.cursor.close()

    def close(self):
        self.conn.close()
        logger.info("关闭Oracle数据库成功.")

# 入参格式如下
# db_sql = OracleHandler().query("SELECT * FROM EDC.QUESTION_TYPES WHERE ID = '97532617-2e02-441d-9a2e-5f2ee4e0399a'",one=True)
# print(db_sql)

5、mysql_handler.py:操作mysql数据库的方法

import pymysql     # 使用pip install PyMySQL命令安装
from pymysql.cursors import DictCursor
from common.logger_handler import logger     # 添加日志


class MySQLHandler:
    def __init__(self,
                 host='',
                 port=3306,
                 user='',
                 password='',
                 charset='utf8',
                 # 指定数据库
                 database='',
                 cursorclass=DictCursor
                 ):
        try:
            self.conn.ping()
        except:
            self.conn = pymysql.connect(host=host,
                                        port=port,
                                        user=user,
                                        password=password,
                                        charset=charset,
                                        # 指定数据库
                                        database=database,
                                        cursorclass=cursorclass)
        logger.info("连接MySQL数据库成功.")

    def query_one(self, sql):
    """查询一条数据"""
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        data = self.cursor.fetchone()
        logger.info("查询MySQL一条数据.")
        self.cursor.close()
        return data

    def query_all(self, sql):
    """查询多条数据"""
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        data = self.cursor.fetchall()
        logger.info("查询MySQL多条数据.")
        self.cursor.close()
        return data

    def query(self, sql, one=False):
        # 结果是个list
        if one:
            return self.query_one(sql)
        return self.query_all(sql)

    def delete_cursor(self, sql):
    """删除数据"""
        self.cursor = self.conn.cursor()
        self.cursor.execute(sql)
        # 事务提交
        self.conn.commit()
        logger.info("成功删除MySQL数据.")
        self.cursor.close()

    def close(self):
        self.conn.close()
        logger.info("关闭MySQL数据库成功.")

# 入参方法如下
# db_sql = DBHandle().query("select leave_amount from member where mobile_phone='1555555555'",one=True)
# print(db_sql)

6、sftp_handler.py:操作后台服务器、上传/下载文件的方法

import paramiko     # 使用pip install paramiko命令安装
from common.logger_handler import logger     # 打印日志

class Sftp:
    def __init__(self,
                 host='',
                 port=22,
                 username='',
                 password=''):
        self.host = host
        self.port = port
        self.username = username
        self.password = password

    def sftp_exec_command(self,command):
    """在服务器执行命令"""
        try:
            # 1.创建SSH对象
            ssh_client = paramiko.SSHClient()
            # 2.允许连接不在know_hosts文件中的主机
            ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            # 3.连接Linux服务器
            ssh_client.connect(hostname=self.host,
                port=self.port,
                username=self.username,
                password=self.password)
            std_in, std_out, std_err = ssh_client.exec_command(command)
            # result = stdout.read()
            for line in std_out:
                print(line.strip("\n"))
            ssh_client.close()
        except Exception as e:
            logger.error(e)

    def sftp_upload_file(self,server_path, local_path):
    """向服务器上传文件"""
        try:
            transport = paramiko.Transport((self.host, self.port))
            transport.connect(username=self.username, password=self.password)
            sftp = paramiko.SFTPClient.from_transport(transport)
            sftp.put(local_path, server_path)
            transport.close()
        except Exception as e:
            logger.info(e)

    def sftp_down_file(self,server_path, local_path):
    """从服务器下载文件"""
        try:
            transport = paramiko.Transport((self.host, self.port))
            transport.connect(username='user', password='password')
            sftp = paramiko.SFTPClient.from_transport(transport)
            sftp.get(server_path, local_path)
            transport.close()
        except Exception as e:
            logger.info(e)

注意:4、5、6模块根据各自项目实际情况选择使用

三、config目录

config下存放项目测试账号、环境信息文件,主要包括:
1、env_config.yaml
环境信息,可以将URL中的公共部分写入该文件

# 书格式为:
# 名称: https://xx.xx.xx.xx:port
# 如下:
env_url: https://10.10.10.10:8080
# 提示:名称可以自定义,名称的冒号后面有个空格

2、mysql_config.yaml
mysql库连接信息,可以将host、port、username、password等写入该文件

# 如下:
db:
  user: 'root'     # 用户名
  host: '10.10.10.10'     # ip
  port: 3306     # 端口号
  password: '123456'     # 登录密码
  charset: 'utf8'
  database: 'abc'     # 数据库子库
# 提示:名称可以自定义,名称的冒号后面有个空格

3、oracle_config.yaml
oracle库连接信息,可以将host、port、username、password等写入该文件

# 如下:
db:
  user: 'root'     # 用户名
  host: '10.10.10.10'     # ip
  port: 1521     # 端口号
  password: '123456'     # 登录密码
  database: 'ORCL'
# 提示:名称可以自定义,名称的冒号后面有个空格

4、user_config.yaml
登录环境的账号信息

# 注释, key后面的: 后必须加空格分开 key value
d_user:
  username: 'xiaoming'
  password: '123456'
p_user:
  username: 'xiaohua'
  password: '123456'
# 若单个用户时,直接写username/password也可以;若多个用户时,建议在username/password上再包一层,如:d_user进行区分

5、path.py
获取reports(测试报告)、logs(日志目录)、case_datas(接口信息)路径

import os

# 动态获取路径
config_path = os.path.dirname(os.path.abspath(__file__))

# 获取项目根目录
root_path = os.path.dirname(config_path)

# reports 路径
reports_path = os.path.join(root_path, 'reports')
# 若reports_path不存在,则新建
if not os.path.exists(reports_path):
    os.mkdir(reports_path)

# log 路径
logs_path = os.path.join(root_path, 'logs')
# 若logs_path不存在,则新建
if not os.path.exists(logs_path):
    os.mkdir(logs_path)

# data 路径
data_path = os.path.join(root_path, 'case_datas')

四、logs目录

该目录存放测试过程中的日志信息,文件中主要包含日志时间、日志等级、日志产生文件、日志产生行、日志内容等信息,其中日志内容用户可以自定义在需要打印日志的文件中添加。

五、middleware目录

该目录存放测试用例和用例执行入口的中间层文件,主要包含:数据库连接、配置文件内容读取、造数据(时间戳、时间、日期和其他变量等)、数据动态替换方法类等
handler.py

import datetime
import os, re
import time

from pymysql.cursors import DictCursor

from common.yaml_handler import read_yaml     # 导入read_yaml
from common.excel_handler import ExcelHandler     # 导入ExcelHandler
from common.mysql_handler import MySQLHandler     # 导入MySQLHandler
from common.oracle_handler import OracleHandler     # 导入OracleHandler
from config import path     # 导入path


class MidMySQLHandler(MySQLHandler):
    """连接MySQL数据库"""

    def __init__(self):
        # 读取mysql_config.yaml文件内容
        mysql_path = os.path.join(path.config_path, 'mysql_config.yaml')
        mysql_config = read_yaml(mysql_path)
        # 继承MySQLHandler
        super().__init__(host=mysql_config['db']['host'],
                         port=mysql_config['db']['port'],
                         user=mysql_config['db']['user'],
                         password=mysql_config['db']['password'],
                         charset=mysql_config['db']['charset'],
                         # 指定数据库
                         database=mysql_config['db']['database'],
                         cursorclass=DictCursor)


class MidOracleHandler(OracleHandler):
    """连接Oracle数据库"""

    def __init__(self):
        # 读取oracle_config.yaml文件内容
        oracle_path = os.path.join(path.config_path, 'oracle_config.yaml')
        oracle_config = read_yaml(oracle_path)
        # 继承OracleHandler
        super().__init__(host=oracle_config['db']['host'],
                         port=oracle_config['db']['port'],
                         user=oracle_config['db']['user'],
                         password=oracle_config['db']['password'],
                         # 指定数据库
                         database=oracle_config['db']['database'])


class Handler():
    """任务:中间层。 common 和 调用层。
    使用项目的配置数据,填充common模块
    """
    # 获取环境信息
    env_path = os.path.join(path.config_path, 'env_config.yaml')
    env_config = read_yaml(env_path)

    # 获取用户信息
    user_path = os.path.join(path.config_path, 'user_config.yaml')
    user_config = read_yaml(user_path)
    # mysql
    mysql_path = os.path.join(path.config_path, 'mysql_config.yaml')
    mysql_config = read_yaml(mysql_path)

    # 获取excel测试用例信息
    excel_file = os.path.join(path.data_path, 'case_datas.xlsx')
    excel = ExcelHandler(excel_file)

    # 需要动态替换 #...# 的数据;将Excel测试用例中两个#之间的值进行替换
    before_day = (datetime.datetime.now() + datetime.timedelta(days=-1)).strftime("%Y-%m-%d")     # 昨天
    now_day = datetime.datetime.now().strftime("%Y-%m-%d")     # 今天
    next_day = (datetime.datetime.now() + datetime.timedelta(days=+1)).strftime("%Y-%m-%d")     # 明天
    week_day = (datetime.datetime.now() + datetime.timedelta(days=-6)).strftime("%Y-%m-%d")     # 近一周
    month_day = (datetime.datetime.now() + datetime.timedelta(days=-28)).strftime("%Y-%m-%d")     # 近一月
    now_time_stamp = int(time.time())     # 获取当前时间戳,单位:秒
    now_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")     # 获取当前时间

    @classmethod
    def replace_data(cls, string, pattern='#(.*?)#'):     # 将Excel测试用例中两个#之间的值进行替换
        """数据动态替换,解决接口依赖问题"""
        # pattern = '#(.*?)#'
        results = re.finditer(pattern=pattern, string=string)
        for result in results:
            old = result.group()
            key = result.group(1)
            new = str(getattr(cls, key, ''))
            string = string.replace(old, new)
        return string

六、reports目录

该目录存放测试报告,报告类型一般为allure或html类型,可任选其一

1、allure测试报告配置,可参考:allure报告配置

2、html测试报告需要导入三方库,安装命令为:pip install pytest-html

七、tests目录

该目录作为单个测试用例执行的入口,文件中主要包含读取Excel中的测试用例并参数化、发送接口请求、对响应结果断言、设置Handler对应的属性(与middleware目录下handler.py文件中的类方法配合使用;若接口间没有依赖,则该部分可以不需要)

tests目录下的“.py”文件要以“test”开头或结尾。

import pytest, requests, json, allure, os
from jsonpath import jsonpath

from common.logger_handler import logger
from middleware.handler import Handler

# 读取Excel测试用例中的sheet页名称,可以自定义
excel_datas = Handler.excel.read('sheet1')

@allure.title("自定义")     # allure测试报告的title,一般根据模块自定义
@pytest.mark.parametrize('datas', excel_datas)     # 参数化读取到的Excel测试用例
# 函数名称要以“test”开头或结尾
def test_project_doc(datas, login_cookie):     # login_cookie为登录用户的Cookie信息,该值来源于conftest.py文件
    datas = json.dumps(datas)

    # 动态替换依赖值
    datas = Handler.replace_data(datas)
    # 转化成字典
    datas = json.loads(datas)

    allure.dynamic.title(datas["title"])     # allure测试报告的title,可以自定义,我这里读取的是Excel用例中title列的值
    allure.dynamic.description("测试{}接口.".format(datas["title"]))     # allure测试报告的description,可以自定义,我这里读取的是Excel用例中title列的值

    headers = json.loads(datas["headers"])     # 获取Excel用例中header列的值
    headers["Cookie"] = login_cookie["cookie"]     # 将Cookie值传入请求头

    with allure.step('请求方式'):     # 测试步骤
        allure.attach('{}'.format(datas["method"]), name='{}请求方式'.format(datas["title"]))

    with allure.step('请求路径'):     # 测试步骤
        allure.attach('{}'.format(datas["path"]), name='{}请求路径'.format(datas["title"]))

    with allure.step('请求体'):     # 测试步骤
        allure.attach('{}'.format(datas["data"]), name='{}请求体'.format(datas["title"]))
    logger.info("{}请求路径为:{},请求体为:{}".format(datas["title"], datas["path"], datas["data"]))

    files = json.loads(datas["files"])
    # 判断Excel用例中title列的值如果包含“上传”则获取“file_name”列的值;
    # 如果是需要上传文件的接口,建议在Excel用例中title列通过关键字标识下,我这里使用的是“上传”。
    if "上传" in datas["title"]:
        config_path = os.path.dirname(os.path.abspath(__file__))
        root_path = os.path.dirname(config_path)
        data_path = os.path.join(root_path, 'upload_files')
        files = {"file": (files["file_name"], open(data_path + files["file_path"], "rb"), files["content_type"])}
    # 发送请求
    resp = requests.request(method=datas["method"],
                            url=Handler.env_config["env_url"] + datas["path"],
                            headers=headers,
                            files=files,
                            json=json.loads(datas["data"]),
                            verify=False)

    with allure.step('响应'):     # 测试步骤
        allure.attach('{}'.format(resp.status_code), name='状态码')
    logger.info("{}状态码:{},响应结果:{}".format(datas["title"], resp.status_code, resp.text))
    # 如果所有接口的响应结果是json格式,建议使用resp.json()
    # logger.info("{}状态码:{},响应结果:{}".format(datas["title"], resp.status_code, resp.json()))

    expected = json.loads(datas['expected'])
    # 状态码断言
    with allure.step('断言'):     # 测试步骤
        for key, value in expected.items():
            assert resp.status_code == value     # 断言状态码
            allure.attach('OK', name='{}断言'.format(key))
            
    # 多值断言,一般断言响应结果中的值。
    # with allure.step('断言'):
    #     for key, value in expected.items():
    #         assert str(jsonpath(resp.json(), key)[0]) == value
    #         allure.attach('OK', name='{}断言'.format(key))

    # 设置Handler对应的属性。
    if datas['extractor']:
        extrators = json.loads(datas['extractor'])
        for prop, jsonpath_exp in extrators.items():
            value = jsonpath(resp.json(), jsonpath_exp)[0]
            setattr(Handler, prop, value)

八、upload_files目录

upload_files目录用于存放上传类接口所需的文件;若没有上传类接口,则该目录可以不需要

九、conftest.py文件

conftest.py主要用作测试用例的前置或后置条件,例如:用户登录获取Cookie、连接数据库等

import pytest
import requests
from middleware.handler import Handler, MidMySQLHandler, MidOracleHandler
from jsonpath import jsonpath


def login(username):
    """登录,得到Cookie
    """
    # 发送登录接口请求
    resp = requests.request(method='POST',
                            url=Handler.env_config["env_url"] + '/UAMS/auth?username={}'.format(username),
                            headers={"Content-Type": "x-www-form-urlencoded"},
                            json={},
                            verify=False
                            )
    resp_json = resp.json()
    name_session = jsonpath(resp_json, '$..name')[0]
    value_session = jsonpath(resp_json, '$..value')[0]

    cookie = "=".join([name_session, value_session])     # 生成Cookie,EDC这边的Cookie是通过登录接口响应结果中的“name=value”拼接的
    return {"cookie": cookie}     # 将Cookie返回


@pytest.fixture()     # 夹具必须要在函数上面使用“@pytest.fixture()”后可以在用例执行入口处直接引用
def login_cookie():
    """交付小组用户登录"""
    user = {
        "username": Handler.user_config['d_user']['username']
    }
    return login(user['username'])

@pytest.fixture()     # 夹具必须要在函数上面使用“@pytest.fixture()”后可以在用例执行入口处直接引用
def a_login():
    """系统管理组组用户登录"""
    user = {
        "username": Handler.user_config['a_user']['username']
    }
    return login(user['username'])

# 若没有mysql库相关的操作,可以删除mysql_db夹具
@pytest.fixture()
def mysql_db():
    """管理MySQL数据库链接的夹具"""
    mysql_db_conn = MidMySQLHandler()
    # db_conn = Handler.db_class
    yield mysql_db_conn
    mysql_db_conn.close()
    
# 若没有oracle库相关的操作,可以删除oracle_db夹具
@pytest.fixture()
def oracle_db():
    """管理Oracle数据库链接的夹具"""
    oracle_db_conn = MidOracleHandler()
    yield oracle_db_conn
    oracle_db_conn.close()

十、run.py文件

run.py是一个测试用例收集器,作用是执行工程目录下所有的“test”开头或结尾的py文件。

"""
项目入口,主程序
收集用例,运行用例,生成报告
"""
import os,pytest
from datetime import datetime
from config.path import reports_path

# pytest 收集用例
def run_case():
  """
    # html测试报告
    timestamp = str(datetime.now().strftime("%Y-%m-%d %H_%M_%S"))
    reportfilename = 'report_%s.html' % timestamp
    htmlreport = os.path.join(reports_path, reportfilename)
    pytest.main(['--html={}'.format(htmlreport)])
  """
    # allure测试报告
    pytest.main(['--alluredir', 'reports/allure-report'])
    os.system(r'allure generate reports/allure-reports -o reports/html --clean')

run_case()

# 运行本地的Allure测试报告可以使用以下命令:
# allure serve <path_to_report_directory>
# 其中,<path_to_report_directory> 指的是测试报告的目录路径。执行上述命令后,会在默认浏览器中打开 Allure 的 HTML 测试报告页面,方便查看测试结果。

十一、README.txt文件

README需要有的几个功能模块:

1、软件项目的定位,基本功能描述;

2、运行项目代码的方法,安装环境,启动方式命令等;

3、简明扼要的使用说明;

4、项目代码结构说明,更详细说明软件的原理;

5、常见问题说明,以及注意事项。

README还要包含的一些内容:

1、项目和所有子类模块和所有库的名字;

2、如何使用;

3、版权和许可信息;

4、提交的bug、功能需求、补丁等介绍;

5、历史记录;

十二、requirements.txt文件

记录项目所有依赖的第三方模块,方便迁移到不同的环境中,防止缺少模块,或因为所依赖的第三方模块不同而引起的一系列问题。

allure-pytest2.12.0
allure-python-commons
2.12.0
async-generator1.10
attrs
22.2.0
baostock0.8.8
bcrypt
4.0.1
certifi2022.12.7
cffi
1.15.1
charset-normalizer2.1.1
colorama
0.4.6
cryptography38.0.4
cx-Oracle
8.3.0
et-xmlfile1.1.0
exceptiongroup
1.1.0
h110.14.0
idna
3.4
iniconfig1.1.1
jsonpath
0.82
kafka-python2.0.2
numpy
1.24.1
openpyxl3.0.10
outcome
1.2.0
packaging22.0
paho-mqtt
1.6.1
pandas1.5.2
paramiko
2.12.0
pluggy1.0.0
py
1.11.0
pycparser2.21
PyMySQL
1.0.2
PyNaCl1.5.0
pyOpenSSL
22.1.0
PySocks1.7.1
pytest
7.2.0
pytest-html3.2.0
pytest-metadata
2.0.4
python-dateutil2.8.2
pytz
2022.7
PyYAML6.0
requests
2.28.1
selenium4.7.2
six
1.16.0
sniffio1.3.0
sortedcontainers
2.4.0
tomli2.0.1
trio
0.22.0
trio-websocket0.9.2
urllib3
1.26.13
wsproto==1.2.0

将上面的三方库复制并保存为requirements.txt文件后,打开命令行执行pip install -r requirements.txt 可以导入所有三方依赖库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值