Python简单工具

◀▶log 日志

★ 基本介绍

logging是Python内置的日志处理模块,用来记录程序的运行状态和故障排除;因为print是同步代码,会影响代码的执行速度,而logging是异步的,可以在多进程爬取中、在协程爬取中使用logging记录调试信息而不会影响到代码的异步执行和运行效率。

★ 日志信息等级

  • 日志信息分成五级;从高到低排序为:
    • CRITICAL:严重错误
    • ERROR: 错误
    • WARNING: 警告 (默认级别)
    • INFO: 主要信息
    • DEBUG: 调试信息

★ 日志输出

  • 将日志输出到控制台
    import logging
    
    # 定义日志输出格式和等级
    logging.basicConfig(level=logging.WARNING,  
                        format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')  
    logger = logging.getLogger("日志名")
    
    logger.debug()
    logger.error()
    ...
    
  • 将日志输出到文件
    import logging  
    
    logging.basicConfig(level=logging.WARNING,  
                        filename='./log.txt',  
                        filemode='w',  
                        format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')  
    
    logger = logging.getLogger("日志名")
    
    # use logging  
    logger.info('这是 loggging info message')  
    logger.debug('这是 loggging debug message')  
    logger.warning('这是 loggging a warning message')  
    logger.error('这是 an loggging error message')  
    logger.critical('这是 loggging critical message')
    
  • 将日志输出到控制台同时输出到文件
    import logging  
    
    # 第一步,创建一个logger  
    logger = logging.getLogger()  
    logger.setLevel(logging.INFO)  # Log等级总开关  
    
    # 第二步,创建一个handler,用于写入日志文件  
    logfile = './log.txt'  
    fh = logging.FileHandler(logfile, mode='a')  # open的打开模式这里可以进行参考
    fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关  
    
    # 第三步,再创建一个handler,用于输出到控制台  
    ch = logging.StreamHandler()  
    ch.setLevel(logging.WARNING)   # 输出到console的log等级的开关  
    
    # 第四步,定义handler的输出格式  
    formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")  
    fh.setFormatter(formatter)  
    ch.setFormatter(formatter)  
    
    # 第五步,将logger添加到handler里面  
    logger.addHandler(fh)  
    logger.addHandler(ch)  
    
    # 日志  
    logger.debug('这是 logger debug message')  
    logger.info('这是 logger info message')  
    logger.warning('这是 logger warning message')  
    logger.error('这是 logger error message')  
    logger.critical('这是 logger critical message')
    

★ 日志应用示例

  • 自定义 log 日志
    # 自定义日志输出的格式类
    import logging
    import logging.handlers
    import os
    
    
    class RequestFormatter(logging.Formatter):
    
        # 重写父类format方法 添加请求url和remote_addr日志项目
        def format(self, record):
            record.url = "www.baidu.com"
            record.remote_addr = "127.0.0.1"
            return super().format(record)
    
    
    # 创建个性化的logger对象
    def create_logger():
        LOG_LEVEL = 'DEBUG'
        LOG_FILE_DIR = 'logs/'
        LOG_FILE_MAX_BYTES = 300 * 1024 * 1024  # 300M
        LOG_FILE_BACKUP = 100  # 备份日志文件数
    
        # 创建日志存放路径
        if not os.path.isdir(LOG_FILE_DIR):
            os.mkdir(LOG_FILE_DIR)
    
        # 设置日志输出格式
        request_file_formatter = RequestFormatter('%(asctime)s - %(url)s[%(remote_addr)s] - '
                                                  '%(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
        request_console_formatter = logging.Formatter(
            '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
    
        # 创建日志的文件处理器对象 和 控制台的处理器对象
        file_handler = logging.handlers.RotatingFileHandler(
            filename=os.path.join(LOG_FILE_DIR, "shopping.log"),
            maxBytes=LOG_FILE_MAX_BYTES,
            backupCount=LOG_FILE_BACKUP
        )
        console_handler = logging.StreamHandler()
    
        # 为日志的文件处理器对象 和 控制台的处理器对象 设置输出格式
        file_handler.setFormatter(request_file_formatter)
        console_handler.setFormatter(request_console_formatter)
    
        # 创建日志并设置日志等级
        logger = logging.getLogger('Shopping')
        logger.setLevel(LOG_LEVEL)
    
        # 将handler添加到logger; debug模式才输出日志到控制台
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        return logger
    
    
    if __name__ == '__main__':
        logger = create_logger()
        logger.info('log日志测试')
    

◀▶ 数据序列化

★ 什么是数据的序列化

数据的序列化指的是将数据从其原始格式转换为一种可存储或传输的格式,通常是字节流或字符串的形式。这样做的目的是为了能够在不同系统、编程语言或设备之间进行数据交换、存储或传输,同时保持数据的结构完整性。

★ 序列化的应用场景:

  • 数据传输
    序列化可用于在网络中传输数据,比如Web应用中的前后端数据交互,API请求和响应的数据传输等。
    
  • 数据持久化
    序列化使得数据可以被保存在文件系统或数据库中,例如存储配置文件、持久化对象等。
    
  • 跨平台数据交换
    序列化使得不同平台、不同编程语言之间能够共享和解释数据,促进系统间的数据交换和互操作。
    

Python 常见序列化工具

  • pickle 序列化
    • pickle 基本介绍

      pickle模块是python的标准模块,提供了对于python数据的序列化操作,可以将数据转换为bytes类型,其序列化速度比json模块要高
      
    • 使用方式

      pickle.dumps() 将python数据序列化为bytes类型
      pickle.loads() 将bytes类型数据反序列化为python的数据类型
      
  • base64 序列化
    • base64 基本介绍

      Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2^6=64,所以每6个比特为一个单元,对应某个可打印字符。3个字节有24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同
      
    • 使用方式

      base64.b64encode() 将bytes类型数据进行base64编码,返回编码后的bytes类型
      base64.b64deocde() 将base64编码的bytes类型进行解码,返回解码后的bytes类型
      
  • json 序列化
    • json 基本介绍

      用于JSON格式的序列化和反序列化。JSON序列化支持Python中的基本数据类型(如字典、列表、字符串、数字等)与JSON字符串之间的转换。
      
    • 使用方式

      json.dumps() 将python数据序列化为json字符串
      json.loads() 将json字符串反序列化为python的数据类型
      

◀▶ 简单进度条

★ 进度条基本实现

  • 功能代码
    import sys
    import time
    
    # 进度条-功能演示
    def progress_bar_demo(title, symbol):
        for per in range(1, 101):
            print("\r", end="")
            print(f"【{title}】: {per}%: {symbol * (per // 2)} ", end="")
            sys.stdout.flush()
            time.sleep(0.1)
        print("Complete !!!")
       
    # 进度条效果演示
    progress_bar_demo("Download progress", "▋")
    

★ 进度条应用版

  • 功能代码
    import sys
    import time
    
    
    # 进度条应用版
    class ProgressBar:
    
        def __init__(self, title, symbol="▋"):
            self.title = title
            self.symbol = symbol
            # 记录生成器状态
            self.is_stop = False
            # 获取一个进度条生成器对象
            self.process = self._process()
            # 启动生成器
            next(self.process)
    
        # 返回进度条状态 True 表示进度条已结束
        def update_process(self, percent):
            if self.is_stop:
                return True
    
            # 数据校验
            if percent < 0:
                percent = 0
            elif percent > 100:
                percent = 100
            percent = int(percent)
    
            # 更新进度条与状态
            try:
                self.process.send(percent)
            except StopIteration:
                self.is_stop = True
                return True
            else:
                return False
    
        def _process(self):
            while True:
                percent = yield
    
                print("\r", end="")
                print(f"【{self.title}】: {percent}%: {self.symbol * (percent // 2)} ", end="")
                sys.stdout.flush()
    
                if percent >= 100:
                    print("Complete !!!")
                    break
    
    
    # 模拟文件总数和处理完成的文件数
    total_num = 125
    complete_num = 0
    
    pb = ProgressBar("Download progress")
    while True:
        # 模拟文件处理耗时
        time.sleep(0.1)
        complete_num += 1
    
        is_over = pb.update_process(complete_num / total_num * 100)
        if is_over:
            break
    
  • 运行效果

    进度条效果

◀▶ Python 加载 C 语言动态库

★ 背景说明

1. python是一门胶水语言,可以通过加载动态库的方式在一个项目中运行不同语言的程序
2. 通过动态库加载其他语言的方式可以解决多线程GIL使用C解释器无法并发运行的问题

★ 在 Linux 中运行 C 语言代码:

  • 编辑C语言代码
    // hello.c
    // c代码作为启动文件必须加include<stdio.h>,作为动态库可以不加
    # include<stdio.h>
    
    void dead_loop(){
        while(1)
        {
            printf("hello world\n");
        }
    }
    
    // c语言必须以main函数作为启动入口
    void main(){
        printf("this is main function!\n");
    }
    
  • 编译C语言代码(会生成xx.out 二进制可执行文件; 不需要编译器)
    gcc  xxx.c -o yyy.out  
    
    // -o 指定生成二进制文件的名称
    gcc hello.c -o hello.out
    
  • 运行编译后的文件
    ./hello.out
    

★ 使用 Python 中加载 C 语言动态库

  • C 语言文件编译成一个动态库(Linux平台下)
    gcc xx.c -fPIC -shared -o  lib_hello.so
    
  • python 调用 C 语言动态库代码实现
    # dynamic_load_c.py
    
    import ctypes
    from threading import Thread
    
    # 加载动态库
    hello_lib = ctypes.cdll.LoadLibrary("./lib_hello.so")
    # 多线程调用C语言库
    Thread(target=hello_lib.dead_loop).start()
    # 也可以直接 hello_lib.dead_loop()  方式调用
    

◀▶ Python 环境变量设置与读取

★ 环境变量基本概念

  • 环境变量定义
    	环境变量是操作系统中存储有关操作系统配置信息和应用程序运行环境的动态值的一种机制。环境变量的主要作用是为正在运行的进程提供配置信息,帮助程序找到所需的资源或者确定程序运行的方式。
    	在操作系统中,每个进程都有自己的环境变量集合。这些变量可以包含诸如路径、临时文件位置、语言设定、用户偏好设置等信息。程序可以通过读取环境变量来获取这些信息,从而适应不同的操作系统环境或用户设置。
    
  • 作用
    	有些字符不宜明文写进代码里,比如数据库密码,个人账户密码,如果写进自己本机的环境变量里,程序用的时候通过 os.environ.get() 取出来就行了。这样开发人员本机测试的时候用的是自己本机的一套密码,生产环境部署的时候,用的是公司的公共账号和密码,这样就能增加安全性
    

★ 环境变量的设置

  • 方式一. 设置系统环境变量
    • 第一步 在我们的电脑上鼠标右键此电脑,选择属性,进去之后,点击高级系统设置

    • 第二步 点击环境变量

    • 第三步 编辑环境变量并保存

  • 方式二. 设置Pycharm环境变量
  • 方式三. 设置单个项目的环境变量
  • 方式四. 将环境变量按指定格式写入到文件(一般设置为隐藏格式如 .env)
    • 1.编辑环境变量文件 .env

      API_KEY=abcdef123456
      DEBUG=True
      MAX_CONNECTIONS=10
      
    • 2.加载环境变量文件

      • 使用 os.environ

        """
        在Python中,os.environ是一个字典,保存了当前进程的环境变量。我们可以使用os.environ来读取env文件中的配置参数
        """
        import os
        
        # 从.env文件中读取配置参数
        def read_env_by_file(file_path):
            with open(file_path, 'r') as file:
                for line in file:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        key, value = line.split('=')
                        os.environ[key] = value
        
        # 示例:读取.env文件
        read_env_by_file('.env')
        
        # 使用配置参数
        api_key = os.environ.get('API_KEY')
        debug = os.environ.get('DEBUG')
        max_connections = os.environ.get('MAX_CONNECTIONS')
        
        print(f'API_KEY: {api_key}')
        print(f'DEBUG: {debug}')
        print(f'MAX_CONNECTIONS: {max_connections}')
        
        
      • 使用 python-dotenv

        """
        python-dotenv是一个非常流行的Python库,用于读取.env文件。相比于手动解析.env文件,使用python-dotenv更加简洁和易用
        """
        from dotenv import load_dotenv
        
        # 从.env文件中加载配置参数
        def read_env_by_file(file_path):
            load_dotenv(file_path)
        
        # 示例:加载.env文件
        read_env_by_file('.env')
        
        # 使用配置参数
        api_key = os.getenv('API_KEY')
        debug = os.getenv('DEBUG')
        max_connections = os.getenv('MAX_CONNECTIONS')
        
        print(f'API_KEY: {api_key}')
        print(f'DEBUG: {debug}')
        print(f'MAX_CONNECTIONS: {max_connections}')
        
        

★ 环境变量的读取

  • 读取环境变量所有key
    os.environ.keys() 
    
  • 环境变量常用key
    • Windows 系统

      os.environ['HOMEPATH']:当前用户主目录。
      os.environ['TEMP']:临时目录路径。
      os.environ["PATHEXT"]:可执行文件。
      os.environ['SYSTEMROOT']:系统主目录。
      os.environ['LOGONSERVER']:机器名。
      os.environ['PROMPT']:设置提示符。
      
    • Linux 系统

      os.environ['USER']:当前使用用户。
      os.environ['LC_COLLATE']:路径扩展的结果排序时的字母顺序。
      os.environ['SHELL']:使用shell的类型。
      os.environ['LAN']:使用的语言。
      os.environ['SSH_AUTH_SOCK']:ssh的执行路径。
      
  • 环境变量的增删改查
    # 新增环境变量
    os.environ['环境变量名称']='环境变量值' #其中key和value均为string类型
    os.putenv('环境变量名称', '环境变量值')
    os.environ.setdefault('环境变量名称', '环境变量值')
    
    #更新环境变量
    os.environ['环境变量名称']='新环境变量值'
    
    #获取环境变量
    os.environ['环境变量名称']
    os.getenv('环境变量名称')
    os.environ.get('环境变量名称', '默认值') 
    
    # 删除环境变量
    del os.environ['环境变量名称']
    del(os.environ['环境变量名称'])
    
    # 判断环境变量是否存在
    '环境变量值' in os.environ   # 存在返回 True,不存在返回 False
    

目录结构生成器

import os
import time
import math


class DirectoryGenerator:

    def __init__(self, root_path):
        self.root_path = root_path
        self.files_directory = {}

    # 格式化时间函数
    @staticmethod
    def format_time(t):
        return time.strftime('%Y-%m-%d %H-%M-%S', time.localtime(t))

    # 文件大小转换
    @staticmethod
    def file_size_conversion(size_bytes):
        if size_bytes == 0:
            return "0B"
        size_unit = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        level = int(math.floor(math.log(size_bytes, 1024)))
        level_bytes = math.pow(1024, level)
        value = round(size_bytes / level_bytes, 2)
        return f'{value}{size_unit[level]}'

    # 获取文件信息的字典数据
    def get_file_detail_info(self, file_path):
        file_info = os.stat(file_path)
        file_info_dict = {
            'last_visit_time': self.format_time(file_info.st_atime),
            'last_modify_time': self.format_time(file_info.st_mtime),
            'size': self.file_size_conversion(file_info.st_size),
        }
        return file_info_dict

    def get_files_directory(self):
        # 调用内部递归方法获取目录结构并保存到实例属性,返回数据
        self.files_directory = self.__get_files_directory(self.root_path)
        return self.files_directory

    def print_file_directory(self):
        # 调用内部递归方法打印目录结构
        self.__print_file_directory(self.files_directory)

    # 返回目录结构{"name": "目录生成器", "child": [{"name": "目录生成器", "child": []}]}
    def __get_files_directory(self, root_path):
        # 将路径处理为标准格式
        files_directory = {'name': os.path.basename(root_path), 'path': root_path, 'child': []}

        # 遍历路径下的所有文件和文件夹
        for file_or_dir in os.listdir(root_path):
            # 拼接完整路径
            path = os.path.join(root_path, file_or_dir)

            # 如果是文件直接添加到列表
            if os.path.isfile(path):
                child = {'name': os.path.basename(path), 'path': path, 'child': []}
                # 如果是文件则获取文件相关信息,合并到文件信息中
                child.update(self.get_file_detail_info(path))
                files_directory['child'].append(child)
            # 如果是文件夹递归调用函数获取文件列表
            else:
                files_directory['child'].append(self.__get_files_directory(path))

        return files_directory

    def __print_file_directory(self, files_directory):
        is_file = os.path.isfile(files_directory['path'])
        child_directorys = files_directory['child']
        if is_file:
            print(f"""
            文件名: {files_directory['name']}
            文件大小: {files_directory['size']}
            最后访问时间: {files_directory['last_visit_time']}
            最后修改时间: {files_directory['last_modify_time']}
            文件路径: {files_directory['path']}
            """, end='')
        else:
            print(f"""
            文件夹名: {files_directory['name']}
            文件夹路径: {files_directory['path']}
            子文件或子文件夹: {[child_directory['name'] for child_directory in child_directorys]}
            """, end='')

        # 递归打印每个目录信息
        for child_directory in child_directorys:
            self.__print_file_directory(child_directory)


if __name__ == '__main__':
    # 查询的路径
    search_path = r'C:\Users\damon\Desktop\目录生成器'
    # 实例化"目录生成器"对象
    directory_generator = DirectoryGenerator(search_path)
    # 获取文件目录结构
    directory_generator.get_files_directory()
    # 打印文件目录
    directory_generator.print_file_directory()

JWT 基本介绍与使用

JWT 基本概念

JWT(JSON Web Token)是一种用于在网络应用之间传递信息的安全方式。它是一种基于 JSON 的开放标准(RFC 7519),用于在网络应用之间安全地传输声明。JWT 通常用于身份验证和授权,以及在分布式系统中传递声明。

JWT 组成部分

JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。其中,头部和载荷都是 JSON 对象,签名是对头部和载荷进行加密后的字符串。

JWT 的使用流程

1. 用户向服务器发送身份验证请求,服务器验证用户身份并生成 JWT。
2. 服务器将 JWT 发送回客户端,客户端将其存储在本地。
3. 客户端向服务器发送请求时,将 JWT 作为请求头部的 Authorization 字段发送给服务器。
4. 服务器验证 JWT 的签名和有效期,并根据其中的声明进行授权。

JWT 的优点

无状态:JWT 中包含了所有必要的信息,因此服务器不需要在自己的数据库中存储用户信息,从而使得服务器可以更容易地扩展。
跨平台:由于 JWT 是基于 JSON 的开放标准,因此它可以在不同的编程语言和平台之间进行交互。
安全性:JWT 使用签名来验证数据的完整性和真实性,从而保证了数据的安全性。

JWT 说明

需要注意的是,JWT 中的信息是可以被解码的,因此不应该在 JWT 中存储敏感信息。此外,JWT 的有效期应该设置得足够短,以避免被恶意利用

JWT 基本使用示例

"""
pip install PyJWT
"""
import jwt
import datetime

# -------------------------JWT生成-----------------------------
# 设置密钥
secret_key = 'my_secret_key'
# 设置过期时间
expires_in = datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
# 设置载荷
payload = {
    'website': 'stu_manage',
    'user_id': 123,
    'exp': expires_in
}

# 生成JWT
jwt_token = jwt.encode(payload, secret_key, algorithm='HS256')
print(jwt_token)


# -------------------------JWT解析-----------------------------
# JWT解析(验证 JWT 签名)
decoded_token = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
# 解密后的 JWT 载荷
print(decoded_token)
print(datetime.datetime.fromtimestamp(decoded_token['exp']))
# 读取JWT头部header
print(jwt.get_unverified_header(jwt_token))


JWT 封装使用

"""
pip install PyJWT
"""
import jwt
from jwt.exceptions import DecodeError, ExpiredSignatureError
import datetime
import logging

logger = logging.getLogger('django')

# 获取Django的密钥
SECRET_KEY = 'django-insecure-2cvu_ew%s2&(3+ex7dn=gs@(4dv_)o5=81(q!4(h+_4eua7-7%'


# 生成JWT token
def generate_jwt_token(payload, expire):
    expires_in = datetime.datetime.utcnow() + datetime.timedelta(seconds=expire)
    payload.update({
        'exp': expires_in
    })
    jwt_token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return jwt_token


# 校验JWT token
def checkout_token(token):
    try:
        decode_token = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
    except (DecodeError, ExpiredSignatureError) as e:
        logger.warning(f"JWT decode error: {e}")
        return False
    else:
        decode_token.pop('exp')
        return decode_token


if __name__ == '__main__':
    import time
    token = generate_jwt_token({"name": 12}, 3)
    print(f"生成的token: {token}")

    time.sleep(2)
    print(f"token校验结果: {checkout_token(token)}")

posted @ 2024-07-27 17:00  CSMrDong  阅读(5)  评论(0编辑  收藏  举报