方佳仪的实验报告📑

目录

Ⅰ.正式作业代码(点击跳转)

  1. 初始数据获取。利用selenium库通过模拟鼠标点击获取各公司年报披露界面,将年报列表的html文件保存到本地并解析为 dataframe方便后续调用。遍历下载链接下载所需年报。(查看代码)
  2. 财务数据提取。循环访问各公司各年年报,将pdf文件读成字符串形式并使用正则表达式匹配需要的“办公地址”、“公司网址”和“董秘名称”等数据。 将各公司的数据保存到本地,便于后面画图。(查看代码)
  3. 根据要求将十家公司的“营业收入”与“所分配净利润”分别绘图,并将“营业收入”画在一张图上。(查看代码)

Ⅱ.实验结果及解读(点击跳转)

  1. 实验结果截图展示(点击跳转)
  2. 实验结果解读(点击跳转)

Ⅲ.感想(点击跳转)

Ⅰ.正式作业代码💻


代码 PART 1

自动匹配被分配到的公司爬取年报并解析网页,根据解析结果下载年报




'''1.从网站获取年报链接'''

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
import re
import pandas as pd
import os

Codes=['603183','603357','603458','603698','603776','603859','603860','603909','605167','688315']

def get_table_sse(code):  #√
    '''
    Get HTML source of 年报链接
    
    :param code: 证券代码,上交所上市公司
    :type string: str
    
    :return: None
    :rtype: None
    
    '''
    browser = webdriver.Edge()
    url='http://www.sse.com.cn/disclosure/listedinfo/regular/'
    browser.get(url)
    browser.set_window_size(1550, 830)
    time.sleep(3)
    browser.find_element(By.ID, "inputCode").click()
    browser.find_element(By.ID, "inputCode").send_keys(code)  #'603183'
    time.sleep(3)
    selector='.sse_outerItem:nth-child(4) .filter-option-inner-inner'  
    
    browser.find_element(By.CSS_SELECTOR,selector).click()
    browser.find_element(By.LINK_TEXT, "年报").click()
    time.sleep(3)
    #
    selector = "body > div.container.sse_content > div > "
    selector += "div.col-lg-9.col-xxl-10 > div > "
    selector += "div.sse_colContent.js_regular > "
    selector += "div.table-responsive > table"
    #
    element = browser.find_element(By.CSS_SELECTOR,selector)
    table_html = element.get_attribute('innerHTML')
    #
    fname=f'{code}.html'
    f = open(fname,'w',encoding='utf-8')
    f.write(table_html)
    f.close()
    #
    browser.quit()

    
def get_table_sse_codes(codes):
    for code in codes:
        get_table_sse(code)

def get_data(tr):
    p_td = re.compile('(.*?)', re.DOTALL)
    tds = p_td.findall(tr)
    #
    s = tds[0].find('>') + 1
    e = tds[0].rfind('<')
    code = tds[0][s:e]
    #
    s = tds[1].find('>') + 1
    e = tds[1].rfind('<')
    name = tds[1][s:e]
    #
    s = tds[2].find('href="') + 6
    e = tds[2].find('.pdf"') + 4
    href = 'http://www.sse.com.cn' + tds[2][s:e]
    s = tds[2].find('$(this))">') + 10
    e = tds[2].find('')
    title = tds[2][s:e]
    #
    date = tds[3].strip()
    
    data = [code,name,href,title,date]
    return(data)

def parse_table(fname,save=True):  #√
    f=open(fname,encoding='utf-8')
    html=f.read()
    f.close()
    #
    p = re.compile('(.+?)', re.DOTALL)
    trs = p.findall(html)
    # 
    trs_new = []
    for tr in trs:
        if tr.strip() != '':
            trs_new.append(tr)
    # 
    data_all = [get_data(tr) for tr in trs_new[1:]]
    df = pd.DataFrame({
        'code': [d[0] for d in data_all],
        'name': [d[1] for d in data_all],
        'href': [d[2] for d in data_all],
        'title': [d[3] for d in data_all],
        'date': [d[4] for d in data_all]
        })
    #
    if save:
        df.to_csv(f'{fname[0:-5]}.csv')
    return(df)

get_table_sse_codes(Codes)
html_list=os.listdir()
html_list=[i for i in html_list if i.endswith('.html')]
df1=parse_table(html_list[0])
df2=parse_table(html_list[1])
df3=parse_table(html_list[2])
df4=parse_table(html_list[3])
df5=parse_table(html_list[4])
df6=parse_table(html_list[5])
df7=parse_table(html_list[6])
df8=parse_table(html_list[7])
df9=parse_table(html_list[8])
df10=parse_table(html_list[9])




