021-Python读取各种编码的csv文件

1: Python读取各种编码的csv文件

1.1 需求背景

  • 因为工作原因需要导出导入大量的CSV文件数据,这些CSV文件中存在大量的中文字符数据。但是不同数据来源的CSV格式文件的编码格式并不固定,为正确解析中文字符需要一个可以批量读取不同编码格式的CSV程序;

1.2 问题描述

  • 通常Python CSV库读取CSV文件经常遇到莫名其妙的编码错误异常,报错信息主要是unicode解码错误,具体报错信息如下:
  • UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb1 in position 0: invalid start byte

2: 解决方案

  • 中文编码规则主要有gbk、uft-8、utf-8-sig、GB2312、gb18030、iso-8859-1,为解析各种编码特别是涉及中文的CSV文件,防止解析出错出现中文乱码情况,需要造一个轮子(自定义函数)便于解决乱码问题;

2.1 解决方案1:一般方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import csv 
def read_csv(filename):
    #预设编码列表
    encodings = ['gbk','utf-8','utf-8-sig','GB2312','gb18030','iso-8859-1']
    #循环编码列表,直到正确解析返回解析后的数组data,否则返回false
    for e in encodings:
        data = []
        try:
            with open(filename, encoding=e) as f:
                reader = csv.reader(f)
                header = next(reader)
                # print(header)
                for row in reader:
                    data.append(row)
            print(filename,e)
            return data
        except: 
            print(filename,e)
    return False

2.2 解决方案2:引入chardet模块来读取文件的编码格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import csv
import chardet

def read_csv(filename):
    # 使用chardet检测文件编码
    with open(filename, 'rb') as f:
        result = chardet.detect(f.read())
        encoding = result['encoding']
    # 使用检测到的编码读取CSV文件
    with open(filename, encoding=encoding) as f:
        reader = csv.reader(f)
        header = next(reader)
        data = [row for row in reader]
    print(filename, encoding)
    return data

2.3 解决方案3:使用pandas库的read_csv函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import chardet
import pandas as pd

def read_csv_pd(filename, sep=','): 
    # 1. 使用 'rb' 模式打开文件并读取前几行/字节进行编码检测
    # chardet 有时可能不可靠,但常用于猜测编码
    try:
        with open(filename, 'rb') as f:
            # 读取部分文件内容以提高检测速度
            raw_data = f.read(4096) 
            encode = chardet.detect(raw_data)['encoding']
            
            # 确保检测到的编码名称是小写字符串
            if encode:
                encode = encode.lower()
            else:
                print(f"未能检测到文件编码: {filename}")
                return False
    except Exception as e:
        print(f"文件读取或编码检测失败: {e}")
        return False

    # 2. 尝试使用检测到的编码读取文件
    try:
        # **修复点 1:变量和编码名称统一使用字符串**
        if encode in ['utf-8', 'ascii']: # chardet 有时可能返回 ascii
            # 尝试最常见的 UTF-8
            data = pd.read_csv(filename, encoding='utf-8', sep=sep)
        
        elif 'gb' in encode:
            # **修复点 2:简化中文编码处理逻辑**
            # GB2312, GBK, GB18030 兼容性:从兼容性最好的 GB18030 开始尝试
            try:
                data = pd.read_csv(filename, encoding='gb18030', sep=sep)
            except UnicodeDecodeError:
                # 如果 GB18030 失败,尝试其他检测到的编码,例如原始的 'gbk'
                data = pd.read_csv(filename, encoding='gbk', sep=sep)
        
        elif encode == 'utf-8-sig':
            data = pd.read_csv(filename, encoding='utf-8-sig', sep=sep)
        
        elif encode == 'iso-8859-1':
            # **修复点 3:修正编码名称和参数拼写**
            data = pd.read_csv(filename, encoding='iso-8859-1', sep=sep)
            
        else:
            # 对于其他未知编码,直接用检测到的编码尝试读取
            print(f"尝试使用检测到的未知编码: {encode}")
            data = pd.read_csv(filename, encoding=encode, sep=sep)
            
    except UnicodeDecodeError as e:
        # **修复点 4:通用错误处理**
        print(f"读取文件 {filename} 时发生编码错误: {e}")
        # **解决方案:忽略错误**
        try:
             data = pd.read_csv(filename, encoding=encode, sep=sep, errors='ignore') 
        except Exception as final_e:
             print(f"无法使用任何方式读取文件: {final_e}")
             return False

    except Exception as e:
        print(f"读取文件时发生未知错误: {e}")
        return False
        
    return data

