一、Python 生态中的实用工具概述
Python 作为一门功能强大且应用广泛的编程语言,凭借其简洁易读的语法和丰富的第三方库,在众多领域发挥着重要作用。无论是 Web 开发领域的 Django、Flask 框架,还是数据分析与科学领域的 NumPy、Pandas 库,亦或是机器学习与人工智能领域的 TensorFlow、PyTorch,Python 都展现出了强大的适应性和扩展性。此外,在桌面自动化、爬虫脚本、金融量化交易以及教育研究等领域,Python 也有着广泛的应用。

在日常的 Python 编程中,我们经常会遇到处理字符串显示的场景。例如,在命令行界面中,我们需要确保文本能够整齐地对齐显示;在开发终端应用时,我们需要准确计算字符串在终端中所占的宽度。然而,由于不同字符在终端中显示的宽度可能不同,这给我们的字符串处理带来了一定的挑战。为了解决这个问题,Python 提供了 wcwidth
库,它可以帮助我们准确计算字符串在终端中的显示宽度。
二、wcwidth 库概述
2.1 用途
wcwidth
库的主要用途是计算 Unicode 字符串在终端中的显示宽度。在终端中,不同的字符可能占用不同的宽度。例如,ASCII 字符通常占用 1 个字符宽度,而中文、日文、韩文等东亚字符通常占用 2 个字符宽度。此外,还有一些特殊字符,如控制字符、零宽度字符等,它们在终端中不占用宽度或占用特殊的宽度。wcwidth
库可以准确地识别这些字符,并计算出它们在终端中实际占用的宽度,从而帮助我们实现文本的整齐对齐和格式化显示。
2.2 工作原理
wcwidth
库的工作原理基于 Unicode 标准中的 East Asian Width (EAW) 属性。Unicode 为每个字符定义了一个 EAW 属性,该属性决定了字符在终端中的显示宽度。wcwidth
库通过查询字符的 EAW 属性来确定其显示宽度,并根据以下规则进行计算:
- 对于 EAW 属性为 “F”(Fullwidth)、”W”(Wide)或 “A”(Ambiguous)的字符,显示宽度为 2。
- 对于 EAW 属性为 “H”(Halfwidth)、”Na”(Narrow)或 “Neutral” 的字符,显示宽度为 1。
- 对于控制字符(如换行符、制表符等),显示宽度为 0。
- 对于其他特殊字符,如零宽度空格、组合字符等,显示宽度也为 0。
2.3 优缺点
优点:
- 准确性高:
wcwidth
库基于最新的 Unicode 标准,能够准确识别大多数字符的显示宽度。 - 跨平台兼容:该库在不同的操作系统和终端环境中都能保持一致的计算结果。
- 使用简单:提供了简洁的 API,方便开发者集成到自己的项目中。
缺点:
- 依赖 Unicode 标准:由于该库依赖于 Unicode 的 EAW 属性,对于一些较新的字符或特殊字符,可能会存在识别不准确的情况。
- 无法处理复杂布局:该库只能计算单个字符的显示宽度,对于一些复杂的文本布局(如表格、对齐等),可能需要结合其他库一起使用。
2.4 License 类型
wcwidth
库采用 MIT License,这是一种非常宽松的开源许可证。使用该库时,用户可以自由地使用、修改和分发代码,只需保留原有的版权声明和许可声明即可。这使得 wcwidth
库在商业项目和开源项目中都得到了广泛的应用。
三、wcwidth 库的基本使用
3.1 安装
在使用 wcwidth
库之前,我们需要先安装它。可以使用 pip 来安装:
pip install wcwidth
3.2 基本 API
wcwidth
库提供了两个主要的函数:
wcwidth(c)
:计算单个 Unicode 字符的显示宽度。wcswidth(s, n=None)
:计算 Unicode 字符串的前 n 个字符的显示宽度。如果 n 为 None,则计算整个字符串的显示宽度。
3.3 简单示例
下面是一个简单的示例,展示了如何使用 wcwidth
库计算不同字符的显示宽度:
import wcwidth
# 计算单个字符的显示宽度
print(wcwidth.wcwidth('A')) # 输出: 1
print(wcwidth.wcwidth('中')) # 输出: 2
print(wcwidth.wcwidth('\t')) # 输出: 0
print(wcwidth.wcwidth('\u200B')) # 零宽度空格,输出: 0
# 计算字符串的显示宽度
print(wcwidth.wcswidth('Hello')) # 输出: 5
print(wcwidth.wcswidth('你好')) # 输出: 4
print(wcwidth.wcswidth('Hello 你好')) # 输出: 10
在这个示例中,我们首先导入了 wcwidth
库。然后使用 wcwidth
函数计算了单个字符的显示宽度,可以看到 ASCII 字符 ‘A’ 的宽度为 1,中文字符 ‘中’ 的宽度为 2,制表符 ‘\t’ 和零宽度空格 ‘\u200B’ 的宽度为 0。接着使用 wcswidth
函数计算了字符串的显示宽度,对于混合了 ASCII 字符和中文字符的字符串,wcswidth
能够正确计算出其总宽度。
3.4 处理特殊字符
wcwidth
库能够正确处理各种特殊字符,包括控制字符、组合字符等。下面是一些示例:
import wcwidth
# 处理控制字符
print(wcwidth.wcwidth('\n')) # 换行符,输出: 0
print(wcwidth.wcwidth('\r')) # 回车符,输出: 0
print(wcwidth.wcwidth('\x1b')) # ESC 字符,输出: 0
# 处理组合字符
print(wcwidth.wcwidth('\u0301')) # 组合重音符号,输出: 0
print(wcwidth.wcswidth('e\u0301')) # "é" (e + 组合重音符号),输出: 1
# 处理表情符号
print(wcwidth.wcwidth('😀')) # 笑脸表情,输出: 2
print(wcwidth.wcswidth('Hello 😀')) # 输出: 8
在这个示例中,我们展示了 wcwidth
库对控制字符、组合字符和表情符号的处理。可以看到,控制字符和组合字符的宽度为 0,而表情符号的宽度为 2。对于组合字符,wcwidth
能够正确计算出它们组合后的显示宽度。
四、wcwidth 库的进阶应用
4.1 文本对齐
在命令行界面中,我们经常需要将文本对齐显示。使用 wcwidth
库可以帮助我们实现准确的文本对齐,无论文本中包含何种字符。下面是一个示例:
import wcwidth
def align_text(text, width, align='left'):
"""根据显示宽度对齐文本"""
text_width = wcwidth.wcswidth(text)
if text_width >= width:
return text[:width]
padding = width - text_width
if align == 'left':
return text + ' ' * padding
elif align == 'right':
return ' ' * padding + text
elif align == 'center':
left_padding = padding // 2
right_padding = padding - left_padding
return ' ' * left_padding + text + ' ' * right_padding
return text
# 示例数据
data = [
('Name', 'Age', 'Country'),
('Alice', 25, 'USA'),
('鲍勃', 30, '中国'),
('佐藤', 28, '日本'),
('Élise', 22, 'France'),
]
# 计算每列的最大宽度
max_widths = [0, 0, 0]
for row in data:
for i, cell in enumerate(row):
cell_width = wcwidth.wcswidth(str(cell))
if cell_width > max_widths[i]:
max_widths[i] = cell_width
# 增加一些边距
max_widths = [w + 2 for w in max_widths]
# 打印表格
for row in data:
aligned_row = [
align_text(str(cell), width, 'left')
for cell, width in zip(row, max_widths)
]
print('|'.join(aligned_row))
在这个示例中,我们定义了一个 align_text
函数,它接受文本、目标宽度和对齐方式作为参数,返回对齐后的文本。然后我们使用这个函数来对齐一个表格中的数据。通过计算每个单元格的显示宽度,并根据最大宽度进行对齐,我们确保了表格在终端中能够整齐地显示,无论单元格中包含的是 ASCII 字符、中文、日文还是其他特殊字符。
4.2 截断长文本
在某些情况下,我们需要截断长文本以适应特定的显示宽度。使用 wcwidth
库可以帮助我们实现基于显示宽度的文本截断,确保截断后的文本不会出现乱码或显示异常。下面是一个示例:
import wcwidth
def truncate_text(text, width, ellipsis='...'):
"""根据显示宽度截断文本,并在末尾添加省略号"""
if not text:
return ''
ellipsis_width = wcwidth.wcswidth(ellipsis)
if width <= ellipsis_width:
return ellipsis[:width]
current_width = 0
truncated = []
for char in text:
char_width = wcwidth.wcwidth(char)
if char_width < 0:
char_width = 0
if current_width + char_width <= width - ellipsis_width:
truncated.append(char)
current_width += char_width
else:
break
# 如果截断后的文本长度小于原文本长度,添加省略号
if len(truncated) < len(text):
truncated.extend(ellipsis)
return ''.join(truncated)
# 示例
text = "这是一段包含中文、English和特殊字符😀的测试文本。"
width = 20
print(truncate_text(text, width)) # 输出: "这是一段包含中文、Eng..."
在这个示例中,我们定义了一个 truncate_text
函数,它接受文本、目标宽度和省略号字符串作为参数,返回截断后的文本。函数会遍历文本中的每个字符,累加其显示宽度,当达到目标宽度减去省略号的宽度时,停止遍历并添加省略号。这样可以确保截断后的文本在终端中显示时不会超出指定的宽度,并且能够正确显示省略号。
4.3 构建命令行界面
wcwidth
库在构建命令行界面(CLI)时非常有用。下面是一个使用 wcwidth
库构建的简单命令行进度条示例:
import wcwidth
import time
def progress_bar(progress, total, width=50):
"""显示进度条"""
if total == 0:
percent = 100
else:
percent = min(100, int(progress * 100 / total))
# 计算进度条的填充部分和空白部分的宽度
fill_width = int(width * percent / 100)
empty_width = width - fill_width
# 构建进度条
fill = '█' * fill_width # 使用全角方块字符
empty = ' ' * empty_width
# 计算百分比文本的显示宽度
percent_text = f"{percent}%"
percent_width = wcwidth.wcswidth(percent_text)
# 确保进度条总宽度正确
bar_width = wcwidth.wcswidth(fill + empty)
if bar_width != width:
diff = width - bar_width
if diff > 0:
empty += ' ' * diff
else:
empty = empty[:diff]
# 构建完整的进度条字符串
bar = f"[{fill}{empty}] {percent_text}"
# 打印进度条(覆盖当前行)
print(f"\r{bar}", end='', flush=True)
# 示例使用
total = 100
for i in range(total + 1):
progress_bar(i, total)
time.sleep(0.05)
print() # 换行
在这个示例中,我们定义了一个 progress_bar
函数,它接受当前进度、总进度和进度条宽度作为参数,显示一个美观的进度条。通过使用 wcwidth
库计算字符的显示宽度,我们确保了进度条在终端中能够正确显示,无论终端使用何种字体或字符集。进度条会动态更新,显示当前的完成百分比。
4.4 处理多语言文本
wcwidth
库能够处理各种语言的文本,包括但不限于中文、日文、韩文、泰文、阿拉伯文等。下面是一个示例,展示了如何使用 wcwidth
库处理多语言文本的对齐:
import wcwidth
def print_multilingual_table():
"""打印多语言文本对齐表格"""
data = [
("English", "中文", "日本語", "한국어", "ไทย", "العربية"),
("Hello", "你好", "こんにちは", "안녕하세요", "สวัสดี", "مرحبًا"),
("World", "世界", "世界", "세계", "โลก", "عالم"),
("Python", "蟒蛇", "パイソン", "파이썬", "ไพธอน", "بايثون"),
]
# 计算每列的最大宽度
max_widths = [0] * len(data[0])
for row in data:
for i, cell in enumerate(row):
cell_width = wcwidth.wcswidth(str(cell))
if cell_width > max_widths[i]:
max_widths[i] = cell_width
# 增加一些边距
max_widths = [w + 2 for w in max_widths]
# 打印表格
for row in data:
aligned_row = []
for i, cell in enumerate(row):
# 右对齐阿拉伯文本,左对齐其他文本
align = 'right' if i == 5 else 'left'
aligned_cell = _align_cell(str(cell), max_widths[i], align)
aligned_row.append(aligned_cell)
print('|'.join(aligned_row))
def _align_cell(text, width, align='left'):
"""根据对齐方式对齐单元格文本"""
text_width = wcwidth.wcswidth(text)
padding = width - text_width
if align == 'left':
return text + ' ' * padding
elif align == 'right':
return ' ' * padding + text
else:
return text
print_multilingual_table()
在这个示例中,我们创建了一个包含多种语言文本的表格,并使用 wcwidth
库确保表格在终端中能够正确对齐显示。对于阿拉伯文本,我们使用右对齐方式,而对于其他语言的文本,我们使用左对齐方式。通过这种方式,我们可以在终端中创建美观、整齐的多语言表格。
五、结合实际案例的总结
5.1 案例:开发一个命令行工具
假设我们正在开发一个命令行工具,需要在终端中显示各种信息,包括表格、进度条等。wcwidth
库可以帮助我们确保这些信息在终端中能够正确对齐和显示。下面是一个示例:
import wcwidth
import time
class CommandLineTool:
"""命令行工具示例"""
def __init__(self):
self.table_data = [
("ID", "名称", "状态", "进度"),
(1, "项目A", "进行中", 75),
(2, "项目B", "已完成", 100),
(3, "项目C", "计划中", 0),
(4, "项目D😀", "进行中", 45),
]
def display_table(self):
"""显示表格"""
print("项目进度表:")
# 计算每列的最大宽度
max_widths = [0] * len(self.table_data[0])
for row in self.table_data:
for i, cell in enumerate(row):
cell_width = wcwidth.wcswidth(str(cell))
if cell_width > max_widths[i]:
max_widths[i] = cell_width
# 增加一些边距
max_widths = [w + 2 for w in max_widths]
# 打印表头
header = self.table_data[0]
aligned_header = [
self._align_text(str(cell), width, 'center')
for cell, width in zip(header, max_widths)
]
print('+' + '+'.join(['-' * width for width in max_widths]) + '+')
print('|' + '|'.join(aligned_header) + '|')
print('+' + '+'.join(['-' * width for width in max_widths]) + '+')
# 打印数据行
for row in self.table_data[1:]:
cells = list(row)
# 将进度转换为进度条
progress = cells[3]
progress_bar = self._get_progress_bar(progress, 10)
cells[3] = progress_bar
aligned_cells = [
self._align_text(str(cell), width, 'left')
for cell, width in zip(cells, max_widths)
]
print('|' + '|'.join(aligned_cells) + '|')
print('+' + '+'.join(['-' * width for width in max_widths]) + '+')
def _align_text(self, text, width, align='left'):
"""根据显示宽度对齐文本"""
text_width = wcwidth.wcswidth(text)
if text_width >= width:
return text[:width]
padding = width - text_width
if align == 'left':
return text + ' ' * padding
elif align == 'right':
return ' ' * padding + text
elif align == 'center':
left_padding = padding // 2
right_padding = padding - left_padding
return ' ' * left_padding + text + ' ' * right_padding
return text
def _get_progress_bar(self, progress, width):
"""生成进度条字符串"""
if progress < 0:
progress = 0
if progress > 100:
progress = 100
filled_width = int(width * progress / 100)
empty_width = width - filled_width
filled = '█' * filled_width # 全角方块字符
empty = ' ' * empty_width
return f"[{filled}{empty}] {progress}%"
def run(self):
"""运行工具"""
self.display_table()
# 模拟一个长时间运行的任务
print("\n正在执行任务...")
total = 100
for i in range(total + 1):
self._update_progress(i, total)
time.sleep(0.05)
print("\n任务完成!")
def _update_progress(self, progress, total):
"""更新进度显示"""
bar_width = 50
percent = min(100, int(progress * 100 / total))
filled_width = int(bar_width * percent / 100)
empty_width = bar_width - filled_width
filled = '█' * filled_width
empty = ' ' * empty_width
# 确保进度条宽度正确
bar_text = f"[{filled}{empty}] {percent}%"
bar_width_actual = wcwidth.wcswidth(bar_text)
if bar_width_actual != bar_width + 4: # 4 是方括号和百分比的宽度
diff = (bar_width + 4) - bar_width_actual
if diff > 0:
bar_text += ' ' * diff
else:
bar_text = bar_text[:diff]
print(f"\r{bar_text}", end='', flush=True)
# 使用示例
if __name__ == "__main__":
tool = CommandLineTool()
tool.run()
在这个示例中,我们开发了一个简单的命令行工具,它可以显示项目进度表格和进度条。通过使用 wcwidth
库,我们确保了表格中的文本能够正确对齐,无论文本中包含何种字符。进度条也能够根据实际显示宽度正确显示,不会出现错位或显示不全的情况。
5.2 案例:开发一个终端文本编辑器
另一个实际案例是开发一个终端文本编辑器。在文本编辑器中,我们需要准确计算光标位置和文本显示,以确保用户输入和编辑操作的正确性。wcwidth
库可以帮助我们实现这一点。下面是一个简化的终端文本编辑器示例:
import wcwidth
import sys
import termios
import tty
class TerminalEditor:
"""简化的终端文本编辑器"""
def __init__(self):
self.text = [] # 文本内容,每行一个字符串
self.cursor_x = 0 # 光标x坐标
self.cursor_y = 0 # 光标y坐标
self.original_settings = None # 用于存储终端原始设置
def run(self):
"""运行编辑器"""
try:
# 保存终端原始设置
self.original_settings = termios.tcgetattr(sys.stdin)
# 设置终端为原始模式
tty.setraw(sys.stdin)
self._clear_screen()
self._draw_editor()
while True:
# 读取用户输入的一个字符
char = sys.stdin.read(1)
# 处理特殊字符
if char == '\x1b': # ESC 键
# 检查是否是方向键序列
next_char = sys.stdin.read(1)
if next_char == '[':
third_char = sys.stdin.read(1)
if third_char == 'A': # 上箭头
self._move_cursor_up()
elif third_char == 'B': # 下箭头
self._move_cursor_down()
elif third_char == 'C': # 右箭头
self._move_cursor_right()
elif third_char == 'D': # 左箭头
self._move_cursor_left()
else:
# 单独的 ESC 键,退出编辑器
break
elif char == '\n': # 换行
self._insert_newline()
elif char == '\x7f': # 退格键
self._delete_character()
else: # 普通字符
self._insert_character(char)
self._draw_editor()
finally:
# 恢复终端设置
if self.original_settings:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.original_settings)
# 移动光标到屏幕底部
print("\033[999;999H", end='')
def _clear_screen(self):
"""清除屏幕"""
print("\033[2J", end='') # 清除屏幕
print("\033[H", end='') # 移动光标到左上角
def _draw_editor(self):
"""绘制编辑器界面"""
self._clear_screen()
# 获取终端尺寸
try:
import fcntl
import struct
import termios
# 获取终端尺寸
h, w = struct.unpack('hh', fcntl.ioctl(0, termios.TIOCGWINSZ, '1234'))
except:
# 默认值,如果无法获取终端尺寸
h, w = 24, 80
# 绘制标题
title = "终端文本编辑器 - 使用 ESC 键退出"
print(f"\033[1;34m{title.center(w)}\033[0m")
print("-" * w)
# 绘制文本内容
visible_lines = h - 4 # 减去标题行、分隔线和状态栏
start_line = max(0, self.cursor_y - visible_lines + 1)
end_line = start_line + visible_lines
for i, line in enumerate(self.text[start_line:end_line]):
# 计算行号的宽度
line_num = i + start_line + 1
line_num_width = len(str(len(self.text) + 1))
# 计算行号显示
line_num_str = f"{line_num:>{line_num_width}} "
# 确保行不超过屏幕宽度
display_line = line
line_width = wcwidth.wcswidth(display_line)
if line_width > w - line_num_width - 2:
# 尝试截断行以适应屏幕
display_line = self._truncate_line(display_line, w - line_num_width - 2)
# 高亮显示当前行
if i + start_line == self.cursor_y:
print(f"\033[7m{line_num_str}{display_line}\033[0m")
else:
print(f"{line_num_str}{display_line}")
# 填充剩余行
for _ in range(visible_lines - len(self.text[start_line:end_line])):
print()
# 绘制状态栏
status_line = f"行: {self.cursor_y + 1}/{max(1, len(self.text))} 列: {self.cursor_x + 1}"
print("-" * w)
print(f"\033[1;37m{status_line.ljust(w)}\033[0m")
# 移动光标到正确位置
cursor_row = self.cursor_y - start_line + 2
cursor_col = self._calculate_display_column(self.text[self.cursor_y], self.cursor_x) + len(str(len(self.text) + 1)) + 1
print(f"\033[{cursor_row};{cursor_col}H", end='')
sys.stdout.flush()
def _truncate_line(self, line, width):
"""根据显示宽度截断行"""
current_width = 0
truncated = []
for char in line:
char_width = wcwidth.wcwidth(char)
if char_width < 0:
char_width = 0
if current_width + char_width <= width:
truncated.append(char)
current_width += char_width
else:
break
return ''.join(truncated)
def _calculate_display_column(self, line, x):
"""计算字符在屏幕上的显示列位置"""
display_width = 0
for i in range(x):
if i < len(line):
char_width = wcwidth.wcwidth(line[i])
if char_width < 0:
char_width = 0
display_width += char_width
return display_width
def _move_cursor_up(self):
"""向上移动光标"""
if self.cursor_y > 0:
self.cursor_y -= 1
# 确保光标不会超出当前行的长度
current_line_length = len(self.text[self.cursor_y])
if self.cursor_x > current_line_length:
self.cursor_x = current_line_length
def _move_cursor_down(self):
"""向下移动光标"""
if self.cursor_y < len(self.text) - 1:
self.cursor_y += 1
# 确保光标不会超出当前行的长度
current_line_length = len(self.text[self.cursor_y])
if self.cursor_x > current_line_length:
self.cursor_x = current_line_length
def _move_cursor_left(self):
"""向左移动光标"""
if self.cursor_x > 0:
self.cursor_x -= 1
def _move_cursor_right(self):
"""向右移动光标"""
current_line_length = len(self.text[self.cursor_y])
if self.cursor_x < current_line_length:
self.cursor_x += 1
def _insert_character(self, char):
"""插入字符"""
# 如果是第一行,确保有一个空行
if not self.text:
self.text.append('')
# 在当前位置插入字符
current_line = self.text[self.cursor_y]
new_line = current_line[:self.cursor_x] + char + current_line[self.cursor_x:]
self.text[self.cursor_y] = new_line
# 移动光标
self.cursor_x += 1
def _insert_newline(self):
"""插入换行"""
# 如果是第一行,确保有一个空行
if not self.text:
self.text.append('')
current_line = self.text[self.cursor_y]
# 分割当前行
line_before = current_line[:self.cursor_x]
line_after = current_line[self.cursor_x:]
# 更新文本
self.text[self.cursor_y] = line_before
self.text.insert(self.cursor_y + 1, line_after)
# 移动光标到新行的开头
self.cursor_y += 1
self.cursor_x = 0
def _delete_character(self):
"""删除字符"""
# 如果是第一行且光标在开头,不执行删除
if not self.text or (self.cursor_y == 0 and self.cursor_x == 0):
return
current_line = self.text[self.cursor_y]
if self.cursor_x > 0:
# 删除当前位置的前一个字符
new_line = current_line[:self.cursor_x - 1] + current_line[self.cursor_x:]
self.text[self.cursor_y] = new_line
self.cursor_x -= 1
else:
# 光标在行首,合并到上一行
previous_line = self.text[self.cursor_y - 1]
new_previous_line = previous_line + current_line
self.text[self.cursor_y - 1] = new_previous_line
self.text.pop(self.cursor_y)
self.cursor_y -= 1
self.cursor_x = len(previous_line)
# 使用示例
if __name__ == "__main__":
editor = TerminalEditor()
editor.run()
在这个示例中,我们开发了一个简单的终端文本编辑器。wcwidth
库在这个编辑器中起到了关键作用:
- 光标定位:通过计算每个字符的显示宽度,我们能够准确地定位光标在屏幕上的位置,确保光标总是出现在正确的字符位置上。
- 文本截断:当文本行长度超过屏幕宽度时,我们使用
wcwidth
库来截断文本,确保截断后的文本不会出现半个字符的情况。 - 行号显示:计算行号和文本内容的显示宽度,确保它们能够正确对齐。
- 状态栏信息:在状态栏中显示准确的行号和列号信息,这些信息是基于字符的显示宽度计算得出的。
这个示例展示了 wcwidth
库在开发复杂终端应用时的重要性和实用性。
六、相关资源
- Pypi地址:https://pypi.org/project/wcwidth
- Github地址:https://github.com/jquast/wcwidth
- 官方文档地址:https://wcwidth.readthedocs.io/en/latest/
通过这些资源,你可以了解更多关于 wcwidth
库的详细信息,包括最新版本的特性、API 文档以及社区贡献等。如果你在使用过程中遇到问题或有任何建议,也可以通过 Github 提交 issue 或 pull request。
关注我,每天分享一个实用的Python自动化工具。
