监管动态周报生成
按周生成上市公司监管动态周报(Word 文档),涵盖全国各省市证监局、上交所、深交所的监管处分案例。
工作流程概览
① 确定日期范围 → ② 分省市+交易所搜索 → ③ 解析过滤案例
→ ④ 补充详情 → ⑤ 生成 Word 文档 → ⑥ 注入超链接 → ⑦ 输出
前置条件
- baidu-search 技能已安装并配置 API Key(
pass api/baidu-search/key🔐) - Python 依赖:
python-docx、lxml、requests - 模板文件:一个已有的
.docx周报模板,含汇总表 + 案例详情表格 - pass 密码管理器:用于读取 API Key 和其他凭据
步骤详解
1. 确定日期范围
与用户确认周报覆盖的日期范围(通常是上周一至周日)。默认格式示例:2026.6.22-2026.6.28。
如果用户说"本周"或"上周",调用 get_current_time 计算具体日期。
2. 分省市 + 交易所搜索
2a. 确定搜索范围
全国 36 个证监局(31 省市区 + 5 计划单列市):
北京、天津、河北、山西、内蒙古、辽宁、大连、吉林、黑龙江、上海、江苏、浙江、宁波、安徽、福建、厦门、江西、山东、青岛、河南、湖北、湖南、广东、深圳、广西、海南、重庆、四川、贵州、云南、西藏、陕西、甘肃、青海、宁夏、新疆
交易所:上交所(沪市主板 + 科创板)、深交所(深市主板 + 创业板)
2b. 执行搜索
使用 baidu-search 技能,对每个目标证监局/交易所发起搜索。
推荐搜索关键词模板:
# 按证监局搜索(监管措施)
sh skills/baidu-search/scripts/search.sh '{"query":"上海证监局 2026年6月 警示函 责令改正 上市公司"}'
# 搜索行政处罚
sh skills/baidu-search/scripts/search.sh '{"query":"上海证监局 2026年 行政处罚 上市公司"}'
# 搜索立案调查
sh skills/baidu-search/scripts/search.sh '{"query":"上海证监局 2026年 立案调查 上市公司"}'
# 搜索上交所处分
sh skills/baidu-search/scripts/search.sh '{"query":"上交所 2026年6月 通报批评 公开谴责 监管警示 上市公司"}'
# 搜索深交所处分
sh skills/baidu-search/scripts/search.sh '{"query":"深交所 2026年6月 通报批评 公开谴责 监管警示 上市公司"}'
搜索技巧:
- 分监管措施、行政处罚、立案调查三个类型分别搜索
- 时间范围缩小到当周前后(如
2026年6月22日至28日) - 每轮搜索取前 5-10 条结果
- 覆盖尽可能多的证监局,优先搜索经济发达地区(上交所官网、深交所官网也是好来源)
- 对于"证监局"搜索结果不足的地区,尝试用"证监会 XX 监管局"作为关键词
2c. 【推荐】浏览器搜东方财富「立方风控鸟」(替代百度 API)
当百度搜索 API 触发限流(429)时,推荐使用浏览器直接搜索东方财富的 「立方风控鸟」 栏目。这是每日监管汇总专栏,效率远高于逐个省搜索:
优势对比:
| 对比项 | 百度 API | 浏览器搜东方财富 ✅ | |--------|----------|-------------------| | 速率限制 | 10次/分钟,连续搜索被429限流 | 无限制 | | 覆盖范围 | 逐个省市搜索 × 36 = 上百次 | 1篇汇总 = 当日全国案例 | | 搜索质量 | 结果掺杂不相关 | 每日编辑精选,直接列处罚原文链接 | | 反爬机制 | 无验证码 | 无验证码 (东方财富对浏览器友好) |
操作步骤:
# 1. 打开浏览器
browser_use(action="start")
browser_use(action="open", url="https://so.eastmoney.com/Web/s?keyword=立方风控鸟+6月26日+警示函")
# 2. 浏览搜索结果,找到当日汇总文章
browser_use(action="open", url="http://finance.eastmoney.com/a/202606263785036421.html")
# 3. 阅读文章内容,提取监管案例
browser_use(action="snapshot")
# 从文章内容中提取:公司名、处罚机构、处罚类型、违规事实
# 4. 覆盖整周:分别搜索 6月22日 到 6月28日的立方风控鸟文章
# 通常每天有 早报 + 晚报 两篇汇总
搜索关键词模板:
# 搜索特定日期的立方风控鸟
keyword=立方风控鸟+6月24日+早报
keyword=立方风控鸟+6月24日+晚报
# 搜索警示函汇总
keyword=立方风控鸟+警示函
# 搜索某地区证监局案例
keyword=立方风控鸟+安徽证监局
注意:
- 东方财富搜索对 headless 浏览器友好,不需要滑块验证
- 立方风控鸟文章路径格式:
http://finance.eastmoney.com/a/YYYYMMDD<数字>.html - 百度网页版(
baidu.com)会触发滑块验证码,headless 浏览器无法绕过,请勿使用
3. 解析过滤案例
3a. 过滤规则
从搜索结果中提取案例,按以下规则筛选:
✅ 保留:
- 处罚对象为上市公司本身、控股股东、实际控制人、董事、监事、高级管理人员
- 处罚类型:警示函、责令改正、通报批评、公开谴责、监管警示、监管谈话、出具监管关注函
- 行政处罚:没收违法所得、罚款、市场禁入
- 立案调查:证监会/证监局立案调查
- 板块:沪深主板、科创板、创业板(含 ST、*ST)
❌ 排除:
- 处罚对象为中介机构(券商、会计师事务所、律所、评估机构等)
- 对象为个人投资者或非上市公司关联人员
- 纯债券违约、银行间市场处分
- 重复案例(同公司同一事项只保留一次)
- 新闻简讯无具体处罚信息
❌ 特别注意排除(避免混淆):
- 行政处罚事先告知书(尚未正式处罚,只是告知)
- 搜索结果中标题含"事先告知书"的条目
3c. 精选去重(Curated Selection)
文档结构为 两部分:
- 第一部分(汇总表):显示所有搜索到的案例统计总数(包括立案调查、行政处罚、监管措施、上交所、深交所数量)。汇总表的数据反映全部搜索结果,而不是精选后的子集。
- 第二部分(案例详情):仅展示精选后的代表案例(6-8个),覆盖不重复的违规类型。
精选规则:
- 按违规类型分组:将案例按违规类型分组(业绩预告违规、会计处理/收入确认、资金占用、信披遗漏、关联交易、一致行动人未披露、立案调查、行政处罚等)
- 同类去重:同一类型有多个案例时,只保留最典型的一个(选受影响最大、处罚最重、或信息最完整的)
- 强制保留:
- ✅ 立案调查(每期至少保留1个)
- ✅ 行政处罚(每期至少保留1个)
- 覆盖多样性:精选后总数控制在 6-8 个,尽量覆盖不同省市证监局和交易所
示例: 搜到 13 个案例,其中业绩预告违规 5 个(超讯通信、*ST宝馨、*ST冀凯、*ST未名、*ST实达),精选时只保留最典型 1 个(如超讯通信),其余类型各保留 1 个,最终精选 7 个案例。
3d. 案例整合
同一公司的同一违规事项同时被证监局和交易所处罚时:
- 分别记录两个处罚来源的链接
- 在表格中合并为一行,体现双重处罚
- 链接列应包含两个链接(证监局原文、交易所原文),不要只写一个
4. 补充详细内容
对每个保留的案例,用 browser_use 打开原文链接,提取以下信息:
【处罚结果】
处罚机构、日期、具体措施、处罚对象
【违规事实】
公司违反了哪些规定、具体违规行为描述、涉及金额等
【违规事项摘要】
一句话概括:XX公司因XX问题被XX处罚(用于表格第二列)
操作示例:
browser_use(action="open", url="<原文链接>")
browser_use(action="snapshot")
→ 阅读内容,提取处罚结果和违规事实
5. 生成 Word 文档
使用 python-docx 和模板生成周报文档。
模板结构
模板包含两个表格:
- 表0(汇总表):统计各类型案例数量(立案调查、行政处罚、监管措施、上交所、深交所)
- 表1(案例详情表):每3行为一个案例(第1行:股票名称、违规类型+链接、原文链接;第2-3行:处罚详情)
生成步骤
from docx import Document
from docx.shared import Pt
from docx.oxml.ns import qn
# 1. 复制模板
shutil.copy2(template_path, output_path)
doc = Document(output_path)
# 2. 更新汇总表
t0 = doc.tables[0]
t0.rows[0].cells[2].text = '立案调查数量'
t0.rows[1].cells[2].text = '行政处罚数量'
t0.rows[2].cells[2].text = '监管措施数量'
# ...
# 3. 填充案例表
# 使用标记法插入超链接:!!HLINK!!<url>!!<显示文本>!!
# 详见第6步
# 4. 保存中间文件
doc.save(output_path)
重要:python-docx 保存时会丢失超链接关系,所以用标记法 + 后处理注入。
标记格式
在 python-docx 填充时,不要直接创建超链接,而是插入标记字符串:
def sl_marker(cell, url, txt):
"""插入标记,后续用 ZIP 注入替换为真实超链接"""
marker = f'!!HLINK!!{url}!!{txt}!!'
run = cell.paragraphs[0].add_run(marker)
run.font.name = '楷体'
run.font.size = Pt(10.5)
对于同一案例有两个链接(证监局+交易所),插入两个标记:
sl_marker(cell, url1, '证监局原文')
# 在同一单元格再接一个
sl_marker(cell, url2, '交易所原文')
6. ZIP 后处理注入超链接
这是关键技术:用 zipfile + lxml 直接操作 .docx(本质是 ZIP 包),替换标记为真实超链接。
⚠️ 为什么需要这一步: python-docx 保存文档时会丢失 <w:hyperlink> 元素和对应的 document.xml.rels 关系条目。所以必须保存后重新打开 ZIP 包手动注入。
核心逻辑:
import zipfile, re, io
from lxml import etree
NS_W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
NS_R = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
NS_REL = 'http://schemas.openxmlformats.org/package/2006/relationships'
buf = io.BytesIO()
with zipfile.ZipFile(docx_path, 'r') as zin:
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zout:
dx = zin.read('word/document.xml')
rx = zin.read('word/_rels/document.xml.rels')
dt = etree.fromstring(dx)
rt = etree.fromstring(rx)
pattern = re.compile(r'!!HLINK!!(.+?)!!(.+?)!!')
nhl = 0
for te in dt.iter('{' + NS_W + '}t'):
if te.text and '!!HLINK!!' in te.text:
m = pattern.search(te.text)
if m:
url, disp = m.group(1), m.group(2)
te.text = disp # Replace marker text with display text
pr = te.getparent()
pp = pr.getparent()
if pp is not None:
nhl += 1
rid = f'rIdHL{nhl}'
# Add relationship
rel = etree.SubElement(rt, '{' + NS_REL + '}Relationship')
rel.set('Id', rid)
rel.set('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink')
rel.set('Target', url)
rel.set('TargetMode', 'External')
# Create hyperlink element
hl = etree.Element('{' + NS_W + '}hyperlink')
hl.set('{' + NS_R + '}id', rid)
hr = etree.SubElement(hl, '{' + NS_W + '}r')
rp = etree.SubElement(hr, '{' + NS_W + '}rPr')
# font, size, color, underline
rf = etree.SubElement(rp, '{' + NS_W + '}rFonts')
rf.set('{' + NS_W + '}ascii', '楷体')
rf.set('{' + NS_W + '}hAnsi', '楷体')
rf.set('{' + NS_W + '}eastAsia', '楷体')
sz = etree.SubElement(rp, '{' + NS_W + '}sz')
sz.set('{' + NS_W + '}val', '21')
sc = etree.SubElement(rp, '{' + NS_W + '}szCs')
sc.set('{' + NS_W + '}val', '21')
co = etree.SubElement(rp, '{' + NS_W + '}color')
co.set('{' + NS_W + '}val', '0563C1')
un = etree.SubElement(rp, '{' + NS_W + '}u')
un.set('{' + NS_W + '}val', 'single')
tt = etree.SubElement(hr, '{' + NS_W + '}t')
tt.text = disp
tt.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
idx = list(pp).index(pr)
pp.remove(pr)
pp.insert(idx, hl)
# Write back
dxb = etree.tostring(dt, xml_declaration=True, encoding='UTF-8', standalone=True)
rxb = etree.tostring(rt, xml_declaration=True, encoding='UTF-8', standalone=True)
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename == 'word/document.xml': data = dxb
elif item.filename == 'word/_rels/document.xml.rels': data = rxb
zout.writestr(item, data)
⚠️ 注意 URL 中的下划线: 正则 (.+?) 匹配任何字符(非贪婪),所以 URL 中包含下划线、连字符、斜杠等都 OK。使用 !! 作为分隔符时,确保 URL 中不包含 !!(正常情况下不会)。如果 URL 含 !!,需换分隔符。
验证: 注入完成后,检查超链接数量是否等于总链接数:
rels = z.read('word/_rels/document.xml.rels').decode()
targets = re.findall(r'Target="(https?://[^"]+)"', rels)
assert len(targets) == expected_link_count
7. 输出文档
将最终 .docx 文件发送给用户:
send_file_to_user(file_path="监管动态周报(2026.XX.XX-2026.XX.XX).docx")
辅助脚本
本技能附带两个脚本,可简化重复性工作。
scripts/gen_report.py — 文档生成 + 超链接注入一体化
接收 JSON 案例数据,自动完成模板填充和超链接注入。
使用方式:
python3 <skill_dir>/scripts/gen_report.py \n --template <template_path> \n --output <output_path> \n --cases cases.json \n --stats '{"立案调查":0,"行政处罚":0,"监管措施":6,"上交所":5,"深交所":3}' \n --date_range "2026.6.22-2026.6.28"
cases.json 格式:
[
{
"stock": "超讯通信(603322)",
"type": "【上交所】:通报批评\n【对象】:公司、董事长梁建华等",
"links": [
{"url": "https://...", "text": "原文链接"}
],
"detail": "【处罚结果】\n2026年6月22日...\n\n【违规事实】\n..."
}
]
对于同一案例有证监局和交易所双重处罚的情况,links 数组包含两个对象:
{
"stock": "上海物贸(600822)",
"type": "【上海证监局】:责令改正\n【上交所】:通报批评",
"links": [
{"url": "https://...", "text": "证监局原文"},
{"url": "https://...", "text": "交易所原文"}
],
"detail": "..."
}
脚本会自动处理双链接,在同一单元格中依次插入两个 !!HLINK!! 标记。
scripts/search_regions.py — 证监局清单管理
python3 <skill_dir>/scripts/search_regions.py --list # 列出36个证监局
python3 <skill_dir>/scripts/search_regions.py --queries --date "2026年6月" # 生成搜索查询
python3 <skill_dir>/scripts/search_regions.py --queries --type "行政处罚" # 按类型过滤
python3 <skill_dir>/scripts/search_regions.py --queries --date "2026年6月22日至28日" --type "监管措施" --output queries.json
常见问题
超链接丢失
问题: 打开生成好的 .docx 发现超链接不可点击。
原因: python-docx 保存时丢掉了 <w:hyperlink> 和 document.xml.rels 中的关系条目。
解决: 坚持使用标记法 + ZIP 后处理注入。不要试图在 python-docx 中直接创建超链接。
搜索结果不够全或触发限流
问题: 某些省证监局搜不到案例,或 Baidu Search API 返回 429 Too Many Requests。 原因:
- 百度搜索覆盖有限,或该局当周确实没有公开案例
- 频繁请求触发 Baidu AI Search 的速率限制(429 错误)
解决:
- 尝试不同的关键词组合(如"监管措施"→"出具警示函"、"证监局"→"证监会XX监管局")
- 分批次搜索,每轮间隔一段时间(遭遇 429 后等待 30 分钟以上再试)
- 优先搜索交易所官网(上交所、深交所官网公告无需调用百度 API)
- 直接使用
browser_use访问交易所官网获取公告信息 - 如实汇报:向用户说明"百度 API 限流中,剩余 XX 个证监局待搜索",并用手头已有的案例生成文档
模板案例槽位不足
问题: 模板只有 10 个案例槽位(30行表格),但需要展示更多案例。 解决(两种方式):
- 方式一(推荐):扩展模板表格行数,使用
extend_template.py脚本 - 方式二:精选案例时控制在 10 个以内,直接用原模板
扩展模板方法:
python3 <skill_dir>/../temp/extend_template.py # 将模板从30行扩展到39行(13案例)
空案例槽残留旧数据
问题: 精选后案例数少于模板槽位数,空槽残留模板旧内容。 解决: 在生成文档后,清除空槽的文本内容:
for slot in range(curated_count, total_slots):
for row in [slot*3, slot*3+1, slot*3+2]:
for tc in trs[row].findall(...):
t.text = ''
同一案例多个来源
问题: 证监局和交易所对同一公司同一事项都处罚了。 解决: 合并为一个案例,在 type 列同时显示两个处罚来源,links 数组包含两个链接。不要创建两个重复案例条目。
输出格式
ALWAYS 使用以下格式:
- 文件名:
监管动态周报(2026.XX.XX-2026.XX.XX).docx - 汇总表头:证监会(局)立案调查/行政处罚/监管措施、上交所自律监管措施及纪律处分、深交所自律监管措施及纪律处分
- 案例表:每3行为一个案例(第1行:股票名称、违规类型、原文链接;第2-3行:处罚详情)
- 超链接:蓝色带下划线,点击可跳转原文
- 双链接案例:原文链接列显示两个链接(证监局原文 + 交易所原文)
Scan to join WeChat group