1.按照顺序,我需要分析的是房地产行业的前十家上市公司2013-2022年十年的年报。
上市公司代码 | 上市公司简称 |
---|---|
000002 | 万科A |
000006 | 深振业A |
000007 | *ST全新 |
000011 | 深物业A |
000014 | 沙河股份 |
000029 | 深深房A |
000031 | 大悦城 |
000036 | 华联控股 |
000042 | 中洲控股 |
000069 | 华侨城A |
2、利用python代码根据证券代码、公告标题、公告时间以及股票简称等信息, 从深圳证券交易所下载十家上市公司2013-2022年十年的年报。
3、将获取到的年报进行清洗,设置摘要、英文、取消以及更新等关键词, 清除不符合要求的年报后,保存需要的年报PDF。
4、逐家逐份读取年报,利用代码获取公司年报中基础性信息,公司股票代码、 股票简称、公司网址和办公地址,保存为csv文件,以文档形式呈现结果。这里只截取部分读取写入情况。
5、获取基础性信息后,选取万科企业股份有限公司,对该公司年报中营业收入进行获取,最终整合好 2013-2022年间营业收入数据,绘制营业收入时间序列图,同样地,获取十年间股东净利润数据, 绘制时间序列图。
6、通过更改公司相关信息,获取另外九家公司的营业收入数据,绘制十家公司十年营业收入折线图。 为使得折线图重叠度降低,营业收入以及股东净利润数据均已处理,以亿元为单位。
7、对于这十家公司中出现重组改名等经营主体未出现改变的情况,仍然以该公司最新的名称为标准, 鉴于股票代码并未出现改变,故下载年报时以股票代码为准。
8、对于这十家公司中出现营业收入数据以及股东净利润数据调整的情况,在数据选取方面, 我也进行了调整,选取了调整更新后的数据。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import pandas as pd
import os
import re
import time
import json
import requests
#输入十家公司股票代码以及股票简称
df_sz = pd.DataFrame({'index': ['000002','000006','000007','000011',
'000014','000029','000031','000036','000042','000069'],
'name': ['万科A','深振业A','*ST全新','深物业A','沙河股份',
'深深房A','大悦城','华联控股','中洲控股','华侨城A']
})
#公司名称可能会因重组并购等原因出现更改的情况,而股票代码唯一,选取股票代码作为筛选条件
name_sz = df_sz['index'].tolist()
driver = webdriver.Edge()#使用Edge浏览器
def getszHTML(index): #获取深交所上市公司html网页
driver.get("http://www.szse.cn/disclosure/listed/fixed/index.html")
driver.maximize_window()
driver.implicitly_wait(3)
driver.find_element(By.ID, "input_code").click()
driver.find_element(By.ID, "input_code").send_keys(index)
driver.find_element(By.ID, "input_code").send_keys(Keys.DOWN)
driver.find_element(By.ID, "input_code").send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, "#select_gonggao .c-selectex-btn-text").click()
driver.find_element(By.LINK_TEXT, "年度报告").click()
driver.find_element(By.CSS_SELECTOR, ".input-left").click()
driver.find_element(By.CSS_SELECTOR, "#c-datepicker-menu-1 .calendar-year span").click()
driver.find_element(By.CSS_SELECTOR, ".active li:nth-child(113)").click()
driver.find_element(By.CSS_SELECTOR,
"#c-datepicker-menu-1 tr:nth-child(1) > .available:nth-child(3) > .tdcontainer").click()
driver.find_element(By.CSS_SELECTOR,
"#c-datepicker-menu-2 tr:nth-child(2) > .weekend:nth-child(1) > .tdcontainer").click()
driver.find_element(By.ID, "query-btn").click()
element = driver.find_element(By.ID, 'disclosure-table')
def Save(filename,content): #保存文件
name = open(filename+'.html','w',encoding='utf-8')
name.write(content)
name.close()
i=1
for name in name_sz:#爬取深交所上市公司html
getszHTML(name)
time.sleep(1)
html = driver.find_element(By.ID, 'disclosure-table')
innerHTML = html.get_attribute('innerHTML')
Save(name,innerHTML)
driver.refresh()
time.sleep(1)
print('深交所共有',len(name_sz),'家,已获取第',i,'/',len(name_sz))
i=i+1
driver.quit()
print('获取完毕')
取出十家公司所有相关年报链接
class DisclosureTable():
'''
解析深交所定期报告页搜索表格
'''
def __init__(self, innerHTML):
self.html = innerHTML
self.prefix = 'https://disc.szse.cn/download'
self.prefix_href = 'https://www.szse.cn/'
#
p_a = re.compile('(.*?)', re.DOTALL)
p_span = re.compile('(.*?)', re.DOTALL)
self.get_code = lambda txt: p_a.search(txt).group(1).strip()
self.get_time = lambda txt: p_span.search(txt).group(1).strip()
#
self.txt_to_df()
def txt_to_df(self):
# html table text to DataFrame
html = self.html
p = re.compile('(.*?)', re.DOTALL)
trs = p.findall(html)
p2 = re.compile('(.*?)', re.DOTALL)
tds = [p2.findall(tr) for tr in trs[1:]]
df = pd.DataFrame({'证券代码': [td[0] for td in tds],
'简称': [td[1] for td in tds],
'公告标题': [td[2] for td in tds],
'公告时间': [td[3] for td in tds]})
self.df_txt = df
def get_link(self, txt):
p_txt = '(.*?)'
p = re.compile(p_txt, re.DOTALL)
matchObj = p.search(txt)
attachpath = matchObj.group(1).strip()
href = matchObj.group(2).strip()
title = matchObj.group(3).strip()
return([attachpath, href, title])
def get_data(self):
get_code = self.get_code
get_time = self.get_time
get_link = self.get_link
#
df = self.df_txt
codes = [get_code(td) for td in df['证券代码']]
short_names = [get_code(td) for td in df['简称']]
ahts = [get_link(td) for td in df['公告标题']]
times = [get_time(td) for td in df['公告时间']]
#
prefix = self.prefix
prefix_href = self.prefix
df = pd.DataFrame({'证券代码': codes,
'简称': short_names,
'公告标题': [aht[2] for aht in ahts],
'attachpath': [prefix + aht[0] for aht in ahts],
'公告时间': times,
'href': [prefix_href + aht[1] for aht in ahts]
})
self.df_data = df
return(df)
def Readhtml(filename): #读取
with open(filename+'.html', encoding='utf-8') as f:
html = f.read()
return html
def tidy(df): #清除
d = []
for index, row in df.iterrows():
dd = row[2]
n = re.search("摘要|取消|英文", dd)
if n != None:
d.append(index)
df1 = df.drop(d).reset_index(drop = True)
return df1
def filter_links(words,df,include=True):
ls=[]
for word in words:
if include:
ls.append([word in f for f in df.公告标题])
else:
ls.append([word not in f for f in df.公告标题])
index = []
for r in range(len(df)):
flag = not include
for c in range(len(words)):
if include:
flag = flag or ls[c][r]
else:
flag = flag and ls[c][r]
index.append(flag)
df2 = df[index]
return(df2)
def rename(df):
for i in df["简称"]:
i = i.replace("*","")
i = i.replace(" ","")
if i !="-":
sn=i
return sn
def Loadpdf_sh(df): #用于下载文件
d1 = {}
df["公告时间"] = pd.to_datetime(df["公告时间"])
na = rename(df)
for index, row in df.iterrows():
names = na + str(row[4].year-1)+"年年度报告"
d1[names] = row[3]
for key, value in d1.items():
f = requests.get(value)
with open (key + ".pdf", "wb") as ff:
ff.write(f.content)
def Loadpdf(df): #用于下载文件
d1 = {}
for index, row in df.iterrows():
d1[row[2]] = row[3]
for key, value in d1.items():
f = requests.get(value)
with open (key + ".pdf", "wb") as code:
code.write(f.content)
i = 0
for index,row in df_sz.iterrows(): #提取深交所信息表
i+=1
name = row[1].replace('*','')
html = Readhtml(name)
dt = DisclosureTable(html)
df = dt.get_data()
df1 = tidy(df)
df1.to_csv(name+'.csv',encoding='utf-8-sig')
os.makedirs(name,exist_ok=True)
os.chdir(name)
Loadpdf(df1)
print(name+'年报已保存完毕。共',len(name_sz),'所公司,当前第',i,'所。')
十家公司年报链接html
链接截图以万科A年报下载为例
保存到电脑的年报
import pandas as pd
import fitz
import re
df_company = pd.DataFrame({'index': ['000002','000006','000007','000011','000014','000029','000031','000036','000042','000069'],
'name': ['万科A','深振业A','*ST全新','深物业A','沙河股份','深深房A','大悦城','华联控股','中洲控股','华侨城A']
})
company = df_company['name'].tolist()
t=0
for member in company:
t+=1
member = member.replace('*','')
df = pd.read_csv(member+'.csv',converters={'证券代码':str})
df = df.sort_index(ascending=False)
final = pd.DataFrame(index=range(2012,2022),columns=['营业收入(元)','基本每股收益(元/股)'])
final.index.name='年份'
code = str(df.iloc[0,1])
name = df.iloc[-1,2].replace(' ','')
for i in range(len(df)):
title=df.iloc[i,3]
doc = fitz.open('./%s/%s.pdf'%(member,title))
text=''
for j in range(20):
page = doc[j]
text += page.get_text()
p_year=re.compile('.*?(\d{4}) .*?年度报告.*?')
year = int(p_year.findall(text)[0])
#设置需要匹配的两种数据的pattern
p_site = re.compile('(?<=\n)\w*办公地址:?\s?\n?(.*?)\s?(?=\n)',re.DOTALL)
p_web =re.compile('(?<=\n)公司\w*网址:?\s?\n?([a-zA-Z./:]*)\s?(?=\n)',re.DOTALL)
final.to_csv('%s数据.csv' %member,encoding='utf-8-sig')
site=p_site.search(text).group(1)
web=p_web.search(text).group(1)
with open('%s数据.csv'%member,'a',encoding='utf-8-sig') as f:
result='股票简称,%s\n股票代码,%s\n办公地址,%s\n公司网址,%s'%(name,code,site,web)
f.write(result)
print(name+'数据已保存完毕'+'(',t,'/',len(company),')')
#写入csv文件,使得股票代码保持为字符串数据而不缺失0
df = pd.read_csv("公司基础性信息.txt",delimiter=",",converters = {u'公司股票代码':str,u'公司股票代码':str})
df.to_csv("公司基础性信息.csv", encoding='UTF-8-sig', index=False)
公司基础性信息
公司基础性信息csv
"""
解析年报
to be finished!
"""
import fitz
import pandas as pd
import re
filename = '万科A:2022年年度报告.PDF'
doc = fitz.open(filename)
def get_subtxt(doc,bounds=('主要会计数据和财务指标','总资产')):
#默认设置为首尾页码
start_pageno = 0
end_pageno = len(doc) - 1
#
lb,ub = bounds #lb:lower bound(下界);ub:upper bound(上界)
#获取左界页码
for n in range(len(doc)):
page = doc[n]
txt = page.get_text()
if lb in txt:
start_pageno = n
break
# 获取右界页码
for n in range(start_pageno,len(doc)):
page = doc[n]
txt = page.get_text()
if ub in txt:
end_pageno = n
break
# 获取小范围内字符串
txt = ''
for n in range(start_pageno,end_pageno+1):
page = doc[n]
txt += page.get_text()
return(txt)
txt = get_subtxt(doc)
#获取单一科目
def get_account_data(account,txt):
p_txt = '%s\D*?(\d{1,3}(?:,\d{3})*(?:\.\d+)?)'% account
p = re.compile(p_txt)
matchobj = p.search(txt)
amt = matchobj.group(1)
return(amt)
#yysr gdjlr
revenue = get_account_data('营业收入',txt)
profit_shlder = get_account_data('归属于上市公司股东的净利润',txt)
subtxt = txt[txt.find('营业收入'):txt.find('总资产')]
def get_keywords(txt):
p = re.compile(r'\d+\s+([\u2E80-\u9FFF]+)')
keywords = p.findall(txt)
return(keywords)
keywords = get_keywords(subtxt)
def parse_key_fin_data(subtext):
keywords = ['营业收入','营业成本','毛利',
'归属于上市','归属于上市','经营活动']
ss = []
s = 0
for kw in keywords:
n = subtext.find(kw,s)
ss.append(n)
s = n + len(kw)
ss.append(len(subtext))
data = []
for n in range(len(ss)-1):
s = ss[n]
e = ss[n+1]
line = subtext[s:e]
data.append(line.split())
return
data = parse_key_fin_data(txt)
colnames = ['指标','2022年','2021年','本年比上年变动','2020年']
df = pd.DataFrame(data,columns=colnames)
df.to_csv('000002_2022.csv')
以万科A2022年为例,营业收入和股东净利润
1、对数据进行整理汇总,写入Excel,绘制一家公司十年营业收入时间序列图(折线图)、 十年股东净利润时间序列、其增长率折线图、十家公司2013-2022年营业收入、股东净利润折线图汇总
#万科A 2013-2022年营业收入折线图
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False
#导入万科A2013-2022年营业收入数据并且进行可视化
revenue_万科A=pd.read_excel("D:\\金融数据获取与处理大作业\\十家上市公司2013-2022营业收入.xlsx",
sheet_name="万科A",header=0,index_col=0) #从外部导入数据
revenue_万科A.plot(figsize=(9,6),grid=True)
#万科A 2013-2022年股东净利润折线图
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False
#导入万科A2013-2022年股东净利润数据并且进行可视化
netprofit_万科A=pd.read_excel("D:\\金融数据获取与处理大作业\\十家上市公司2013-2022营业收入.xlsx",
sheet_name="万科A1",header=0,index_col=0) #从外部导入数据
netprofit_万科A.plot(figsize=(9,6),grid=True)
#万科A 2013-2022年营业收入和股东净利润增长率折线图
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False
#导入万科A2013-2022年营业收入以及股东净利润增长率数据并且进行可视化
netprofit_万科A=pd.read_excel("D:\\金融数据获取与处理大作业\\十家上市公司2013-2022营业收入.xlsx",
sheet_name="万科A营业收入和股东净利润增长率",header=0,index_col=0) #从外部导入数据
netprofit_万科A.plot(figsize=(9,6),grid=True)
#十家公司2013-2022年营业收入折线图汇总
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False
#导入十家公司2013-2022年营业收入数据并且进行可视化
netprofit_company=pd.read_excel("D:\\金融数据获取与处理大作业\\十家上市公司2013-2022营业收入.xlsx",
sheet_name="十家公司2013-2022年营业收入汇总表",header=0,index_col=0) #从外部导入数据
netprofit_company.plot(figsize=(9,6),grid=True)
#十家公司2013-2022年股东净利润折线图汇总
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False
#导入十家公司2013-2022年股东净利润数据并且进行可视化
netprofit_company=pd.read_excel("D:\\金融数据获取与处理大作业\\十家上市公司2013-2022营业收入.xlsx",
sheet_name="十家公司2013-2022年股东净利润汇总表",header=0,index_col=0) #从外部导入数据
netprofit_company.plot(figsize=(9,6),grid=True)
2、运行结果分析
十家公司2013-2022年营业收入汇总表
十家公司2013-2022年股东净利润汇总表
万科A营业收入和股东净利润增长率
万科A2013-2022营业收入时间序列图
万科企业股份有限公司自2013年至2022年这十年间营业收入不断攀升,只有2017年与2016年持平, 可能是受到了万科董事会主席王石的退出这一事件的影响。
万科A2013-2022股东利润时间序列图
在2013至2020年间,万科企业股份有限公司股东净利润不断攀升,在2020年达到峰值,超过400亿元,而在2021年急剧下跌, 并保持在一个较低的水平,这与万科自身业务转型尚未成功有很大关系。
万科A 2013-2022年营业收入和股东净利润增长率折线图
2013至2022年间,万科企业股份有限公司营业收入增长与股东净利润增长不成正比,甚至出现反向变动,这也验证了当公司经营状况向好时, 往往不分发股利,也不保留过多的未分配利润,因为此时公司有很多很好的投资机会,有正向的现金流,而此时,股东也并不担心公司不分发股利, 因为公司有很好的发展前景和很大的盈利空间,未来能获得更多的收益,股东对公司的信心很足。相反,倘若公司盈利下降,股东对公司的信心下降, 股东纷纷抛售股票,为了给股东以信心,让其相信公司运转未出现问题,公司正常运转,公司往往会发放股利以稳定投资者。
十家公司2013-2022年营业收入折线图
万科A的营业收入远远高于另外九家公司,万科A作为国内房地产龙头企业,占据了很大的房地产行业市场份额。在这十家公司中,排在第二位的是华侨城,紧随其后的是大悦城,深振业、*ST全新、深物业、沙河股份、深深房A、华联控股以及中洲控股这七家公司营业收入曲线几乎重合,不论是数值上还是趋势上都大致相同, 处于较为平稳的状态,这也在一定程度上代表了房地产行业的发展现状。值得注意的是,在2021年,华侨城和大悦城两家企业的营业收入都有不同程度的上升,2022年, 华侨城和大悦城两家央企正式达成合作,强强联手,这对两家企业未来的发展都将起到推动作用。
十家公司2013-2022年股东净利润折线图
万科A的归属于股东的净利润远高于另外九家公司,排在第二位的是华侨城,与其营业收入走势图相似。大悦城排名第三,但其相比于另外七家公司波动更大, 另外七家公司几乎成一条直线,说明其股东净利润非常稳定。值得注意的是,万科A、华侨城以及大悦城三家公司股东净利润均在2020年有所回落, 万科A在下降后处于较为平稳的状态,而华侨城和大悦城均还处于下降趋势下。
在本次实验报告的编写过程中,我遇到了许许多多的问题,甚至在同样的问题上跌倒过很多次,但是在这场艰难而漫长的学习过程中,我获益匪浅。
每次上课的内容对我来说都是一次新的挑战,在老师的指导下,我慢慢地重新拾起之前学习的编程相关知识点,也在老师的引导下,慢慢走近编程的世界, 这是一场深切的与计算机进行交流的宝贵机会,也是我未来继续深入学习编程知识的巨大推动力,代码的编写让我变得更加认真,更加细心。
这门课程将我之前学习的知识串联起来,同时为我提供许多非常实用的方法,让编程能够为我所用,为我的专业知识的学习服务, 这门课学到的方法将成为我往后学习的利器,让我能够在专业领域走得更远。
我知道自己在金融数据获取与处理的课程上学会的以及熟练掌握的还远远不够,希望我能够在以后的学习中能够经常回顾这门课程的知识, 常看常新,也非常感谢老师给我们提供了一个丰富的知识宝库供我们学习。