'''2.过滤掉一些不重要的的公告链接'''

import time

def  filter_links(words,df,include=True):
    '''筛选保留年报链接
    :param words:保留或剔除包含关键词的列表
    :param df:DataFrame
    :param include:keep or ellipsisxclude
    '''
    ls=[]
    for word in words:
        if include:
            ls.append([word in f for f in df['title']])
        else:
            ls.append([word not in f for f in df['title']])
    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 filter_date(start,end,df):
    date = df['date']
    v = [d>=start and d<= end for d in date]
    df_new = df[v]
    return(df_new)

import datetime
def start_end_10y():
    dt_now = datetime.datetime.now()
    current_year = dt_now.year
    start = f'{current_year-9}-01-01'
    end = f'{current_year}-12-31'
    return((start,end))
                                                                                      
def filter_nb_10y(df,
                  keep_words=['年报','年度报告'],
                  exclude_words=['摘要','修订稿','持续督导'],
                  start=''):
    if start == '':
         start,end = start_end_10y()
    else:
        start_y = int(start[0:4])
        end = f'{start_y + 9}-12-31'   
    #
    df = filter_links(keep_words, df,include=True)
    df = filter_links(exclude_words, df,include=False)
    df = filter_date(start,end,df)
    return(df)                                                                                                                                              

df_all=[df1,df2,df3,df4,df5,df6,df7,df8,df9,df10]
df_all_n=[]
for i in df_all:
    df_all_n.append(filter_nb_10y(i,
                  keep_words=['年报','年度报告'],
                  exclude_words=['摘要','修订稿','持续督导'],
                  start=''))




'''3.下载年报'''

import requests
def download_pdf(href, code, year):
    """
    下载单份年报,自动命名保存
    herf: download link address,
    typestring:str
    code:证券代码,
    year:年报年份,
    string: str

    return:None
    rtype:None
    """
    r = requests.get(href,allow_redirects=True)
    fname = f'{code}_{year}.pdf'
    f = open(fname,'wb')
    f.write(r.content)
    f.close
    #
    r.close
   
def download_pdfs(hrefs,code,years):
    for i in range(len(hrefs)):
        href = hrefs[i]
        year = years[i]
        download_pdf(href,code,year)
        time.sleep(30)
    return()

def download_pdfs_codes(list_hrefs,codes,list_years):
    for i in range(len(list_hrefs)):
        hrefs = list_hrefs[i]
        years = list_years[i]
        code = codes[i]
        download_pdfs(hrefs, code, years)
       
    return()


hrefs=[]
for i in range(10):
    hrefs.append(list(df_all_n[i]['href']))
years=[]
for i in range(10):
    years.append(list(df_all_n[i]['date']))

download_pdfs_codes(hrefs,Codes,years)

代码 PART 2

提取“营业收入(亿元)”、“拟分配的利润或股利(亿元)”

“股票简称”、“股票代码”、“办公地址”、“公司网址”等




'''4.解析年报'''

import fitz
import pandas as pd

    
def get_csv(doc,bounds=('联系人和联系方式','四、 信息披露及备置地点')):
    start_pageno = 0
    end_pageno = len(doc) - 1
    lb,ub=bounds
    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)):
        if ub in doc[n].get_text():
            end_pageno = n+1; break
    txt1 = ''
    for n in range(start_pageno,end_pageno):
        page = doc[n]
        txt1 += page.get_text()
    return(txt1)



#提取基本信息CSV
import numpy as np
na=[np.nan]*len(Codes)
csv2=pd.DataFrame(data={'公司股票代码':Codes,
                        '公司简称':na,
                        '公司网址':na,
                        '公司邮箱':na,
                        '办公地址':na,
                        '董秘姓名':na,
                        '董秘电话':na})
col=csv2.columns
p_list=['公司的中文简称.*?\n(.*?)\n',
        '公司网址.*?\n(.*?)\n',
        '电子信箱.*?电子信箱.*?\n(.*?)\n',
        '公司办公地址.*?\n(.*?)\n',
        '姓名.*?\n(.*?)\n',
        '电话.*?\n(.*?)\n']
