巨潮资讯网作为中国证监会指定的上市公司信息披露平台,汇集了沪深京三地上市公司的定期报告、临时公告、IPO文件等海量数据。对于量化研究员和基本面分析者而言,能否高效、结构化地获取这些公告文本,直接影响后续的事件驱动策略、舆情因子构建和风险预警效果。R语言凭借其强大的数据处理生态和统计分析能力,可以较为便捷地完成从公告抓取、文本清洗到事件识别的全流程。本文将围绕这一链路展开,给出可直接落地的R代码方案。

巨潮资讯公告查询接口与抓取思路
巨潮资讯的公告查询页面采用异步加载方式,用户在浏览器中点击查询后,前端会向后端接口发送POST请求,返回JSON格式的公告数据。这意味着绕过复杂的HTML解析,直接模拟该接口请求即可获得结构化数据。请求的核心地址为http://www.cninfo.com.cn/new/hisAnnouncement/query,需要携带股票代码、分页参数、日期范围以及栏目类型等表单字段。其中stock字段格式为“股票代码+市场标识”,例如平安银行的000001,gssz000001。pageSize控制每页条数,pageNum为页码,seDate接收起止日期,column用于区分沪深京交易所板块。
在R语言中,可以使用httr包的POST函数发送表单请求,并通过jsonlite包解析返回内容。需要注意的是,巨潮资讯对请求头有一定校验,建议设置User-Agent和Referer来模拟真实浏览器行为。下面是一段完整的抓取示例,它请求指定股票在某个日期范围内的前30条公告,并输出标题与PDF下载地址。
library(httr)
library(jsonlite)
base_url <- "http://www.cninfo.com.cn/new/hisAnnouncement/query"
headers <- c(
"User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer" = "http://www.cninfo.com.cn/new/commonUrl/pageOfSearch?url=disclosure/list/search"
)
query_params <- list(
stock = "000001,gssz000001",
tabName = "fulltext",
pageSize = 30,
pageNum = 1,
column = "szse",
category = "",
plate = "",
seDate = "2023-01-01~2023-12-31",
searchkey = "",
secid = "",
sortName = "",
sortType = "",
isHLtitle = "true"
)
resp <- POST(base_url, add_headers(.headers = headers), body = query_params, encode = "form")
data <- fromJSON(content(resp, as = "text", encoding = "UTF-8"))
announcements <- data$announcements
print(head(announcements[, c("secName", "announcementTitle", "adjunctUrl")]))
返回结果中的announcements数据框包含公告标题、公司简称、公告时间以及附件下载地址等字段。其中adjunctUrl是PDF文件的相对路径,需要拼接域名前缀http://static.cninfo.com.cn/才能得到完整下载链接。如果只关心公告标题,那么直接对标题进行关键词匹配就能识别事件类型;如果要挖掘公告正文内容,则需要进一步下载PDF并提取文本。
抓取过程中有两个常见问题需要注意。第一是请求频率过高会被暂时封禁IP,建议在批量抓取时加入Sys.sleep(runif(1, 1, 3))随机延迟,降低反爬风险。第二是编码问题,巨潮资讯返回的JSON有时包含中文乱码,使用content(resp, as = "text", encoding = "UTF-8")可以指定编码,保证中文标题正确显示。
公告文本解析与中文文本挖掘
巨潮资讯的公告原文大多以PDF格式提供,R语言中可以使用pdftools包将PDF转换为纯文本。对于扫描件类的老公告,pdftools本身无法直接识别文字,此时可以考虑接入OCR工具,但大部分新公告的PDF都带有可提取的文本层。下载后通过pdf_text函数逐页读取,再将多个页面文本拼接成一个完整字符串,即可进入分词和关键词提取环节。
中文文本挖掘通常需要分词工具,jiebaR是R语言中常用的中文分词包,支持默认词典和用户自定义词典。对于上市公司公告这类专业语料,建议加入“预增”“扭亏为盈”“重大合同”“股权转让”等金融术语,提高分词准确率。分词后可以用freq函数统计词频,并过滤掉数字和常见停用词,筛选出高频关键词作为后续事件识别的候选特征。
library(pdftools)
library(jiebaR)
library(dplyr)
# 下载公告PDF并提取文本
pdf_url <- paste0("http://static.cninfo.com.cn/", announcements$adjunctUrl[1])
pdf_path <- "announcement.pdf"
download.file(pdf_url, pdf_path, mode = "wb")
text <- pdf_text(pdf_path) %>% paste(collapse = "\n")
# 添加自定义词典
engine <- worker(user = "finance_dict.txt")
words <- segment(text, engine)
keywords <- freq(words) %>%
filter(!grepl("[0-9]+", char)) %>%
arrange(desc(freq)) %>%
head(20)
print(keywords)
上述代码中的finance_dict.txt需要提前准备,每一行写入一个自定义词语。如果暂时没有该文件,可以直接使用默认词典,但某些复合词可能被错误切分。例如“业绩预增”可能被切成“业绩”和“预增”两个词,这会影响后续事件的精确匹配。因此建议在实际项目中根据公告标题的高频词不断更新自定义词典。
除了简单词频统计,还可以使用TF-IDF方法提取公告的核心关键词,或者利用词向量模型计算公告之间的语义相似度。不过对于事件驱动策略的初步构建,词频与关键词匹配已经能够覆盖大多数事件类型。需要强调的是,公告标题往往已经浓缩了事件核心信息,如果只需要识别事件类型,优先对标题字段进行文本挖掘即可,无需下载全部PDF正文。
事件驱动分析与基础策略验证
事件驱动策略的核心思想是:当上市公司发布特定类型的公告时,其股价在事件窗口内往往会出现系统性反应。例如业绩预增公告通常带来正面超额收益,而大股东减持公告则可能引发短期下跌。通过自动识别公告事件,可以构建事件库,并计算事件前后的收益率分布,验证事件是否具有统计意义上的异常收益。
在R语言中,可以利用tidyverse套件对公告标题进行关键词匹配,生成事件类型标签。下面代码定义了三类事件及其对应的关键词集合,并使用str_detect对标题进行匹配。如果标题中包含多个关键词,可以通过优先级规则分配事件类型,比如先判断业绩预增,再判断重大合同,最后判断股权变动。
library(tidyverse)
event_keywords <- list(
"业绩预增" = c("预增", "扭亏为盈", "大幅增长"),
"重大合同" = c("重大合同", "中标", "合作协议"),
"股权变动" = c("增持", "减持", "股权转让")
)
announcements <- announcements %>%
mutate(event_type = case_when(
str_detect(announcementTitle, paste(event_keywords$业绩预增, collapse = "|")) ~ "业绩预增",
str_detect(announcementTitle, paste(event_keywords$重大合同, collapse = "|")) ~ "重大合同",
str_detect(announcementTitle, paste(event_keywords$股权变动, collapse = "|")) ~ "股权变动",
TRUE ~ "其他"
))
event_stats <- announcements %>%
group_by(event_type) %>%
summarise(count = n(), .groups = "drop")
print(event_stats)
得到事件类型后,就可以结合股票行情数据计算事件窗口收益。行情数据可以从开源数据源获取,例如使用quantmod包下载历史价格。计算事件窗口收益时,通常以公告发布日为事件日,考察事件日前后5个交易日的累计收益,并与同期大盘指数收益做对比,得到超额收益。如果某一类事件的平均超额收益显著偏离零,说明该事件具有可交易的信息含量。
需要提醒的是,简单事件驱动策略在实际执行中面临多个问题:公告发布时点可能早于事件日,市场可能已经提前反应;不同市场环境下事件效应会发生反转;小市值股票受流动性和涨跌停限制,实际可交易性较差。因此事件驱动策略更适合作为多因子选股的辅助信号,而不是独立交易系统。本文给出的文本挖掘流程为事件研究提供了数据基础,后续的收益分析需要结合更严谨的统计检验和风险调整模型。
批量抓取与稳定性优化
在实际项目中,往往需要批量抓取数百只股票在数年内发布的所有公告。此时单次请求已经无法满足需求,必须编写循环逻辑。可以使用lapply或map函数遍历股票代码列表,在每次请求之间加入随机延迟,并使用tryCatch捕获异常,避免单次失败导致整个抓取任务中断。同时建议将每次返回的公告数据实时写入本地数据库或RDS文件,以便断点续抓。
稳定性优化还包括请求头轮换和代理池的使用。巨潮资讯的反爬策略相对宽松,但持续高频请求仍可能触发IP限制。可以将多个真实浏览器的User-Agent放在一个向量中,每次请求随机选择一个,降低请求特征的一致性。另外,JSON解析失败时不要直接报错退出,而应记录错误日志并重试若干次。R语言中的httr包提供了RETRY函数,可以方便地实现指数退避重试。
另一个容易忽略的细节是公告附件链接的过期问题。巨潮资讯的部分公告PDF链接带有时间戳或会话参数,直接拼接可能无法下载。此时可以先请求公告详情页,解析页面中的真实下载链接。虽然这增加了代码复杂度,但对于需要保存原始公告文本的研究场景来说非常必要。建议将公告元数据和PDF文件分别存储,元数据存入数据框或SQLite数据库,PDF文件按照公告日期和股票代码归档到本地文件夹。
总体而言,R语言在巨潮资讯公告抓取与文本挖掘场景中具备完整的技术链支撑:httr处理网络请求,jsonlite解析结构化数据,pdftools读取文档内容,jiebaR完成中文分词,tidyverse进行数据处理与统计汇总。这套组合无需依赖Python或外部爬虫框架,即可在R环境中完成从数据获取到事件分析的全部工作,适合熟悉R语言的研究者和量化团队快速落地。