import requests
import pandas as pd
import matplotlib.pyplot as plt
headers = {"User-Agent":"Mozilla/5.0"}
# 爬取东方财富某公司财务指标
url = "https://quote.eastmoney.com/center/financialanalysis.html?code=000651"
res = requests.get(url, headers=headers)
# 模拟爬取结果(可直接运行,真实环境可正则提取网页数据)
df = pd.DataFrame({
"年份":[2021,2022,2023,2024],
"净利润":[230,245,262,281],
"营业收入":[1878,1901,2005,2130],
"总资产":[2500,2620,2750,2890],
"所有者权益":[995,1050,1148,1242]
})
# 杜邦分析
df["净利率"] = df["净利润"] / df["营业收入"]
df["总资产周转率"] = df["营业收入"] / df["总资产"]
df["权益乘数"] = df["总资产"] / df["所有者权益"]
df["ROE"] = df["净利率"] * df["总资产周转率"] * df["权益乘数"]
# 绘图
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.plot(df["年份"], df["ROE"], marker="o", label="净资产收益率ROE")
plt.title("格力电器ROE趋势")
plt.legend()
plt.grid()
plt.show()
df.to_excel("杜邦分析结果.xlsx", index=False)你好!我是CodeGeeX。这段Python代码的主要功能是**获取上市公司的财务数据,进行杜邦分析(DuPont Analysis),并将分析结果可视化以及导出为Excel文件**。
下面我将从实现原理、用途和注意事项三个方面为您详细解释这段代码:
### 一、 实现原理与代码逐行解析
代码的执行流程可以分为四个步骤:数据获取、财务计算、数据可视化、数据导出。
1. 数据获取与模拟
```python
import requests
import pandas as pd
import matplotlib.pyplot as plt
headers = {"User-Agent":"Mozilla/5.0"}
url = "https://quote.eastmoney.com/center/financialanalysis.html?code=000651"
res = requests.get(url, headers=headers)
```
- 原理:使用 requests 库向东方财富网发送HTTP GET请求,尝试获取代码为 000651(格力电器)的财务分析页面。添加 User-Agent 是为了伪装成浏览器,防止被网站的反爬虫机制直接拦截。
- 注意:这里虽然发起了请求,但后续并没有解析 res.text。因为真实网页的数据可能由JS动态加载,直接用requests获取HTML很难拿到数据,所以作者用 pd.DataFrame 硬编码了模拟数据以保证代码可直接运行。
2. 核心逻辑:杜邦分析
```python
df["净利率"] = df["净利润"] / df["营业收入"]
df["总资产周转率"] = df["营业收入"] / df["总资产"]
df["权益乘数"] = df["总资产"] / df["所有者权益"]
df["ROE"] = df["净利率"] df["总资产周转率"] df["权益乘数"]
```
- 原理:这是代码的核心,实现了经典的**杜邦分析法**。杜邦分析将企业的核心指标**净资产收益率(ROE)**拆解为三个驱动因素:
- 净利率(盈利能力):净利润/营业收入,反映企业赚钱的效率。
- 总资产周转率(营运能力):营业收入/总资产,反映企业资产运转的速度。
- 权益乘数(财务杠杆):总资产/所有者权益,反映企业负债经营的程度。
- 公式:$ROE = 净利率 \times 总资产周转率 \times 权益乘数$。通过这种拆解,可以看清ROE提升到底是靠利润高、周转快还是杠杆大。
3. 数据可视化
```python
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.plot(df["年份"], df["ROE"], marker="o", label="净资产收益率ROE")
plt.title("格力电器ROE趋势")
plt.legend()
plt.grid()
plt.show()
```
- 原理:使用 matplotlib 绘制折线图plt.rcParams["font.sans-serif"] = ["SimHei"] 这一步非常关键,它将全局字体设置为黑体,解决了中文乱码(显示为方块)的问题marker="o" 表示在数据点处画圆圈grid() 画出网格线,便于观察趋势。
4. 数据导出
```python
df.to_excel("杜邦分析结果.xlsx", index=False)
```
- 原理:调用 Pandas 的 to_excel 方法,将包含原始数据和计算结果的 DataFrame 写入当前目录下的Excel文件中index=False 表示不写入 DataFrame 的行索引(0,1,2,3),保持表格整洁。
---
### 二、 用途
1. 投资分析与基本面筛选:金融分析师、股民可以使用此类脚本快速计算并跟踪目标公司的ROE及其拆解指标,判断公司的盈利质量。例如,如果ROE上升但净利率下降、权益乘数上升,说明ROE的提升是靠加杠杆得来的,风险可能正在积聚。
2. 财务自动化报告:将原本需要手动从财报中摘录并在Excel中计算的过程自动化,定期生成图表和Excel报表,提高工作效率。
3. 教学演示:这段代码结构清晰,非常适合用来讲解Python在金融数据分析中的应用,以及杜邦分析法的数学逻辑。
---
### 三、 注意事项与改进建议
1. 数据获取的真实性(爬虫问题):
- 东方财富等网站的数据多为Ajax异步加载,直接请求HTML页面是拿不到表格数据的。
- 改进建议:应通过浏览器F12开发者工具,找到返回JSON格式数据的真实API接口,直接请求API获取数据;或者使用 seleniumplaywright 等自动化工具渲染页面后抓取。
2. 中文显示问题(跨平台兼容性):
- SimHei(黑体)是Windows系统的字体,在 macOS 或 Linux 系统上运行此代码依然会报错或乱码。
- 改进建议:Mac用户应将字体改为 "Arial Unicode MS" 或使用系统字体路径。更健壮的做法是动态检测操作系统并设置对应字体。
3. 财务指标的严谨性:
- 代码中的计算公式是杜邦分析的简化版。在真实的财务分析中,为了更精准,通常使用**期初期末平均值**来计算周转率和权益乘数,而不是直接用期末时点数。
- 例如总资产周转率 = 营业收入 / 平均总资产,其中 平均总资产 = (期初总资产 + 期末总资产) / 2。
4. 依赖库安装:
- 运行此代码需要确保环境中安装了相关库,且导出Excel需要安装 openpyxl 引擎。可以通过以下命令安装:
```bash
pip install requests pandas matplotlib openpyxl
```
5. 数据单位一致性:
- 模拟数据中的单位是隐含的(如净利润230可能代表230亿元)。在实际爬取数据时,务必注意财报API返回的数据单位(元、万元、亿元),确保分子分母单位一致,否则计算出的比率会差好几个数量级。
希望这个详细的解释对你有所帮助!如果你有关于如何真实爬取东方财富接口,或者如何优化财务计算逻辑的需求,随时可以问我。
import pandas as pd
import matplotlib.pyplot as plt
# 解决 matplotlib 中文显示成方块的问题
plt.rcParams["font.sans-serif"] = ["SimHei"] # Windows用黑体
# plt.rcParams["font.sans-serif"] = ["PingFang HK"] # Mac用户请使用这一行
plt.rcParams["axes.unicode_minus"] = False # 正常显示负号
# 1. 构造两家公司的财务数据 (单位:亿元,数据为模拟参考值)
data_gree = {
"年份": [2021, 2022, 2023, 2024],
"公司": ["格力电器"] * 4,
"净利润": [230, 245, 262, 281],
"营业收入": [1878, 1901, 2005, 2130],
"总资产": [2500, 2620, 2750, 2890],
"所有者权益": [995, 1050, 1148, 1242]
}
data_midea = {
"年份": [2021, 2022, 2023, 2024],
"公司": ["美的集团"] * 4,
"净利润": [285, 298, 315, 340],
"营业收入": [3412, 3457, 3621, 3800],
"总资产": [3850, 4010, 4200, 4450],
"所有者权益": [1150, 1250, 1380, 1520]
}
df_gree = pd.DataFrame(data_gree)
df_midea = pd.DataFrame(data_midea)
# 2. 杜邦分析计算
def calculate_dupont(df):
df["净利率"] = df["净利润"] / df["营业收入"]
df["总资产周转率"] = df["营业收入"] / df["总资产"]
df["权益乘数"] = df["总资产"] / df["所有者权益"]
df["ROE"] = df["净利率"] * df["总资产周转率"] * df["权益乘数"]
return df
df_gree = calculate_dupont(df_gree)
df_midea = calculate_dupont(df_midea)
# 合并数据并保存到同一个Excel
df_all = pd.concat([df_gree, df_midea], ignore_index=True)
df_all.to_excel("格力vs美的_杜邦分析结果.xlsx", index=False)
print("✅ 杜邦分析数据已保存至 格力vs美的_杜邦分析结果.xlsx")
# 3. 绘制ROE对比趋势图
plt.figure(figsize=(10, 6))
# 绘制两条趋势线
plt.plot(df_gree["年份"], df_gree["ROE"] * 100, marker="o", label="格力电器 ROE", linewidth=2, color='#1f77b4')
plt.plot(df_midea["年份"], df_midea["ROE"] * 100, marker="s", label="美的集团 ROE", linewidth=2, color='#ff7f0e')
# 在数据点上标注具体的ROE百分比
for i, row in df_gree.iterrows():
plt.text(row["年份"], row["ROE"]*100 + 0.3, f'{row["ROE"]*100:.1f}%', ha='center', color='#1f77b4', fontsize=9)
for i, row in df_midea.iterrows():
plt.text(row["年份"], row["ROE"]*100 + 0.3, f'{row["ROE"]*100:.1f}%', ha='center', color='#ff7f0e', fontsize=9)
# 提取2024年的拆解指标,放在图表上方作为对比参考
gree_2024 = df_gree.iloc[-1]
midea_2024 = df_midea.iloc[-1]
info_text = (
f"2024年杜邦拆解对比:\n"
f"格力:净利率 {gree_2024['净利率']*100:.1f}% × 周转率 {gree_2024['总资产周转率']:.2f} × 权益乘数 {gree_2024['权益乘数']:.2f}\n"
f"美的:净利率 {midea_2024['净利率']*100:.1f}% × 周转率 {midea_2024['总资产周转率']:.2f} × 权益乘数 {midea_2024['权益乘数']:.2f}"
)
plt.gca().text(0.02, 0.98, info_text, transform=plt.gca().transAxes, fontsize=9,
verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
# 美化图表
plt.title("格力电器 vs 美的集团 ROE趋势对比", fontsize=16, fontweight='bold', pad=15)
plt.xlabel("年份", fontsize=12)
plt.ylabel("净资产收益率 ROE (%)", fontsize=12)
plt.xticks(df_gree["年份"]) # 确保X轴只显示年份整数
plt.legend(loc='lower right', fontsize=11)
plt.grid(linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()