041-Linux systemd & journalctl

1: 使用 systemd 管理服务

1.1 systemd 介绍

  • systemctl是Linux中管理systemd服务的核心工具,通过统一命令集实现服务启动、停止、重启、状态查看及开机自启设置,支持并行启动、依赖管理与日志集中查看,显著提升服务管理效率

1.2 systemd 服务文件配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# /etc/systemd/system/app.service # xxx.ini格式
#
[Unit]
Description=My Custom Service         # 服务描述
Documentation=https://your-docs-link.com (可选)
After=network.target                  # 服务启动顺序(在 network.target 之后启动)
Requires=network-online.target        # 强制依赖(服务启动前必须满足)
Wants=another-service.target          # 弱依赖(服务启动前尝试启动 another-service)
Conflicts=httpd.service               # 冲突服务(与 httpd 互斥)
————————————————
版权声明:本文为CSDN博主「岚叔运维」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lmzf2011/article/details/150656143
[Service]
Type=simple # [simple, forking, oneshot, notify, dbus, idle]
User=gitlab
Group=gitlab
WorkingDirectory=/opt/gitlab/bin/
ExecStart=/opt/gitlab/bin/gitlab-ctl start  # 必须使用绝对路径来指定启动命令或脚本
ExecStop=/opt/gitlab/bin/gitlab-ctl stop
Restart=on-failure # [no,on-failure,on-abnormal,alaways]
RestartSec=5s
Environment=NODE_ENV=production # 直接设置环境变量
EnvironmentFile=/home/proj/app/config # 从指定文件加载环境变量,格式为 KEY=VAL,适合配置较多的情况
LimitNOFILE=65536 # 限制打开的文件数
LimitCPU=500% # 限制CPU使用率,infinity表示无限制
MemoryMax=8G

[Install]
WantedBy=multi-user.target # 表示当系统进入“多用户命令行模式”(标准服务器运行级别)时,该服务应被自动启动。这是实现开机自启的关键

1.3 systemd & journalclt 命令汇总

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
sudo apt install systemd # install 

sudo systemctl status gitlab # status
sudo systemctl start gitlab  # start service
sudo systemctl stop gitlab   # stop
sudo systemctl restart gitlab # restart
sudo systemctl enable gitlab  # enable 服务开机自启
sudo systemctl disable gitlab # disable 禁止服务开机自启
sudo systemctl reload gitlab # 优雅的重载gitlab配置

sudo systemctl daemon-reload # 重新加载systemd配置
sudo systemctl list-units --type=service  # 查看所有服务单元
sudo systemctl list-units --type=service --state=active # 查看所有活跃的服务单元
sudo systemctl list-units --type=service --state=inactive # 查看所有不活跃的服务单元
sudo systemctl list-units --type=service --state=failed # 查看所有失败的服务单元
sudo systemctl --failed  #查看失败的服务


sudo journalctl -u gitlab  # 查看gitlab日志
sudo journalctl -u gitlab -f # 查看gitlab日志,实时更新
sudo journalctl -u gitlab --since "1 hour ago" # 查看gitlab日志,最近一小时
sudo journalctl -b -u your-app.service # 查看your-app.service日志,最近一次启动
sudo journalctl -u your-app.service --since "2024-06-13 09:00:00" --until "2024-06-13 10:00:00"  # 按照时间范围查看your-app.service日志
sudo journalctl -p err -u your-app.service # 按日志级别过滤, 只显示错误级别(ERROR)及以上的日志,帮助快速定位问题