file_list=os.listdir()
file_list=[i for i in file_list if i.endswith('.pdf') and  '2023' in i]
for n in range(len(file_list)):
    # n=4
    filename = file_list[n]
    doc = fitz.open(filename)
    csv1=get_csv(doc)
    i=1
    for p in p_list:
        try:
            p1 = re.compile(p,re.DOTALL)
            # p1 = re.compile(p_list[0],re.DOTALL)
            IR = p1.findall(csv1)[0]
            # IR
            csv2.loc[n,col[i]]=IR
            # csv2.loc[4,col[1]]='利柏特'
            i+=1
        except:
            i+=1
csv2.to_csv('公司基本信息.csv')
    


# import pdfplumber

def get_rev(doc,bounds=('主要会计数据和财务指标','主要财务指标')):
    start_pageno = 0
    end_pageno = len(doc) - 1
    lb,ub=bounds
    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)):
        if ub in doc[n].get_text():
            end_pageno = n+1; break
    txt1 = ''
    for n in range(start_pageno,end_pageno):
        page = doc[n]
        txt1 += page.get_text()
    return(txt1)

def get_netp(doc,bounds=('利润分配情况','销售退回')):
    start_pageno = 0
    end_pageno = len(doc) - 1
    lb,ub=bounds
    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)):
        if ub in doc[n].get_text():
            end_pageno = n+1; break
    txt1 = ''
    for n in range(start_pageno,end_pageno):
        page = doc[n]
        txt1 += page.get_text()
    return(txt1)

csv_rev=pd.DataFrame(data={'公司股票代码':Codes})
csv_netp=pd.DataFrame(data={'公司股票代码':Codes})
d_list=['营业收入.*?\n(.*?)\s',
        '拟分配的利润或股利.*?\n(.*?)\s']
for n in range(len(Codes)):
    # n=0
    file_list=os.listdir()
    file_list=[i for i in file_list if i.endswith('.pdf') and  Codes[n] in i]
    for f in range(len(file_list)):
        # f=0
        try:
            doc = fitz.open(file_list[f])
            txt11=get_rev(doc)
            p1 = re.compile(d_list[0],re.DOTALL)
            r = p1.findall(txt11)[0]
            r=r.replace(',','')
            r=round(float(r)/(10**8),2)
            csv_rev.loc[n,file_list[f][7:11]]=r
        except:
            pass
        try:
            txt12=get_netp(doc)
            p2 = re.compile(d_list[1],re.DOTALL)
            netp = p2.findall(txt12)[0]
            netp=netp.replace(',','')
            netp=round(float(netp)/(10**8),2)
            csv_netp.loc[n,file_list[f][7:11]]=netp
        except:
            pass
        
        
#数据整理
csv_rev=csv_rev.set_index(['公司股票代码'])
csv_rev=csv_rev.T
csv_rev = csv_rev.sort_index(ascending=True)
csv_netp=csv_netp.set_index(['公司股票代码'])
csv_netp=csv_netp.T
csv_netp = csv_netp.sort_index(ascending=True)
# csv_rev=csv_rev.apply(pd.to_numeric,errors='ignore')
# csv_netp=csv_netp.apply(pd.to_numeric,errors='ignore')
csv_rev=csv_rev.fillna(0)
csv_netp=csv_netp.fillna(0)
index1=[ int(i)-1 for i in csv_rev.index]
csv_rev['index1']=index1
csv_rev=csv_rev.set_index(['index1'])
index2=[ int(i)-1 for i in csv_netp.index]
csv_netp['index1']=index2
csv_netp=csv_netp.set_index(['index1'])
csv_rev.to_csv('公司营业收入.csv')
csv_netp.to_csv('公司利润分配.csv')

代码 PART 3

十家公司的“营业收入(亿元)”、“拟分配的利润或股利(亿元)”随时间变化趋势图

按每一年度,对十家公司“营业收入(元)”绘制对比图




#输出图表
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']  #确保显示中文
plt.rcParams['axes.unicode_minus'] = False  #确保显示负数的参数设置
#输出营业收入图
for i in range(len(csv_rev.columns)):
    plt.figure()
    plt.plot(csv_rev.index,csv_rev.loc[:,csv_rev.columns[i]],label=u'年营业收入',color='b')
    for x,y in zip(csv_rev.index,csv_rev.loc[:,csv_rev.columns[i]]):#显示bar数值
        plt.text(x,y,'%.2f'%y,ha='center',va='bottom')
    plt.xlabel(u'(年)',fontsize=13)
    plt.ylabel(u'年营业收入(亿元)',fontsize=13,rotation=90)
    plt.legend(loc='best')
    plt.title(u'%s.SH %s-%s年营业收入的可视化'%(str(csv_rev.columns[i]),str(csv_rev.index[0]),str(csv_rev.index[-1])),fontsize=13)
    # plt.yticks(range(0,3,30))
    plt.savefig( '%s.SH营业收入.jpg'%csv_rev.columns[i])
    plt.show()
    

