
1. 项目概述为什么用Python处理Excel是效率利器如果你每天还在手动复制粘贴Excel数据或者被各种复杂的公式和VBA搞得头大那今天这篇分享可能会帮你打开新世界的大门。我干了十多年数据分析从最初用鼠标点菜单到后来写VBA再到这几年全面转向Python最大的感受就是用Python处理Excel不是“会不会”的问题而是“早用早下班”的问题。这个项目标题“python对excel表格处理的一些小功能”听起来很朴实但背后涵盖的场景极其广泛——从简单的数据清洗、格式调整到复杂的多表合并、自动化报表生成甚至是结合文本分析和可视化Python都能帮你搞定。很多人觉得Python是“编程”离自己很远。其实不然尤其是处理Excel这种重复性高、规则性强的任务Python的pandas库就是为你量身定做的“瑞士军刀”。它让你能用几行代码完成以前需要几个小时甚至几天的手工操作。比如你手头有几十个分公司的销售报表需要汇总或者需要从一堆杂乱的数据里提取特定信息再或者要把分析结果自动画成图表插入报告这些“小功能”串联起来就是一套强大的自动化工作流。这篇文章我会抛开那些晦涩的理论直接带你上手实操。我会假设你有一些Python基础知道怎么装包、运行脚本但即使你是刚入门跟着步骤走也能复现。我们将围绕pandas这个核心并延伸到textblob做简单的文本情感分析最后用matplotlib把结果可视化出来。你会发现这些“小功能”组合起来能解决你工作中80%的Excel痛点。2. 核心工具链选型与配置工欲善其事必先利其器。在开始写代码之前我们得先把“厨房”收拾好。Python处理Excel的库有很多但99%的场景一个pandas就足够了。它底层依赖openpyxl或xlrd来读写Excel文件所以我们通常一起安装。2.1 环境搭建一步到位的安装方案我强烈建议你使用Anaconda来管理Python环境特别是对于数据分析工作。它集成了pandas、numpy、matplotlib等几乎所有你需要的科学计算包避免了令人头疼的依赖冲突。如果你已经安装了Python那么用pip安装也是完全可行的。打开你的命令行Windows上是CMD或PowerShellMac/Linux上是终端执行以下命令来安装核心包pip install pandas openpyxl matplotlib textblob这里解释一下每个包的作用pandas核心数据处理库我们所有对Excel的读写、清洗、计算都靠它。openpyxl用于读写.xlsx格式的Excel文件。pandas在读写Excel时会根据文件后缀自动调用它。matplotlibPython最基础的绘图库虽然样式不如seaborn等库美观但功能最全、最稳定画个折线图、柱状图绰绰有余。textblob一个简单的自然语言处理库我们用它来对Excel单元格中的文本进行快速的情感分析判断正面/负面情绪。安装它时会自动安装其依赖nltk。注意如果你在安装textblob时遇到网络问题可能是因为需要下载nltk的语料库。你可以先只安装pandas和matplotlibtextblob的部分我们后面再单独处理。安装完成后可以在Python交互环境里输入以下命令验证是否成功import pandas as pd print(pd.__version__) import matplotlib.pyplot as plt print(plt.__name__)如果没有报错并输出了版本号说明环境配置成功。2.2 为什么是Pandas与其他方案的对比你可能会问处理Excel不是有VBA吗为什么还要学Python这里我简单对比一下工具优点缺点适用场景Excel 手动/公式无需编程直观易上手适合简单、一次性的操作。重复劳动多容易出错处理大数据10万行卡顿复杂逻辑公式难维护。快速查看、简单计算、一次性报表。Excel VBA深度集成Excel可以操作所有Excel对象单元格格式、图表等功能强大。语言相对老旧调试不便代码不易复用和版本管理生态系统弱。需要与Excel界面深度交互的复杂自动化如自定义窗体、特定格式批量修改。Python Pandas代码简洁清晰数据处理能力极强百万行数据轻松应对有强大的科学计算和AI生态支持易于复用和团队协作。需要学习基础Python语法对于修改复杂单元格样式支持不如VBA直接。数据清洗、整合、分析、自动化报表生成、与数据库/网络API交互等。我的经验是凡是涉及“数据”本身的操作读、洗、算、存优先用pandas凡是涉及“表格样式和交互”的精细操作比如设置某个单元格的边框颜色VBA或openpyxl直接操作更合适。本文聚焦前者这也是最能提升效率的部分。3. 基础但高频的Excel处理功能实战让我们从最常见的任务开始。假设你有一个名为sales_data.xlsx的销售数据文件里面有一个Sheet1包含“日期”、“销售员”、“产品”、“销售额”、“客户评价”这几列。我们的任务就是处理它。3.1 数据的读取与探查你的第一行代码任何处理的第一步都是把数据读进来。pandas的read_excel函数非常强大。import pandas as pd # 1. 最基本读取读取第一个工作表 df pd.read_excel(sales_data.xlsx) # df是DataFrame的简称你可以把它理解成一个超级强大的Excel表格 print(df.head()) # 查看前5行相当于Excel里滚动看一下 print(df.shape) # 查看数据形状(行数 列数) print(df.info()) # 查看列名、数据类型和非空值数量这是了解数据全貌的关键 # 2. 指定工作表或区域 # 读取名为“2023年销售”的工作表 df_specific_sheet pd.read_excel(sales_data.xlsx, sheet_name2023年销售) # 只读取B2到F100这个区域 df_range pd.read_excel(sales_data.xlsx, usecolsB:F, skiprows1, nrows99) # skiprows跳过标题行 # 3. 处理常见读取问题 # 问题文件中有合并单元格读出来是NaN。 # 方案读取时填充或者读完后处理。这里用前向填充。 df_filled pd.read_excel(sales_data.xlsx).ffill()df.info()的输出是你第一个要仔细看的东西。它能告诉你哪一列是数字int64float64哪一列是文本object以及有没有缺失值。很多后续的错误都源于数据类型不对。3.2 数据清洗从混乱到规整读进来的数据很少是完美的清洗是必经之路。# 1. 处理缺失值 # 查看缺失情况 print(df.isnull().sum()) # 删除所有包含缺失值的行谨慎使用可能删掉很多数据 df_dropped df.dropna() # 用特定值填充比如销售额缺失填0评价缺失填“无” df_filled df.fillna({销售额: 0, 客户评价: 无}) # 用上一行的值填充对于时间序列数据常用 df_filled_forward df.fillna(methodffill) # 2. 处理重复值 # 查看重复行 duplicates df[df.duplicated()] print(f找到 {len(duplicates)} 条重复记录) # 删除完全重复的行 df_deduped df.drop_duplicates() # 基于特定列去重比如保留同一销售员最新日期的记录 df_deduped_subset df.sort_values(日期).drop_duplicates(subset[销售员], keeplast) # 3. 数据类型转换 # 常见的错误“销售额”列读成了字符串因为里面有“1000”这样的字符 df[销售额] df[销售额].astype(str).str.replace(, ).str.replace(,, ).astype(float) # “日期”列读成了字符串转换为日期类型 df[日期] pd.to_datetime(df[日期], format%Y/%m/%d, errorscoerce) # errorscoerce将转换失败的设为NaT时间戳的缺失值 # 查看转换结果 print(df.dtypes)实操心得pd.to_datetime的errors‘coerce’参数非常有用。原始数据里经常混着“2023-13-01”错误月份或“暂无”这样的脏数据直接转换会报错。用这个参数它会把这些无法转换的值变成NaT让你的程序能继续运行后续再统一处理这些异常值。3.3 数据筛选、排序与计算像写公式一样简单这部分是pandas的精华用好了能替代无数个VLOOKUP和SUMIFS。# 1. 筛选比Excel筛选器强大得多 # 筛选销售额大于10000的记录 df_high_sales df[df[销售额] 10000] # 多条件筛选销售员为“张三”且产品为“手机”的记录 df_zhangsan_phone df[(df[销售员] 张三) (df[产品] 手机)] # 模糊筛选客户评价中包含“满意”的记录 df_satisfied df[df[客户评价].str.contains(满意, naFalse)] # naFalse 忽略缺失值 # 2. 排序 # 按销售额降序排列 df_sorted df.sort_values(销售额, ascendingFalse) # 多级排序先按销售员升序再按销售额降序 df_sorted_multi df.sort_values([销售员, 销售额], ascending[True, False]) # 3. 分组聚合Excel数据透视表的功能 # 计算每个销售员的总销售额和平均销售额 summary df.groupby(销售员)[销售额].agg([sum, mean, count]).reset_index() summary.columns [销售员, 总销售额, 平均销售额, 订单数] # 重命名列 print(summary) # 4. 新增计算列 # 新增一列“销售额级别”大于1万为“高”否则为“低” df[销售额级别] df[销售额].apply(lambda x: 高 if x 10000 else 低) # 更复杂的计算结合多列 # 假设有“成本”列计算利润率 # df[利润率] (df[销售额] - df[成本]) / df[销售额]groupby是核心中的核心。它实现了SQL中的GROUP BY操作。agg函数可以传入多个聚合函数summeanmaxmincount等一次性完成多种计算。reset_index()是为了把分组键‘销售员’从索引变回普通列方便后续处理。3.4 多表合并与连接告别VLOOKUP当数据分布在多个工作表或多个文件时合并是刚需。# 假设我们有两个DataFrame df_orders pd.read_excel(orders.xlsx) # 订单表有“订单ID”“客户ID”“金额” df_customers pd.read_excel(customers.xlsx) # 客户表有“客户ID”“客户姓名”“城市” # 1. 类似VLOOKUP根据“客户ID”把客户姓名合并到订单表 df_merged pd.merge(df_orders, df_customers[[客户ID, 客户姓名]], on客户ID, howleft) # howleft 表示以左表df_orders为主保留所有订单找不到对应客户ID的姓名填NaN # 2. 上下拼接多个结构相同的表比如各月数据 df_jan pd.read_excel(sales_jan.xlsx) df_feb pd.read_excel(sales_feb.xlsx) df_whole_year pd.concat([df_jan, df_feb], ignore_indexTrue) # ignore_index重置索引 # 3. 更复杂的连接多键连接 # 如果需要用“客户ID”和“日期”两个字段才能唯一匹配 # df_merged_complex pd.merge(df1, df2, on[客户ID, 日期], howinner)merge的how参数有left、right、inner、outer四种分别对应左连接、右连接、内连接和全外连接。这是数据库里的概念理解它们对数据处理至关重要。简单说left保留左表所有行inner只保留两个表都匹配的行outer保留所有行不匹配的地方用NaN填充。4. 进阶功能文本分析与可视化基础数据处理熟练后我们可以玩点更花的把简单的表格数据变成更有洞察力的信息。4.1 使用TextBlob进行简单的文本情感分析“客户评价”列是文本我们可以用textblob快速判断评价是正面的还是负面的。这在分析用户反馈、客服记录时非常有用。首先确保textblob已安装并且下载了必要的语料库。# 首次使用可能需要下载语料库如果网络不通可以手动下载或跳过此步骤 import nltk nltk.download(punkt) # 分词器 nltk.download(averaged_perceptron_tagger) # 词性标注器 nltk.download(movie_reviews) # 情感分析模型依赖部分模型需要 nltk.download(stopwords)然后进行情感分析from textblob import TextBlob # 定义一个函数用于分析单条文本的情感极性 # 极性polarity在[-1 1]之间越接近1越正面越接近-1越负面。 def get_sentiment(text): if pd.isna(text): # 处理缺失值 return None blob TextBlob(str(text)) return blob.sentiment.polarity # 应用函数到“客户评价”列 df[情感极性] df[客户评价].apply(get_sentiment) # 根据极性打标签 def polarity_to_label(polarity): if polarity is None: return 未知 elif polarity 0.1: return 正面 elif polarity -0.1: return 负面 else: return 中性 df[情感标签] df[情感极性].apply(polarity_to_label) # 查看结果 print(df[[客户评价, 情感极性, 情感标签]].head(10)) # 可以结合分组聚合看看哪个销售员获得的正面评价多 sentiment_summary df.groupby(销售员)[情感标签].value_counts(normalizeTrue).unstack().fillna(0) print(sentiment_summary)注意事项TextBlob基于规则和简单机器学习对标准英文文本效果较好对中文支持有限需要先分词。对于中文情感分析更专业的做法是使用snownlp、jieba情感词典或者调用大模型的API。这里用TextBlob主要是展示一种将文本数据量化的思路。对于简单的中文评价如“很好”、“太差了”通过适当的预处理比如翻译成英文或使用简单关键词匹配也能起到一定作用。4.2 使用Matplotlib实现数据可视化数据最终要呈现给人看图表比数字更直观。我们用matplotlib把刚才分析的结果画出来。import matplotlib.pyplot as plt import numpy as np # 设置中文字体防止图表中文乱码根据你的系统调整字体路径 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, DejaVu Sans] # 指定默认字体 plt.rcParams[axes.unicode_minus] False # 解决负号‘-’显示为方块的问题 # 1. 绘制销售员总销售额的柱状图 fig axs plt.subplots(2 2 figsize(14 10)) # 创建2x2的子图 # 子图1销售员销售额排名 sales_by_salesman df.groupby(销售员)[销售额].sum().sort_values(ascendingFalse) axs[0 0].bar(sales_by_salesman.index sales_by_salesman.values colorskyblue) axs[0 0].set_title(销售员总销售额排名) axs[0 0].set_xlabel(销售员) axs[0 0].set_ylabel(总销售额) axs[0 0].tick_params(axisx, rotation45) # 旋转x轴标签 # 子图2产品销售额分布饼图 sales_by_product df.groupby(产品)[销售额].sum() axs[0 1].pie(sales_by_product.values labelssales_by_product.index autopct%1.1f%%, startangle90) axs[0 1].set_title(产品销售额占比) # 子图3情感标签分布饼图 sentiment_dist df[情感标签].value_counts() axs[1 0].pie(sentiment_dist.values labelssentiment_dist.index autopct%1.1f%%, colors[lightgreen, lightcoral, lightgrey]) axs[1 0].set_title(客户评价情感分布) # 子图4每日销售额趋势折线图 df[日期] pd.to_datetime(df[日期]) # 确保是日期类型 daily_sales df.groupby(df[日期].dt.date)[销售额].sum() # 按天聚合 axs[1 1].plot(daily_sales.index daily_sales.values markero, linestyle-) axs[1 1].set_title(每日销售额趋势) axs[1 1].set_xlabel(日期) axs[1 1].set_ylabel(销售额) axs[1 1].grid(True linestyle--, alpha0.7) fig.autofmt_xdate(rotation45) # 自动调整日期标签格式 plt.tight_layout() # 自动调整子图间距 plt.savefig(sales_analysis_report.png, dpi300 bbox_inchestight) # 保存高清图片 plt.show()这段代码生成了一个包含四个子图的仪表板一次性展示了销售额排名、产品构成、情感分布和趋势。plt.savefig可以把图表保存为图片方便插入PPT或Word报告。bbox_inches‘tight’参数可以自动裁剪掉图片周围多余的白边。4.3 将处理结果写回Excel分析完了总要把结果保存下来。pandas可以轻松写回Excel。# 1. 将清洗和分析后的DataFrame保存到新的Excel文件 with pd.ExcelWriter(sales_data_processed.xlsx, engineopenpyxl) as writer: df.to_excel(writer, sheet_name清洗后数据, indexFalse) # indexFalse不保存行索引 summary.to_excel(writer, sheet_name销售员汇总, indexFalse) sentiment_summary.to_excel(writer, sheet_name情感分析汇总) # 2. 更高级的写入自定义格式需要结合openpyxl from openpyxl import load_workbook from openpyxl.styles import Font Alignment Border Side # 先用pandas写入数据 with pd.ExcelWriter(sales_report_formatted.xlsx, engineopenpyxl) as writer: summary.to_excel(writer, sheet_name报告, indexFalse startrow1) # 从第2行开始写 # 再用openpyxl打开文件添加标题和格式 wb load_workbook(sales_report_formatted.xlsx) ws wb[报告] # 在第一行插入标题 ws.insert_rows(1) ws[A1] 2023年度销售员业绩汇总报告 title_font Font(size16 boldTrue color1F4E79) ws[A1].font title_font ws.merge_cells(A1:E1) # 合并第一行的A到E列 ws[A1].alignment Alignment(horizontalcenter) # 设置表头样式 header_font Font(boldTrue) header_fill PatternFill(start_colorD9E1F2, end_colorD9E1F2, fill_typesolid) for cell in ws[2]: # 第二行是表头 cell.font header_font cell.fill header_fill # 调整列宽 for column in ws.columns: max_length 0 column_letter column[0].column_letter for cell in column: try: if len(str(cell.value)) max_length: max_length len(str(cell.value)) except: pass adjusted_width (max_length 2) ws.column_dimensions[column_letter].width adjusted_width wb.save(sales_report_formatted.xlsx) print(格式化的报告已保存。)这里展示了两种写入方式。第一种用pd.ExcelWriter配合to_excel简单快捷适合保存纯数据。第二种结合了openpyxl可以对单元格进行精细的格式化生成更专业的报告。通常我会先用pandas处理好数据并写入再用openpyxl做最后的“美容”。5. 实战案例构建一个自动化销售周报脚本现在我们把上面的所有“小功能”串联起来形成一个完整的自动化流程。假设你每周一都需要做以下工作从网盘或邮箱获取上周的销售原始数据raw_sales.xlsx。清洗数据去重、填充缺失值、修正格式。计算核心指标各销售员销售额、各产品销量、客户评价情感分布。生成可视化图表。将清洗后的数据、汇总表和图表整合到一个格式美观的周报Excel文件Sales_Report_YYYYMMDD.xlsx中。下面是一个简化的脚本框架import pandas as pd import matplotlib.pyplot as plt from datetime import datetime timedelta import os def generate_weekly_report(raw_file_path, output_dir): 生成销售周报 # 1. 读取数据 print(f正在读取数据: {raw_file_path}) try: df_raw pd.read_excel(raw_file_path) except FileNotFoundError: print(f错误文件 {raw_file_path} 未找到。) return # 2. 数据清洗 print(正在进行数据清洗...) df_clean df_raw.copy() # 去重 df_clean df_clean.drop_duplicates() # 填充缺失销售额为0 df_clean[销售额] df_clean[销售额].fillna(0) # 确保日期列格式 df_clean[日期] pd.to_datetime(df_clean[日期], errorscoerce) # 过滤出上周的数据假设数据包含日期列 last_week_start datetime.now() - timedelta(daysdatetime.now().weekday() 7) last_week_end last_week_start timedelta(days6) df_last_week df_clean[(df_clean[日期] last_week_start) (df_clean[日期] last_week_end)] if df_last_week.empty: print(警告未找到上周的数据。) return # 3. 核心计算 print(正在计算核心指标...) # 销售员汇总 salesman_summary df_last_week.groupby(销售员).agg({ 销售额: [sum, mean, count], 订单ID: nunique # 假设有订单ID列 }).round(2) salesman_summary.columns [总销售额, 平均销售额, 销售次数, 订单数] # 扁平化列名 # 产品汇总 product_summary df_last_week.groupby(产品)[销售额].sum().sort_values(ascendingFalse) # 4. 生成图表 print(正在生成图表...) plt.figure(figsize(10 8)) # 绘制销售员总销售额柱状图 plt.subplot(2 1 1) plt.bar(salesman_summary.index salesman_summary[总销售额], colorteal) plt.title(上周销售员业绩排名) plt.ylabel(总销售额) plt.xticks(rotation45) plt.tight_layout() # 绘制产品销售额饼图 plt.subplot(2 1 2) plt.pie(product_summary.values labelsproduct_summary.index autopct%1.1f%%) plt.title(上周产品销售额构成) chart_path os.path.join(output_dir, weekly_chart.png) plt.savefig(chart_path dpi150) plt.close() # 关闭图形释放内存 # 5. 输出到Excel print(正在生成Excel报告...) output_filename fSales_Report_{datetime.now().strftime(%Y%m%d)}.xlsx output_path os.path.join(output_dir, output_filename) with pd.ExcelWriter(output_path engineopenpyxl) as writer: df_last_week.to_excel(writer, sheet_name上周原始数据, indexFalse) salesman_summary.to_excel(writer, sheet_name销售员汇总) product_summary.to_excel(writer, sheet_name产品汇总) print(f周报生成完成文件保存在: {output_path}) print(f图表保存在: {chart_path}) # 使用函数 if __name__ __main__: raw_file raw_sales.xlsx # 你的原始数据文件路径 output_directory ./reports # 输出目录 os.makedirs(output_directory, exist_okTrue) # 创建输出目录如果不存在 generate_weekly_report(raw_file, output_directory)这个脚本就是一个自动化工作流的雏形。你可以用Windows的任务计划程序或Mac/Linux的cron定时任务让它每周一早上自动运行然后把生成的报告文件发到你的邮箱。从此周报这件事就从一项耗时的手工劳动变成了一个全自动的后台进程。6. 常见问题与排查技巧实录在实际操作中你肯定会遇到各种报错和奇怪的问题。这里我总结了一些最常见的坑和解决办法。6.1 文件读取相关错误错误FileNotFoundError或ModuleNotFoundError: No module named ‘openpyxl’原因文件路径错误或未安装openpyxl引擎。解决检查文件路径是否正确。建议使用原始字符串或双反斜杠如r‘C:\data\file.xlsx’或‘C:\\data\\file.xlsx’。确保已安装openpyxlpip install openpyxl。使用绝对路径更可靠。错误ImportError: Missing optional dependency ‘xlrd’。原因尝试读取老版本的.xls文件但未安装xlrd库。解决安装xlrdpip install xlrd。但注意新版xlrd2.0只支持.xls文件。对于.xlsx坚持用openpyxl。错误读取的数值变成了科学计数法或字符串原因Excel单元格格式为“文本”或包含特殊字符如逗号、货币符号。解决使用pd.read_excel的converters参数或读入后用astype和字符串方法清洗。df pd.read_excel(file.xlsx, converters{销售额: lambda x: float(str(x).replace(, ).replace(, ))})6.2 数据处理中的典型问题问题分组聚合groupby后数据格式混乱列名变成多层索引MultiIndex现象df.groupby(‘A’)[‘B’].agg([‘sum’ ‘mean’])得到的DataFrame列名是(‘B’ ‘sum’)和(‘B’ ‘mean’)。解决使用.reset_index()将分组键变回列或使用as_indexFalse参数。summary df.groupby(A as_indexFalse).agg({B: sum, C: mean}) # 或者 summary df.groupby(A).agg({B: sum, C: mean}).reset_index()问题合并merge后数据行数变多或变少不符合预期原因对how参数连接方式理解不透彻或连接键on存在重复值。排查先分别检查两个DataFrame基于连接键的重复值df1[‘key’].duplicated().sum()和df2[‘key’].duplicated().sum()。明确你想要的连接逻辑是要保留所有左表记录left还是只保留两边都有的inner使用validate参数检查合并类型如pd.merge(df1 df2 on‘key’ how‘inner’ validate‘one_to_one’)如果是一对一或多对一合并它能帮你提前发现错误。问题处理大数据几十万行以上时速度慢内存占用高优化技巧指定数据类型用dtype参数在读取时指定列类型如dtype{‘id’: ‘int32’ ‘amount’: ‘float32’}能大幅减少内存。只读需要的列用usecols参数。使用分块读取pd.read_excel(… chunksize10000)然后循环处理每个块。避免在循环中修改DataFrame尽量使用向量化操作pandas内置函数或.apply()而不是for循环。6.3 可视化与输出问题问题Matplotlib图表中文显示为方框解决这是最常见的问题。需要在绘图前设置中文字体。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 黑体Windows常用 # plt.rcParams[font.sans-serif] [Arial Unicode MS] # Mac常用 plt.rcParams[axes.unicode_minus] False # 解决负号显示问题如果上述方法无效可以指定系统内具体字体文件的路径。import matplotlib zh_font matplotlib.font_manager.FontProperties(fname‘C:/Windows/Fonts/simhei.ttf’) # Windows # 然后在绘图时通过fontproperties参数使用如 plt.xlabel(‘销售员’ fontpropertieszh_font)问题保存的Excel文件用WPS打开报错或格式丢失原因不同办公软件对Excel文件兼容性有差异或者openpyxl写入的某些高级特性不被支持。解决尝试用微软Office打开确认是否是文件本身问题。保存时使用.xlsx格式并确保engine‘openpyxl’。如果使用了复杂的openpyxl格式如条件格式、复杂图表尝试简化格式。一个笨办法但有效用pandas写入数据后手动用Excel打开另存一下。踩过这些坑之后我的核心心得是处理任何数据前先用df.head()、df.info()、df.describe()快速浏览一遍对数据质量有个基本判断。遇到报错不要慌把错误信息完整复制到搜索引擎里十有八九已经有前人踩过同样的坑并给出了解决方案。Python生态的魅力就在于你遇到的问题几乎总能在社区找到答案。