# 管理 journald 系统日志
journalctl --disk-usage # 检查当前日志占用的磁盘空间
sudo journalctl --vacuum-time=7d  # 清理日志,保留最近7天的日志
sudo journalctl --vacuum-size=100M # 清理日志,保留100M大小的日志
sudo journalctl --vacuum-files=10 # 清理日志,保留最近10个日志文件
sudo journalctl --vacuum-files=10 --vacuum-time=7d --vacuum-size=100M # 清理日志,保留最近10个日志文件,最近7天的日志,100M大小的日志

# 永久性配置:若要一劳永逸,可以编辑 /etc/systemd/journald.conf文件,设置限制
#  [Journal]
# SystemMaxUse=1G
# MaxRetentionSec=1week
# 运行 sudo systemctl restart systemd-journald 使配置生效

021-uv使用指南

uv使用指南

1: Introduction

  • uv 是一款用 Rust 开发的高性能 Python 包管理器,旨在统一和简化 Python 的包管理、项目管理和环境管理流程,
  • 核心功能对比
功能领域传统工具uv对应命令备注
包管理pip, pip-toolsuv pip install
虚拟环境virtualenvuv venv
依赖锁定pip-compile + requirements.txt自动生成 uv.lock
Python版本管理pyenvuv python install

2:uv的优势

  • 极致的速度:得益于 Rust 的实现、并行网络请求和智能缓存,uv 在依赖解析和包安装速度上相比传统工具(如 pip 和 Poetry)有显著提升。
  • 功能全面集成:它将 Python 项目开发中常用的多种工具功能集成于一身,你不再需要频繁在 pip、virtualenv、pipx 等工具间切换。
  • 符合 Python 标准:uv 的项目管理基于 pyproject.toml 文件,遵循 PEP 621 标准,并支持生成跨平台的锁文件 uv.lock,保证了项目在不同环境下依赖的一致性。
  • uv 提供了更强大的依赖管理功能,可以自动解析和安装依赖,避免手动安装和管理依赖的麻烦。

3:安装与配置uv

3.1 安装uv

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 在某些计算集群或受限环境中,默认安装路径可能不合适。你可以通过设置环境变量 UV_INSTALL_DIR 来自定义安装目录,并通过 INSTALLER_NO_MODIFY_PATH=1 阻止其自动修改 shell 配置文件

# macOS 和 Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
choco install uv

# pip 
pip install uv

# 验证
uv --version

3.2 配置uv镜像源

1
2
3
4
5
# 项目级配置(推荐): 在项目的 pyproject.toml 文件中添加

[[tool.uv.index]]
url = 'https://mirrors.aliyun.com/pypi/simple'
default=true

4: uv核心功能与常用命令

4.1 项目管理与依赖管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# uv 可以帮你初始化项目、添加依赖并自动生成锁文件
# 1:初始化新项目
mkdir my_project && cd my_project
uv init #这会在当前目录生成一个基本的 pyproject.toml 文件
# 2:添加依赖
uv add requests          # 添加生产依赖
uv add pytest --dev      # 添加开发依赖
uv add -r requirements.txt # 从现有requirements.txt文件导入
# 这些命令会自动更新 pyproject.toml 并安装相应的包

# 3:同步依赖与运行项目
uv sync                  # 安装所有依赖并创建/更新锁文件
uv run python main.py    # 在项目专属环境中运行命令,无需手动激活虚拟环境
uv run python main.py --lock

4.2 虚拟环境管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1: 创建虚拟环境
uv venv                 # 在当前目录创建 .venv 虚拟环境
uv venv --python 3.12 my_env  # 创建指定Python版本和名称的环境

# 2: 列出所有虚拟环境
uv venv list

# 3: 激活虚拟环境
source .venv/bin/activate
.venv\Scripts\activate # Windows
uv venv activate my_env

# 4: 删除虚拟环境
uv venv delete my_env

