Python:爬取巨潮网公告

发布时间:2021-06-05 阅读 5666

Stata连享会   主页 || 视频 || 推文 || 知乎

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

New! lianxh 命令发布了:
随时搜索推文、Stata 资源。安装命令如下:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh

课程详情 https://gitee.com/lianxh/Course

课程主页 https://gitee.com/lianxh/Course

⛳ Stata 系列推文:

PDF下载 - 推文合集

作者:秦利宾 (厦门大学)
邮箱qlb150@63.com

编者按:关于「Python:爬取上市公司公告-Wind-CSMAR」这篇推文,经常会有人反映不能正常下载公告,这可能和一些不可控的因素有关。因此,作者重新写了一篇能够从「巨潮网」批量下载所有公告的推文,以供大家参考。不过,本推文相对比较基础,很多细节并没有考虑到,读者在使用过程中需要根据具体的需要和问题进行修改。


目录


1. 获取公告概览

以任意几个公告的 PDF 链接为例,我们可以发现区别仅仅在 finalpage 后面的部分,即日期和公告 ID。

打开「沪市公告」页面,通过右键检查,可以发现公告地址以 JSON 格式存储在 disclosure 文件下。

一个简单的思路,就是直接循环页码,来获取每页公告的地址。但是,当循环到 100 页时,我们会发现后面的页码禁止访问,且每次只能访问 3 年公告。此时,不妨换个思路,由于单个股票的公告页数几乎不可能超过 100 页,即 100×30 个公告,我们可以通过对股票循环来获取所有公告地址。

根据以上分析,我们要获取所有公告地址,大致需要以下三步:

  • 获取所有股票代码;
  • 获取股票公告地址;
  • 下载所有股票公告。

2. 获取股票代码

关于股票代码,我们可以通过多种途径获取,比如 CSMAR 和 Wind 这类数据库,也可以直接从相关网站获取。在这里,本文以「巨潮网」为例。同样地,打开「沪市公告」页面,通过右键检查,可以发现股票代码以 JSON 格式存储在 szse_stock.json 文件下。

通过点击 Headers,可以发现该文件的链接如下:

  • http://www.cninfo.com.cn/new/data/szse_stock.json

将其复制粘贴到浏览器,可以发现股票代码如下:

接着,我们通过编写如下程序,获取 A 股股票信息:

#导入必要的包
import os       #实现系统功能
import xlrd     #读取excel
import xlwt     #写入excel
import json     #解析json
import requests #获取网页内容
import math     #数学函数
import re       #正则表达
from urllib.request import urlretrieve #下载网络文件到本地
#获取股票信息
url = "http://www.cninfo.com.cn/new/data/szse_stock.json"
ret = requests.get(url=url)
ret = ret.content
stock_list = json.loads(ret)["stockList"]
stock_list[:2]
[{'orgId': 'gssz0000001',
  'category': 'A股',
  'code': '000001',
  'pinyin': 'payh',
  'zwjc': '平安银行'},
 {'orgId': 'gssz0000002',
  'category': 'A股',
  'code': '000002',
  'pinyin': 'wka',
  'zwjc': '万科A'}]
#只保留A股
stock_list_new = []
for stock in stock_list:
    if stock["category"] == "A股":
        stock_list_new.append(stock)
        
#添加column和plate信息,后续需要用到
i = 0
for stock in stock_list_new:
    if stock["code"][0] == "0" or stock["code"][0] == "3":
        stock_list_new[i]["column"] = "szse"
        stock_list_new[i]["plate"] = "sz"
    else:
        stock_list_new[i]["column"] = "sse"
        stock_list_new[i]["plate"] = "sh"  
    i = i + 1  
stock_list_new[:2]
[{'orgId': 'gssz0000001',
  'category': 'A股',
  'code': '000001',
  'pinyin': 'payh',
  'zwjc': '平安银行',
  'column': 'szse',
  'plate': 'sz'},
 {'orgId': 'gssz0000002',
  'category': 'A股',
  'code': '000002',
  'pinyin': 'wka',
  'zwjc': '万科A',
  'column': 'szse',
  'plate': 'sz'}]

3. 获取公告地址

「平安银行」为例,右键检查可以发现,公告地址存储在 query 文件夹。

通过 Headers 和 Preview,我们可以发现,每页有 30 条公告,总共有 51 页,总记录数为 30×51=1530,与实际记录数 1547 不符。因此,在实际操作时,可以将总页数加 1,或者用总记录数除以每页记录数,并向上取整。

通过以上分析可知,循环的变量有两个,即股票代码和股票页数。股票代码在前一小节已经获取,接下来我们要获取每个股票总页数。

#获取股票公告页数
stock_list_new = stock_list_new[:10] #以前10个为例,直接删除这行
url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"
i = 0
for stock in stock_list_new: 
    data = {
        "stock": stock["code"]+","+stock["orgId"],
        "tabName": "fulltext",
        "pageSize": 30,
        "pageNum": 1,
        "column": stock["column"],
        "plate": stock["plate"],
        "isHLtitle": "true",
    }

    ret = requests.post(url=url, data=data)
    if ret.status_code == 200:
        ret = ret.content
        ret = str(ret,encoding="utf-8")
        total_ann = json.loads(ret)["totalAnnouncement"]
        stock_list_new[i]["pages"] = math.ceil(total_ann/30)
        print(f"成功获取第{i}个股票页数!")
        i = i+1
    else:
        break

在以上步骤的基础上,我们就可以对股票代码和页数进行循环,以获所有公告地址。