#输出利润分配图  
for i in range(len(csv_netp.columns)):
    plt.figure()
    plt.plot(csv_netp.index,csv_netp.loc[:,csv_netp.columns[i]],label=u'年利润分配',color='r')
    for x,y in zip(csv_netp.index,csv_netp.loc[:,csv_netp.columns[i]]):#显示bar数值
        plt.text(x,y,'%.2f'%y,ha='center',va='bottom')
    plt.xlabel(u'(年)',fontsize=13)
    plt.ylabel(u'年利润分配(亿元)',fontsize=13,rotation=90)
    plt.legend(loc='best')
    plt.title(u'%s.SH %s-%s年利润分配的可视化'%(str(csv_netp.columns[i]),str(csv_rev.index[0]),str(csv_rev.index[-1])),fontsize=13)
    # plt.yticks(range(0,3,30))
    plt.savefig( '%s.SH利润分配.png'%csv_netp.columns[i])
    plt.show()
    

#输出10家公司年营业收入
for i in range(len(csv_rev.index)):
    plt.figure()
    #colors=['#FF8247','33FF66','g','b','y','r','EEAEEE','dda0dd','BBFFFF','63B8FF']
    y=csv_rev.iloc[i,:]
    y=y.sort_values()
    x=csv_rev.columns
    plt.bar(x=x,height=y,label=u'年营业收入',color='#BBFFFF')
    for x,y in zip(x,y):#显示bar数值
        plt.text(x,y,'%.2f'%y,ha='center',va='bottom')
    plt.xlabel(u'(股票代码)',fontsize=13)
    plt.ylabel(u'年营业收入(亿元)',fontsize=13,rotation=90)
    plt.legend(loc='best')
    plt.title(u'%s年营业收入的可视化'%str(csv_rev.index[i]),fontsize=13)
    # plt.yticks(range(0,3,30))
    plt.xticks(rotation=45)
    plt.savefig( '%s年营业收入.png'%csv_rev.index[i])
    plt.show()
    
#输出10家公司营业收入  
fig=plt.figure()
fig,axs=plt.subplots(constrained_layout=True)
ax1=plt.subplot(211)
x=csv_rev.index
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[0]],label=csv_rev.columns[0],color='b')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[1]],label=csv_rev.columns[1],color='g')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[2]],label=csv_rev.columns[2],color='y')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[3]],label=csv_rev.columns[3],color='r')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[4]],label=csv_rev.columns[4],color='#FF8247')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[5]],label=csv_rev.columns[5],color='#EE82EE')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[6]],label=csv_rev.columns[6],color='#9ACD32')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[7]],label=csv_rev.columns[7],color='#006400')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[8]],label=csv_rev.columns[8],color='#1E90FF')
ax1.plot(x,csv_rev.loc[:,csv_rev.columns[9]],label=csv_rev.columns[9],color='#9400D3')
ax1.set_ylabel('年营业收入(亿元)')
ax1.set_xlabel('年份')
ax1.set_title('十家公司年营业收入图')
ax1.legend(bbox_to_anchor=(0.5,-0.5),ncol=2)
plt.yticks(rotation=90)
plt.rcParams['figure.figsize']=(7, 7)
# ax1.grid(True,axis='both')
plt.savefig( '10家公司营业收入.png')
plt.show()

Ⅱ.实验结果及解读📝


实验结果截图展示

年报下载

结果截图 结果截图 结果截图 结果截图

基本信息提取

结果截图

各个公司近十年的营业收入(折线图)

结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图

十家公司每年的营业收入对比(直方图)

结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图

各个公司十年的分配利润

结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图 结果截图

十家公司十年的营业收入

结果截图

Ⅲ.感想

这门课的很多代码老师上课已经讲解过了,更多地是需要耐心去做。写代码的过程中经常会遇到报错,写报告的过程中可能经常会感到心累,但就是两个字——“耐心”,耐心地去“百度”,耐心地“求助”,耐心地坚持下去,相信你一定能成功!
另外,很感谢吴老师开了这门课,使我受益匪浅,希望老师的网站越办越好!


🏁---------------FIN---------------🏁