4.3 工具管理(uv tool)

  • 类似于 pipx,uv tool 用于在隔离环境中安装和运行 Python 命令行工具
1
2
3
uv tool install ruff              # 安装代码检查工具 ruff
uv tool run ruff check .          # 直接运行工具
uv tool run ruff check .          # 也可使用别名 uvx

4.4 Python版本管理

4.4.1 安装和使用特定 Python 版本

  • uv 可以直接安装和管理多个版本的 Python 解释器,替代 pyenv。
1
2
3
uv python install 3.12    # 安装 Python 3.12
uv python list            # 列出已安装的 Python 版本
uv venv --python 3.12     # 使用指定版本创建虚拟环境

4.4.2 uv进阶使用技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 依赖数分析
uv tree

# 依赖锁定与同步
uv lock --upgrade-package numpy  # 仅升级指定包
uv sync --force                  # 强制同步环境,即使本地有修改

# 单文件脚本依赖管理: uv 支持基于 PEP 723 标准的单文件 Python 脚本,可以在脚本内部通过注释块声明依赖

uv init --script myscript.py  # 初始化一个脚本文件
uv add --script myscript.py rich #为脚本添加依赖,uv 会自动在脚本顶部生成依赖元数据
uv run myscript.py 运行脚本,uv #会自动处理这些依赖

01-005-lppKDB脚本

1:KDB 简介

2:KDB 安装

3:KDB+/Q 脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
 wans//utf-8 ------------kdb+ Common Statments ---kdb+常用语句----------

// kdb+ tutorials https://www.timestored.com/kdb-guides/?utm_source=qstudio&utm_medium=app&utm_campaign=qstudio
// kdb+ keywords  https://www.timestored.com/kdb-guides/kdb-keyword-reference

// list
y:(`cpp;`python;`go;`java) // list of four symbols, y is variable name
y

y:(`cpp`python`go`java) // list of four symbols, Note not semicolon
y

y2:("symbol may have interior blanks") // string
y2
type y2

// string convert symbol  将字符串转换为符号
s: "hello world"
symbolList: `$(" " vs s) // use vs 函数 ; $ 是类型转换操作符
symbolList
symbolList[1]

// table  https://www.wenjiangs.com/doc/p4btamfe
// 创建表
userTable:([]name:(`cpp`go`python);[]amount:(100,200,300);[]price:(10.5,95.5,60.5))
// 获取表信息
cols userTable
userTable.name
meta userTable // 展示table meta信息: 列名, 列类型,其他信息

val:flip`name`id!(`John`Jenny`Jonathan;9 18 27) // flip list转为列,也就是list转为table

val:flip`name`id!(`John`Jenny`Jonathan;9 18 27)
idTable:flip (enlist`eid)!enlist 99 198 297
newTable: idTable ! val //
// select
select from responsev4 where sym like "*zjzg_test_1*", qid like "*6dc47d9e-b6bd-47e5-aa59-8ac4dc701776*",entrustno=100016,status=1

select count qid from responsev4  where sym like "*zjzg_test_1*"

select from responsev4 where sym like "*zjzg_test_1*",securityID like "*600570*"

select from responsev4 where qid like "*b9edeee9-88e3-4b45-8231-da6d1136a34d*", entrustno = 10009,status = 2

select from responsev4 where sym like "*zjzg_test_1*", entrustno = 10003

select count qid from responsev4 where sym like "*zjzg_test_1*"

0!select sym,qid,entrustno from responsev4Qid where sym like "*zjzg_test_1*", entrustno = 10001

`open xdesc select from tableB // by open filed sort

`resptime xdesc (select  from responsev4Qid where sym like "*2668i*") // sort xasc, xdesc

// 查询聚合语句
select count(qid) from responsev4 where sym like "*9878*"   , status in(2,3);
select sum(cumqty) from responsev4 where sym like "*9878*"   , status in(2,3);

// delete
delete columns from table  / delete columns
delete from table where clause
delete from `responsev4Qid where qid like "delete_test_1"
delete from `assetTab  where i>=0  // 清空表
drop `Stu    // 删除表

// insert
`trade insert (`hsbc`apple;302.0 730.40;3020 3012;09:30:17.00409:15:00.000)
insert[`cancelTab;enlist `sym`qid`entrustno


// update 更新语句
update entrustno: 10005 from (0!select from responsev4Qid where qid like "*c3c63d45-4c9c-4de6-8276-86b9a516c924*")
upd0['responsev4;update status:5i, cumqty:0i, avgpx:0.0f, note: `DFD from ( 0!select from responsev4Qid where sym like "*guangda test*")]


// insert 插入语句
insert[`cancelTab;enlist `sym`qid`entrustno!(`zjzg_test_1,`t7912h9sh3, 10004)];

// 发布数据.u.pub
u.pub[`cancelTab;enlist`sym`qid`entrustno!(`zjzg_test_1 ,`t7912h9sh3, 10003)];


// kdb 撤单指令
cancelReq:{[q] t:select sym, qid, entrustno from (select from responsev4Qid where qid = q); upd0[`cancelTab;t];}
cancelReq[`$"a3dcb0ea-d26a-40d6-87b6-98a6b705fc4e"]

//  time
select from responsev4Qid where resptime.time>09:40:00 ,status=6, note like "*ACK*"

// group

// cancel order

cancelReq:{[q] t:select sym, qid, entrustno from (select from responsev4Qid where  qid like "*dca382c8*"  ); upd0[`cancelTab;t];}

cancelReq[`$"527ed110-6e4a-11ef-8997-00163e313bec"]


// limit stat
select count qid by `second$time from request where sym like "*1099*"
select count qid by `second$resptime  from responsev4Qid

// not sym like "*gs2828i*"

//
(1) 查看状态不正常的订单,比如status=0,3
(2) qmt查看账户交易是否正常,GT停用启用该账户:
(3) 重启下游交易程序,并查看订单状态是否正常回报;
(4) 没回报的话,手动修改订单状态
(5) 没有报出去order,状态为3,置为5
(6) qmt成交,cim丢失了,先把该账户对应订单置为0,重启客户端,停用启用,在重启下游交易程序


//  统计
r: select sym,qid,time from request where sym=`mshw_ubs26_03
v: select  sym,qid,resptime,status from responsev4 where status=1, sym in (`mshw_ubs26_03), not null resptime
v_unkeyed: select  sym,qid,resptime,status from responsev4 where status=1, sym in (`mshw_ubs26_03)
v_keyed: `qid xkey v_unkeyed // 将右表 v_unkeyed 转换为以 qid 为键的键控表
jt: r ij v_keyed // lj, ij
select from jt  where not null resptime
select sym,qid,time,latency: resptime - time  from jt  where not null resptime
select sym,qid,time,latency_ms: 0.001 * (resptime - time)    from jt  where not null resptime

select sym,qid,time,latency: 1000000*(resptime - time) from jt  where not null resptime ,qid=`$"3fcac912-b078-11f0-b812-00163e313bec" //含有-, `$"string" 先转字符串再转 symbol

01-Docker部署go项目

Docker部署go项目

1: 准备 Go 项目

  • 确保 Go 项目已经编写完成,并且可以在本地正常运行。

  • 项目目录结构如下:

    1
    2
    3
    4
    5
    
    ├── main.go
    ├── go.mod
    ├── go.sum
    ├── Dockerfile
    ├── Makefile
    
    • main.go:Go 项目的入口文件。
    • go.mod:Go 项目的依赖管理文件。
    • go.sum:Go 项目的依赖管理文件。
    • Dockerfile:Dockerfile 文件,用于构建 Docker 镜像。
    • Makefile:Makefile 文件,用于简化构建过程。

2: 编写 Dockerfile

2.1 基础镜像

  • 在项目根目录下创建 Dockerfile 文件,内容如下:

010-update-alternatives安装gcc多版本

update-alternatives安装gcc多版本

1: 安装update-alternatives

  • 该工具属于dpkg软件包管理系统的核心组件,一般系统已预装。若提示命令不存在,可通过以下命令安装
  • 安装: sudo apt install dpkg
  • 验证: update-alternatives --help

2: ​添加旧版本软件源(针对gcc-4.8.5)

  • 由于Ubuntu 2024.04的默认源可能不包含gcc-4.8.5,需要手动添加旧版仓库:
1
2
sudo sed -i '$a deb http://archive.ubuntu.com/ubuntu/ xenial main universe' /etc/apt/sources.list
sudo apt update

3: 安装各版本GCC/G++

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 安装gcc/g++ 4.8.5(需依赖旧版源)
sudo apt install gcc-4.8 g++-4.8

# 安装gcc/g++ 7和9
sudo apt install gcc-7 g++-7 

#(默认源支持)
sudo apt install gcc-9 g++-9


# 如果apt 安装不了,手动下载安装
https://ftp.gnu.org/gnu/gcc/

4: 配置update-alternatives

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 添加gcc版本(优先级数值越大优先级越高)
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 40 \ 
--slave /usr/bin/g++ g++ /usr/bin/g++-4.8 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-4.8

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ 
--slave /usr/bin/g++ g++ /usr/bin/g++-7 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-7

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 \ 
--slave /usr/bin/g++ g++ /usr/bin/g++-9 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-9

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \ 
--slave /usr/bin/g++ g++ /usr/bin/g++-13 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-13

5: 切换默认版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 交互式选择版本
sudo update-alternatives --config gcc

# 输出示例:
There are 3 choices for the alternative gcc (providing /usr/bin/gcc).

  Selection    Path              Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gcc-9     90        auto mode
  1            /usr/bin/gcc-4.8   40        manual mode
  2            /usr/bin/gcc-7     70        manual mode
  3            /usr/bin/gcc-9     90        manual mode

Press <Enter> to keep the current choice[*], or type selection number: 

6: 版本验证

1
2
gcc --version  # 应显示当前选择的版本
g++ --version  # 应与gcc版本一致

020-pyenv管理python多版本

pyenv 管理 python 多版本

1:virtualenv

  • virtualenv 所要解决的是同一个库不同版本共存的兼容问题。例如项目 A 需要用到 requests 的 1.0 版本,项目 B 需要用到 requests 的 2.0 版本。如果不使用工具的话,一台机器只能安装其中一个版本,无法满足两个项目的需求。

019-文档转为markdown

将非 Markdown 格式的文档转换为 Markdown

1:介绍

  • 这是一个由微软开源的 Python 工具,可以将非 Markdown 格式的文档转换为 Markdown 格式,支持 PDF、PPT、Word 和 Excel 等多种文件类型,为文档的索引和文本分析提供了极大的便利。

017-pysftp SFTP Pool

pysftp SFTP Pool

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

import pysftp
import os
import sys
import time
import paramiko
from queue import Queue, Empty

# -----------------------------


from etc import config
import utils
from utils import logger_web
from utils import logger_lms as logger


# -----------------------------
broker=config.LMS["broker"]
notifier_list=config.Notifier
notifier_list2=config.Notifier2

# -----------------------------

class SFTPConnectionPool:
    """SFTP连接池"""
    def __init__(self,host,port,username,password,max_connections=20):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.max_connections = max_connections
        self.pool=Queue(maxsize=max_connections)
        self.cnopts = paramiko.SSHClient()
        self.cnopts.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        # self.cnopts.hostkeys = None
        self._init_pool()

    def _init_pool(self):
        for _ in range(self.max_connections):
            conn=self._create_conn()
            if conn:
                self.pool.put(conn)
            else:
                logger.error("初始化SFTP连接池失败")

    def _create_conn(self):
        transport=paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.password)
        return paramiko.SFTPClient.from_transport(transport)

    def get_conn(self):
        try:
            return self.pool.get(timeout=10)
        except Empty:
            logger.error("SFTPConnPool no avaiable connections ...")

    def release_conn(self,conn):
        if conn:
            self.pool.put(conn) # 将对象放回连接池
        else:
            logger.warning("SFTPConnPool attempted to release a None connection")

    def close_all_conn(self):
        while not self.pool.empty():
            conn=self.pool.get()
            if conn:
                conn.close()
            else:
                logger.warning("SFTPConnPool Found a None connection in the pool")




class SFTPClient:
    def __init__(self, host,port, username, password, upload_dir, download_dir):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.upload_dir = upload_dir
        self.download_dir = download_dir
        self.max_retries = 3
        self.retry_delay = 5
        self.pool=SFTPConnectionPool(host,port,username,password)

    def ensure_connected(self):
        """确保SFTP连接是活跃的,如果断开则重连"""
        sftp=self.pool.get_conn()
        if not sftp:
            return False
        try:
            sftp.listdir()
            return True
        except Exception as e:
            logger.error("SFTPClient connection check failed, error={}".format(e))
            self.pool.release_conn(sftp)
            time.sleep(3)
            return False

    def upload_file(self, local_file):
        logger.info(f"SFTPClient start upload file={local_file} to remote_path={self.upload_dir}")
        if not os.path.exists(local_file):
            logger.error("SFTPClient UploadFile ={} does not exist".format(local_file))
            return False
        for attempt in range(self.max_retries):
            try:
                if not self.ensure_connected():
                    continue
                sftp=self.pool.get_conn()
                sftp.chdir(self.upload_dir)
                filename = os.path.basename(local_file)
                sftp.put(local_file) # /root/project/lms_jpm/static/files/xxx_request.csv
                logger.info(f"SFTPClient Successfully upload file={filename} to remote_path={self.upload_dir}")
                return True
            except Exception as e:
                logger.error(f"SFTPClient Failed upload file={local_file}, attempt {attempt+1} error={e}")
                if attempt < self.max_retries-1:
                    logger.error(f"SFTPClient Retrying upload file={local_file}, seconds ...")
                    time.sleep(self.retry_delay)
        return False

    def search_file(self, file):
        logger.info(f"SFTPClient start search remote_path={self.download_dir}/{file} ...")
        for attempt in range(self.max_retries):
            try:
                self.ensure_connected()
                self.conn.chdir(self.download_dir)
                files = self.conn.listdir()
                # logger.info(f"----------listdir-----------------{self.download_dir}---------------------------")
                # logger.info(f"-------------------------{file}---------------------{attempt+1}--------------------")
                # logger.info(files)
                # logger.info("--------------------------------------------------------------------")
                # logger.info("--------------------------------------------------------------------")
                # logger.info("--------------------------------------------------------------------")
                if file in files:
                    # logger.info(f"SFTPClient search remote_path={self.download_dir}/{file} ****** find ******")
                    return True
                return False
            except Exception as e:
                logger.error(f"SFTPClient search failed remote_path={self.download_dir}/{file}"+
                    f", attempt= {attempt+1}, error={e}")
                if attempt < self.max_retries-1:
                    time.sleep(self.retry_delay)
        return False

    def download_file(self, file, local_path):
        logger.info(f"SFTPClient start download remote_file={file} to {local_path}")
        for attempt in range(self.max_retries):
            try:
                if not self.ensure_connected():
                    continue
                sftp=self.pool.get_conn()
                sftp.chdir(self.download_dir)
                sftp.get(file, local_path)
                logger.info(f"SFTPClient Successfully downloaded {file} to {local_path}")
                return True
            except Exception as e:
                logger.error(f"SFTPClient Download failed remote_file={file}, attempt= {attempt+1}, error={e}")
                if attempt < self.max_retries-1:
                    time.sleep(self.retry_delay)
                    if os.path.exists(f"{local_path}/{file}"):
                        os.remove(f"{local_path}/{file}")  # 删除可能的部分下载文件
        return False