#将stock_list信息写入excel
w = xlwt.Workbook()
ws = w.add_sheet("股票信息")
title_list = ["orgId","category","code","pinyin", \
              "zwjc","pages","column","plate"]
j = 0
for title in title_list:
    ws.write(0,j,title)
    j = j+1

i = 1
for stock in stock_list_new[:10]: 
    content_list = [stock["orgId"],stock["category"], \
                    stock["code"],stock["pinyin"],    \
                    stock["zwjc"],stock["pages"],     \
                    stock["column"],stock["plate"]]
    j = 0
    for content in content_list:
        ws.write(i,j,content)
        j = j+1
    i = i+1
w.save("股票信息.xls")
#读入excel
w = xlrd.open_workbook("股票信息.xls")
ws = w.sheet_by_name("股票信息")
nor = ws.nrows
nol = ws.ncols

stock_list = []
for i in range(1, nor):
    dict = {}
    for j in range(nol):
        title = ws.cell_value(0, j)
        value = ws.cell_value(i, j)
        dict[title] = value
    stock_list.append(dict)
#获取公告地址
for stock in stock_list:
    url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"
    name = stock["code"]

    w = xlwt.Workbook()
    ws = w.add_sheet(name)
    title_list = ["secCode","secName","announcementTitle", \
                  "adjunctUrl","columnId"]
    j = 0
    for title in title_list:
        ws.write(0,j,title)
        j = j+1

    i = 1   
    for page in range(1,int(stock["pages"])+1):
        data = {
            "stock": stock["code"]+","+stock["orgId"],
            "tabName": "fulltext",
            "pageSize": 30,
            "pageNum": page,
            "column": stock["column"],
            "plate": stock["plate"],
            "isHLtitle": "true",
            }
        ret = requests.post(url, data=data)
        ret = ret.content
        ret = str(ret,encoding="utf-8")
        ann_list = json.loads(ret)["announcements"]
        for ann in ann_list:
            content_list = [ann["secCode"],ann["secName"], \
                            ann["announcementTitle"],      \
                            ann["adjunctUrl"],ann["columnId"]]
            j = 0
            for content in content_list:
                ws.write(i,j,content)
                j = j+1
            i = i+1
    print(f"成功写入{name}!")
    w.save(f"{name}.xls")

下图展示了部分结果。其中,每个股票的所有公告单独存在一个 Excel 文件夹下。股票公告地址则存放在 adjunctUrl 变量下。由于这里是所有公告,若是要细分公告类型,可依据 announcementTitle 和 columnld 变量划分。

需要注意的是,理论上单个股票只能获取前 100 页公告地址。若想避免这个问题,有以下两个方法:

  • 筛选出超过 100 页的股票代码,单独处理,运行时间较短;
#判断股票页数是否超过 100
i = 0
for stock in stock_list:
    if stock["pages"] >= 100:
        print(i, stock["code"], stock["pages"])
    i = i + 1
  • 代码写作过程中,逐年考虑,相对复杂,运行时间较长。

4. 下载所有公告

最后,我们就可以根据公告地址下载所有公告了。需要注意的是,在文件命名过程中不要有特殊字符,否则会报错。并且,有些公告链接是 html 格式的,因此可以根据链接直接将网页内容保存,这里不再过多赘述。

if not os.path.exists("PDF"): #不存在,创建pdf子文件夹
    os.mkdir("PDF") 
files =[f for f in os.listdir() if re.match("\d{6}",f)]
for file in files:
    w = xlrd.open_workbook(file)
    ws = w.sheet_by_name(file.replace(".xls", ""))
    nor = ws.nrows
    nol = ws.ncols
    for i in range(1, nor):
        url = "http://static.cninfo.com.cn/"+ ws.cell_value(i, 3)
        print(url)
        name = ws.cell_value(i, 2) + ".pdf"
        urlretrieve(url, filename = "./PDF/" + name)
        print(f"成功下载{name}!")

同时,本文附上 Stata 数据整理代码,之后可以用 copy 命令批量下载公告。

cd D:\A股公告
cap fs *.xls
foreach xls in `r(files)'{
  if "`xls'" != "股票信息.xls"{
    import excel using `xls', firstrow clear
    local name = subinstr("`xls'", ".xls", "",.)
    save `name'.dta, replace
  }
}

cap fs *.dta
use 000001.dta, clear
foreach dta in `r(files)'{
  if "`dta'" != "000001.dta"{
    append using `dta', force
  }
}
save A股公告整理.dta, replace

cap fs *.dta
foreach dta in `r(files)'{
  if "`dta'" != "a股公告整理.dta"{
    erase `dta'
  }
}

cap mkdir "./PDF"
use A股公告整理.dta, clear
replace adjunctUrl = "http://static.cninfo.com.cn/" + adjunctUrl
replace announcementTitle = announcementTitle + ".pdf"
cap dis _N
forvalues i = 1(1)`r(N)'{
  local u1 = adjunctUrl[`i']
  local u2 = announcementTitle[`i']
  copy "`u1'" "./PDF/`u2'", replace
}

5. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh lianxh 公告 爬虫 文本, m
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

免费公开课

最新课程-直播课

专题 嘉宾 直播/回看视频
最新专题 文本分析、机器学习、效率专题、生存分析等
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]
  • Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。

课程主页

课程主页

关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 连享会-主页知乎专栏,400+ 推文,实证分析不再抓狂。直播间 有很多视频课程,可以随时观看。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会-常见问题解答:
https://gitee.com/lianxh/Course/wikis

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh