公众号:Python for Finance
巨潮资讯网是中国证监会指定的上市公司信息披露网站,平台提供上市公司公告、公司资讯、公司互动、股东大会网络投票等内容功能,一站式服务资本市场投资者。在本文,我将展示如何批量下载上市公司年报,如果大家想下载其他类型报告也是一样的方法。
巨潮资讯网沪深公告网址:
http://www.cninfo.com.cn/new/commonUrl/pageOfSearch?url=disclosure/list/search
我们在右方的“公告速查”的“分类”中勾选“年报”,即可筛选出上市公司年报。那我们的目标是下载这些pdf,如何实现呢?
基本思路:
1、获取巨潮资讯网上市公司年报pdf的网址及公司公告标题、公司代码、公司名称等信息
2、通过访问pdf地址进行下载,按照公司公告标题、公司代码、公司名称进行命名
一、观察网页
1.判断网页是静态网页还是动态网页
(1)我们翻页后,发现网址栏的网址没有发生变化,说明这是ajax动态网页。
(2)我们右键“查看源代码”,搜索第一个公司名称“紫晶存储”,发现公司名字不在源代码里,说明我们想要的信息没有存储在我们当前打开的网页上,所以我们需要找到我们需要的数据存在哪个网页。
2.找到数据的真实的网页地址
(1)谷歌浏览器右键“检查”,点击“Network”,在出现的界面中选择“Fetch/XHR”按钮,刷新页面。
(2)点击名为“query”的链接
(3)点击“Preview”或者“Response”,可以发现我们需要的数据在这里。
二、请求数据
requests-post请求
我们查看“Headers”发现请求方法为post请求,我们拉到最下面,找到“Form Data”,即为post请求的数据参数。
我们请求数据的时候,有时候需要携带请求头,请求头信息如下:
代码如下:
#定义下载单页年报pdf的函数
def get_and_download_pdf_flie(pageNum):
url='http://www.cninfo.com.cn/new/hisAnnouncement/query'
pageNum=int(pageNum)
data={'pageNum':pageNum,
'pageSize':30,
'column':'szse',
'tabName':'fulltext',
'plate':'',
'stock':'',
'searchkey':'',
'secid':'',
'category':'category_ndbg_szsh',
'trade':'',
'seDate':'2021-03-26~2021-09-26',
'sortName':'',
'sortType':'',
'isHLtitle':'true'}
headers={'Accept':'*/*',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Content-Length':'181',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Host':'www.cninfo.com.cn',
'Origin':'http://www.cninfo.com.cn',
'Referer':'http://www.cninfo.com.cn/new/commonUrl/pageOfSearch?url=disclosure/list/search',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'}
r=requests.post(url,data=data,headers=headers)
三、存储数据
json数据解析
由于网页返回的是json格式数据,获取我们需要的公司名称、公司代码、公司公告,我们通过字典访问即可。其中公司公告pdf的网页链接如何拿到?我们点击第一条公司年报观察,发现其网址后缀存储在adjunctUrl里,我们可以提取此后缀,再将前缀加上,就可以拿到年报pdf的完整链接。
拿到pdf链接后,我们如何下载pdf呢,需要用response.content来写入文件信息。在此我们补充一个知识点:
response.text 与 response.content的区别
response.text 与 response.content 都是来获取response中的数据信息,那么response.text 和 response.content 到底有哪些差别?
(1)返回的数据类型
response.text 返回的是一个 unicode 型的文本数据;
response.content 返回的是 bytes 型的二进制数据;
也就是说如果想取文本数据可以通过response.text;如果想取文件等,则可以通过 response.content。
(2)数据编码
response.content 返回的是二进制响应内容,可通过response.content.decode()进行解码。
response.text 则是默认”iso-8859-1”编码,服务器不指定的话是根据网页的响应来猜测编码。
代码如下:
result=r.json()['announcements']#获取单页年报的数据,数据格式为json。获取json中的年报信息。
#2.对数据信息进行提取
for i in result:
if re.search('摘要',i['announcementTitle']):#避免下载一些年报摘要等不需要的文件
pass
else:
title=i['announcementTitle']
secName=i['secName']
secName=secName.replace('*','')#下载前要将文件名中带*号的去掉,因为文件命名规则不能带*号,否则程序会中断
secCode=i['secCode']
adjunctUrl=i['adjunctUrl']
down_url='http://static.cninfo.com.cn/'+adjunctUrl
filename=f'{secCode}{secName}{title}.pdf'
filepath=saving_path+'\\'+filename
r=requests.get(down_url)
with open(filepath,'wb') as f:
f.write(r.content)
print(f'{secCode}{secName}{title}下载完毕')#设置进度条
四、通过循环,批量下载公司年报
寻找翻页规律。
我们分别点击第1页、第2页、第3页,发现不同页码的动态网页一致,只是post参数不一致,第1页的“pageNum”是1,第2页的“pageNum”是2,第3页的“pageNum”是3,以此类推。
因此我们嵌套循环即可,代码如下:
for pageNum in range(1,3):#为演示,下载1-2页的年报
get_and_download_pdf_flie(pageNum) #执行以上定义的下载单页年报pdf的函数
全套代码如下:
import requests
import re
#定义爬取函数
#1、对单个页面进行请求,返回数据信息——以第一页为例
saving_path='C:\\Users\\chenwei\\Desktop\\巨潮资讯年报'#设置存储年报的文件夹,把文件夹改成你自己的
import requests
def get_and_download_pdf_flie(pageNum):
url='http://www.cninfo.com.cn/new/hisAnnouncement/query'
pageNum=int(pageNum)
data={'pageNum':pageNum,
'pageSize':30,
'column':'szse',
'tabName':'fulltext',
'plate':'',
'stock':'',
'searchkey':'',
'secid':'',
'category':'category_ndbg_szsh',
'trade':'',
'seDate':'2021-03-26~2021-09-26',
'sortName':'',
'sortType':'',
'isHLtitle':'true'}
headers={'Accept':'*/*',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Content-Length':'181',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Host':'www.cninfo.com.cn',
'Origin':'http://www.cninfo.com.cn',
'Referer':'http://www.cninfo.com.cn/new/commonUrl/pageOfSearch?url=disclosure/list/search',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'}
r=requests.post(url,data=data,headers=headers)
result=r.json()['announcements']#获取单页年报的数据,数据格式为json。获取json中的年报信息。
#2.对数据信息进行提取
for i in result:
if re.search('摘要',i['announcementTitle']):#避免下载一些年报摘要等不需要的文件
pass
else:
title=i['announcementTitle']
secName=i['secName']
secName=secName.replace('*','')#下载前要将文件名中带*号的去掉,因为文件命名规则不能带*号,否则程序会中断
secCode=i['secCode']
adjunctUrl=i['adjunctUrl']
down_url='http://static.cninfo.com.cn/'+adjunctUrl
filename=f'{secCode}{secName}{title}.pdf'
filepath=saving_path+'\\'+filename
r=requests.get(down_url)
with open(filepath,'wb') as f:
f.write(r.content)
print(f'{secCode}{secName}{title}下载完毕')#设置进度条
#3.设置循环,下载多页的年报
for pageNum in range(1,3):#为演示,下载1-2页的年报
get_and_download_pdf_flie(pageNum)
代码效果:
进度条效果(可不设置)
文件下载效果:
以上就是今天的分享,每天进步一点点。