Python爬虫Ajax系列——批量下载巨潮资讯网上市公司年报

公众号:Python for Finance

巨潮资讯网是中国证监会指定的上市公司信息披露网站,平台提供上市公司公告、公司资讯、公司互动、股东大会网络投票等内容功能,一站式服务资本市场投资者。在本文,我将展示如何批量下载上市公司年报,如果大家想下载其他类型报告也是一样的方法。

巨潮资讯网沪深公告网址:

http://www.cninfo.com.cn/new/commonUrl/pageOfSearch?url=disclosure/list/search

我们在右方的“公告速查”的“分类”中勾选“年报”,即可筛选出上市公司年报。那我们的目标是下载这些pdf,如何实现呢?

image

基本思路:

1、获取巨潮资讯网上市公司年报pdf的网址及公司公告标题、公司代码、公司名称等信息

2、通过访问pdf地址进行下载,按照公司公告标题、公司代码、公司名称进行命名

一、观察网页

1.判断网页是静态网页还是动态网页

(1)我们翻页后,发现网址栏的网址没有发生变化,说明这是ajax动态网页。

image

(2)我们右键“查看源代码”,搜索第一个公司名称“紫晶存储”,发现公司名字不在源代码里,说明我们想要的信息没有存储在我们当前打开的网页上,所以我们需要找到我们需要的数据存在哪个网页。

image

2.找到数据的真实的网页地址

(1)谷歌浏览器右键“检查”,点击“Network”,在出现的界面中选择“Fetch/XHR”按钮,刷新页面。

(2)点击名为“query”的链接

(3)点击“Preview”或者“Response”,可以发现我们需要的数据在这里。

image

二、请求数据

requests-post请求

我们查看“Headers”发现请求方法为post请求,我们拉到最下面,找到“Form Data”,即为post请求的数据参数。

image

我们请求数据的时候,有时候需要携带请求头,请求头信息如下:

image

代码如下:

#定义下载单页年报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,以此类推。

image
image

因此我们嵌套循环即可,代码如下:

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)

代码效果:

进度条效果(可不设置)

image

文件下载效果:

image

以上就是今天的分享,每天进步一点点。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容