招标信息采集与自动推送技能
把「招标商机监控 + 资质匹配 + 定时多通道推送 + HTML 日报」封装成可复用工作流。
核心原则:先交互式问清配置 → 再按数据源分支执行采集(优先复用本地历史数据)→ 最后按用户选择定时推送。
自带脚本(位于 scripts/,仅用 Python 标准库,可直接执行;jianyu_to_records.py 额外依赖 pandas/openpyxl):
archive.py— 本地历史数据包的归档与复用校验(实现「本地历史优先复用」)qualification_match.py— 企业资质智能匹配,输出 完全符合/部分符合/不符合build_report.py— 生成 HTML 招标日报(字段规范见references/report-spec.md)send_email.py— QQ 邮箱 SMTP 推送(标题统一格式,支持纯文本正文+HTML附件模式避免被反垃圾拦截)is_holiday.py— 中国法定节假日判断(用于定时跳过)jianyu_to_records.py— 把 jianyu-exporter 导出的剑鱼 xlsx 转为标准 records.json(两技能衔接脚本)qcc_tender_query.py— 企查查招投标查询引擎(ApiCode:958,由独立技能 qcc-tender-query 并入):MD5签名、搜索(GetList)+详情(GetDetail)、纯文本/HTML日报、标准 records.json 输出;每次查询仅 1 次 API 调用(pageIndex=1, pageSize≤20),绝不自动翻页(保护调用额度)
复用依赖:
- 剑鱼导出分支不内联浏览器流程,必须通过 Skill 工具调用
jianyu-exporter技能(Skill(command='jianyu-exporter'))完成导出与去重;其浏览器细节见 jianyu-exporter 技能自带的references/browser-sop.md与scripts/dedup_export.py。bidding 在拿到剑鱼全量 xlsx 后,用本技能scripts/jianyu_to_records.py衔接为 records.json。 - 公网爬虫分支 SOP 见
references/crawler-sop.md(使用agent-browser采集公开招标网站)。 - 微信群推送复用
wechat-desktop-send技能。
0. 第一步:交互式配置引导(技能每次运行时先执行)
技能启动后,用问答式引导用户完成配置,严格按以下顺序逐一询问,未问清前不开始采集。
第一步:数据源选择(必问,决定后续流程分支)
请问您希望通过哪种方式采集招标商机?
- 剑鱼标讯账号导出数据(精准全字段,需账号权限)
- 公网爬虫采集公开招标信息(无需账号,覆盖公开渠道)
- 企查查 API 采集(官方接口,覆盖全国招投标信息,需企查查开放平台 APPKEY,免费20次/付费按次计费)
第二步:对应数据源的采集配置
分支 A(选「剑鱼标讯账号导出」)继续问:
- 请用户提供剑鱼标讯平台的登录账号与密码。重要:账号密码仅用于本次运行会话内临时自动登录导出,绝不写入任何文件、绝不打印到日志、绝不写入记忆、用完不回显、不持久化。(不落盘存储;若后续需定时自动运行,运行时再向用户索取,或通过受信任的系统密钥库/环境变量注入,AI 自身不读取、不留存明文。)
- 逐一确认数据筛选条件:
- 日期范围:需要导出哪一天 / 哪一时间段的招标数据
- 区域范围:需要哪些省份、城市、区县的招标数据
- 行业分类:需要限定哪些行业类别
- 采购单位类型、采购类别等其他筛选条件
- 询问是否需要强制重新导出数据;默认优先复用本地已存档历史数据,避免消耗剑鱼导出点数。
分支 B(选「公网爬虫采集」)继续问:
- 询问需要采集的城市 / 区域范围,获取用户要求采集城市的招标网址信息(如:用户要求采集珠海市信息,则默认获取珠海市招标网等网址的信息)。
- 询问日期范围、行业分类等其他筛选条件。
- 询问是否需要强制重新采集数据;默认优先复用本地已存档历史数据,避免重复爬取消耗 token。
分支 C(选「企查查 API 采集」)继续问:(采用交互式对话,逐项确认后再调用,不猜不跳)
- 企查查密钥(必问):请用户提供企查查开放平台的 AppKey(即 key / apikey) 与 SecretKey。
🔒 安全铁律:密钥仅用于本次运行会话内临时调用 API,绝不写入任何文件、绝不打印日志、绝不写入记忆、用完不回显、不持久化。若后续需定时自动运行,运行时再向用户索取,或通过受信任的系统密钥库/环境变量注入,AI 自身不读取、不留存明文。 📌 获取方式:https://openapi.qcc.com 注册/登录 →「应用管理」→ 创建应用 → 获取 AppKey + SecretKey(免费赠送 20 次调用额度)。
- 企业名称 / 搜索关键词(必问):要查哪家企业或哪类招标?支持企业名称、项目名称、项目编号、行业词;多个词用空格分隔(AND 关系),如"珠海格力 信息化"。
- 查询范围(选问):
- 行政区域:省/市级(如"广州"→440100、"珠海"→440400),留空=全国
- 发布日期范围:起始/结束日期(YYYY-MM-DD),留空=近2年
- ⚠️ 关键词宽松匹配提示:企查查此接口对多关键词为宽松匹配(关键词命中有其一即返回,城市名当弱约束,并非严格限定该地区)。若需要严格限定某城市的招标,必须把城市通过
--areaCode(如珠海→440400)传入锁定区域,而不要只在 keyword 里写城市名。例:严格查珠海信息化 →--keyword "信息化" --areaCode 440400。
- 筛选条件(选问):信息类型 3=招标公告 / 4=中标公告 / 留空=全部。
- 询问是否强制重新拉取;默认优先复用本地已存档历史数据,避免消耗企查查 API 调用次数。
⚠️ IP 白名单说明:企查查开放平台可能要求配置 IP 白名单。WorkBuddy 运行环境的出口 IP 可能不固定,若遇到 IP 限制报错,请参考本文档末尾「IP 白名单解决方案」处理。
第三步:公共功能配置(三种数据源均需询问)
- 询问是否需要开启企业资质智能匹配功能,支持上传企业资质材料自动匹配招标要求,允许用户跳过此功能。
- 询问推送周期与推送时间:支持每天 / 每周 / 每月指定时间推送。
- 询问推送方式:支持 QQ 邮箱推送、WorkBuddy APP 端推送、微信群推送,默认使用 WorkBuddy APP 端推送。
- 询问用户推送方式的相关信息:如用户选择邮箱推送,则运行时向用户索取邮箱账号及授权码(仅本次使用,不落盘、不记忆);微信群推送需询问群名;其他情况类似。
默认规则说明(用户未做明确要求时执行默认值)
- 日期:默认采集 / 导出前一天的全部招标数据
- 区域:默认获取用户当前位置所在城市的招标数据
- 其他筛选条件:默认全选不做限制
- 三种数据源模式均默认本地历史数据优先复用规则,无匹配数据时才执行线上导出 / 爬取 / API拉取
1. 数据采集核心执行流程(根据数据源分支执行)
分支 A:剑鱼标讯账号导出模式
配置完成后,先执行本地历史数据校验,再决定是否发起线上导出。 核心原则:匹配到符合筛选条件的历史数据则优先复用,避免重复消耗导出点数;无匹配数据时再执行自动化导出。 步骤严格按顺序执行,每一步带「确定」按钮的弹层必须先点击该「确定」再继续。
前置步骤:本地历史数据匹配校验(优先执行)
python scripts/archive.py find --source jianyu --date <日期> --region <区域> [--industry <行业>]
- 根据用户本次设置的全部核心筛选条件(日期范围、区域范围、行业分类、采购单位类型、采购类别等),检索本地已存档的剑鱼历史导出数据包。
- 匹配判定规则:日期范围、区域范围完全一致,其余筛选条件无冲突时,判定为可复用数据。
- 若匹配到对应历史数据,且用户未选择强制重新导出:
- 直接读取本地存档数据进入后续处理流程,跳过全部线上导出步骤
- 向用户同步提示:已复用本地历史数据,本次未消耗剑鱼导出点数
- 若未匹配到对应数据,或用户明确要求强制重新导出:继续执行以下线上导出流程。
线上导出执行流程(严格按顺序)→ 调用 jianyu-exporter 技能
⚠️ 本技能不内联浏览器点击流程。剑鱼导出的单一真相来源是 jianyu-exporter 技能。
按以下步骤编排:
- 调用 jianyu-exporter 技能:通过 Skill 工具加载它 ——
Skill(command='jianyu-exporter')并把本次已确认的筛选条件(日期范围、区域省/市、行业、采购单位类型、采购类别、信息类型)传给它。 - jianyu-exporter 技能负责:登录剑鱼 → 进入「数据自助导出」→ 按条件筛选(日期/区域/行业等)→
选「单日限量数据包」免费导出 → 下载 xlsx → 用其自带
dedup_export.py做当日增量去重 (一天内多次导出只交付新增)。完成后把全量 xlsx 路径交付回本流程。 - 本技能执行衔接:用
scripts/jianyu_to_records.py把多级表头 xlsx 转为标准 records.json:python scripts/jianyu_to_records.py <全量xlsx> <records.json> \ [--region 珠海市] [--industry 行政办公] [--date 2026-07-22] - 进入下方「本地数据自动归档」与后续 资质匹配 / 日报生成 / 推送。
任何剑鱼相关的浏览器操作、筛选点击、支付弹窗处理、去重逻辑,一律在 jianyu-exporter 技能内完成, 本技能只负责拿到 xlsx 之后的结构化、归档、推送。两个技能通过「xlsx 文件」这一产物衔接。
导出后:去重与衔接(去重已由 jianyu-exporter 完成)
jianyu-exporter 技能在导出阶段已用其自带 dedup_export.py 完成当日增量去重
(一天内多次导出只交付新增,不重复发送)。本技能不再内联去重逻辑。
本技能拿到全量 xlsx 后,只做「结构化转换 + 跨天复用」:
- 结构化转换:
scripts/jianyu_to_records.py <全量xlsx> <records.json> [--region --industry --date] - 跨天复用:依赖下方「本地数据自动归档」,匹配到历史则优先复用,避免重复消耗剑鱼导出点数。
导出后:本地数据自动归档
python scripts/archive.py store --source jianyu --date <日期> --region <区域> --industry <行业> --input <records.json> [--force]
将完整数据包按「导出日期 + 核心筛选维度」自动归档存储到本地 <skill_dir>/data/archive/jianyu/,供下次复用。
分支 B:公网爬虫采集模式
配置完成后,先执行本地历史数据校验,再决定是否发起公网爬取。 核心原则:匹配到符合筛选条件的历史数据则优先复用,避免重复爬取消耗 token;无匹配数据时再执行爬虫采集。
前置步骤:本地历史数据匹配校验(优先执行)
python scripts/archive.py find --source crawler --date <日期> --region <区域> [--industry <行业>]
- 根据用户本次设置的全部核心筛选条件(日期范围、城市 / 区域范围、行业分类等),检索本地已存档的公网历史采集数据包。
- 匹配判定规则:日期范围、区域范围完全一致,其余筛选条件无冲突时,判定为可复用数据。
- 若匹配到对应历史数据,且用户未选择强制重新采集:
- 直接读取本地存档数据进入后续处理流程,跳过全部公网爬取步骤
- 向用户同步提示:已复用本地历史数据,本次未消耗爬取 token
- 若未匹配到对应数据,或用户明确要求强制重新采集:继续执行以下公网爬虫采集流程。
公网采集执行流程(无匹配数据时执行)
- 支持全国或客户所在城市,获取用户要求城市的招标信息网址(如各市公共资源交易中心、政府采购网、招标网等),通过爬虫技能(推荐
agent-browser)进行获取招标信息。 - 自动对多来源数据进行去重、清洗、结构化整理,剔除无效和重复信息,归一化为 crawler 核心字段(见
references/report-spec.md)。 - 采集完成后自动读取解析全部招标字段,进入后续处理流程。
采集后:本地数据自动归档
python scripts/archive.py store --source crawler --date <日期> --region <区域> --industry <行业> --input <records.json> [--force]
将完整数据包按「采集日期 + 核心筛选维度」自动归档存储到本地 <skill_dir>/data/archive/crawler/,供下次复用。
分支 C:企查查 API 采集模式
配置完成后,先执行本地历史数据校验,再决定是否发起 API 调用。 核心原则:匹配到符合筛选条件的历史数据则优先复用,避免消耗企查查 API 调用次数;无匹配数据时再执行 API 拉取。
接口信息:
- ApiCode:958(招投标信息)
- 接口地址:
https://api.qichacha.com/TenderCheck/GetList - 请求方式:GET
- 认证方式:Header 携带
Token(MD5签名)+Timespan(Unix时间戳) - 免费额度:注册即送 20 次,付费 ¥1.00/次 或 ¥5,000.00/5000次
⚠️ 次数保护铁律(重要):企查查每次 API 调用计费 1 次。本分支每次查询仅发起 1 次 API 调用(
pageIndex=1,pageSize=20),返回该次调用能拿到的全部数据(≤20 条),绝不自动翻页、绝不循环调用。即便接口提示总计有 2000 条命中,也只取第 1 页 20 条;如用户明确说"翻到第 N 页看更多",才再发起 1 次调用(额外消耗 1 次额度)。
前置步骤:本地历史数据匹配校验(优先执行)
python scripts/archive.py find --source qcc --date <日期> --region <区域> [--keyword <关键词>] [--msgType <类型>]
- 根据用户本次设置的全部核心筛选条件(关键词、区域范围、信息类型、发布日期范围等),检索本地已存档的企查查历史拉取数据包。
- 匹配判定规则:关键词 + 区域范围 + 信息类型 + 日期范围完全一致时,判定为可复用数据。
- 若匹配到对应历史数据,且用户未选择强制重新拉取:
- 直接读取本地存档数据进入后续处理流程,跳过全部 API 调用步骤
- 向用户同步提示:已复用本地历史数据,本次未消耗企查查 API 调用次数
- 若未匹配到对应数据,或用户明确要求强制重新拉取:继续执行以下 API 采集流程。
API 采集执行流程(无匹配数据时执行)
调用自带查询引擎 qcc_tender_query.py(即原 qcc-tender-query 独立技能之引擎,已并入本技能):
python scripts/qcc_tender_query.py \
--key <AppKey> \
--secret <SecretKey> \
--keyword "<企业名称或关键词>" \
[--areaCode <区域编码或城市名>] \
[--msgType 3|4] \
[--start <起始日期>] [--end <结束日期>] \
--pageSize 20 \
--output qcc_report.txt \
--html qcc_report.html \
--records qcc_records.json
脚本功能说明:
- MD5 签名自动生成:内部自动计算
Token = MD5(key + Timespan + SecretKey).upper(),无需手动处理 - 单次调用上限拉满:
pageSize默认 20(接口硬上限),1 次调用即返回该页全部 ≤20 条,不浪费这次额度 - 绝不翻页:脚本内无循环/翻页逻辑,查完第 1 页即停(次数保护铁律见上方框)
- 标准 records.json 输出:
--records输出的字段(purchaser_name / budget_amt / content_url 等)与剑鱼/爬虫模式统一,可直接进入下方资质匹配、归档、推送流程 - 详情查询(可选):带
--detail <Id>可调用 GetDetail 接口拉取某条公告完整正文(额外消耗 1 次额度)
企查查返回字段 → 标准 record 映射关系(与 qcc_tender_query.py 的 to_records() 一致):
| 企查查原始字段 | 标准 record 字段 | 说明 | |---|---|---| | Title | title | 项目名称 | | ProjectNo | project_no | 项目编号 | | Province / City | province / city | 省份 / 城市 | | IndustryDesc | industry_desc | 行业分类描述 | | BudgetAmt | budget_amt | 预算金额 | | PublishDate | publish_date | 发布时间 | | OpenDate | open_date | 开标时间 | | ObtainEndDate | obtain_end_date | 标书获取截止时间 | | BidEndDate | bid_end_date | 投标截止时间 | | BidInviUnitList | purchaser_name | 招标/采购单位 | | AgentUnitList | agent_name | 代理单位 | | WinBidUnitList | winner_name | 中标单位 | | BidProgressList | bid_progress | 招标进度 | | ContentUrl | content_url | 原文链接 | | ContractEndTime | contract_end_time | 合同截止时间 |
采集后:本地数据自动归档
python scripts/archive.py store --source qcc --date <日期> --region <区域> --keyword <关键词> --input <qcc_records.json> [--force]
将完整数据包按「拉取日期 + 关键词 + 区域」维度自动归档存储到本地 <skill_dir>/data/archive/qcc/,供下次复用。
2. 企业资质智能匹配(三种数据源通用)
- 若用户开启资质匹配功能,支持用户上传企业资质证书、营业范围等材料(提取为
company.json:qualifications列表、business_scope、registered_capital_wan、established_years)。 - 自动解析采集到的招标公告中的资质要求,与用户企业资质进行逐条匹配。
- 在推送结果中标注匹配度,区分「完全符合」「部分符合」「不符合」三个等级。
- 若用户跳过此功能,则直接输出原始招标数据,不做匹配计算。
执行:
python scripts/qualification_match.py --requirements <req.txt|json> --company <company.json>
逐条解析资质要求子句并与企业资质匹配,输出 verdict(完全符合 / 部分符合 / 不符合)、命中率、未满足项与理由。
3. 定时推送规则(三种数据源通用)
- 按照用户配置的推送周期和时间,自动触发全流程采集 + 推送。
- 用 WorkBuddy 自动化(automation)承载定时任务:调用
automation_update创建 recurring 任务,周期 / 时间取用户配置。常用 rrule:- 每天 09:00:
FREQ=DAILY;BYHOUR=9;BYMINUTE=0 - 每周一 09:00:
FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;BYMINUTE=0 - 每月 1 日 09:00:
FREQ=MONTHLY;BYMONTHDAY=1;BYHOUR=9;BYMINUTE=0任务 prompt 复用本技能流程,并先调用is_holiday.py判断当日是否为法定节假日,是则静默结束、不触发数据采集与推送。
- 每天 09:00:
- 支持用户后续在技能配置 / 自动化中修改推送时间、推送周期。
- 法定节假日自动跳过定时执行,不触发数据采集与推送。
4. 推送方式(三种数据源通用,可同时开启多种)
- WorkBuddy APP 端推送(默认):将生成的招标日报以卡片 + HTML 附件形式推送到用户工作台(用
present_files呈现)。 - QQ 邮箱推送:将 HTML 日报作为邮件正文 + 附件发送到用户指定邮箱,邮件标题统一格式:
【招标日报】XXXX年XX月XX日 招标信息汇总。python scripts/send_email.py --to <收件人> --html-file <report.html> \ --account <发件QQ邮箱> --auth-code <授权码> --date <日期> [--attach]授权码非登录密码,按需向用户索取。
- 微信群推送:将日报核心内容整理为清晰文本格式,调用
wechat-desktop-send技能发送到用户指定的微信群。
5. HTML 日报输出规范(详见 references/report-spec.md)
生成日报前读取 references/report-spec.md,并调用生成器:
python scripts/build_report.py --input <records.json> --source <jianyu|crawler|qcc> \
--date <日期> [--match-enabled] --output <report.html>
根据数据源自动适配展示字段,需支持点击跳转到招标信息的具体页面,支持用户后续自定义调整展示字段:
- 剑鱼标讯模式(全字段展示) 按以下字段分组展示:
- 基础维度:省份、城市、区县、公告标题、公告类别、公告内容、发布时间、公告地址、剑鱼标讯地址
- 项目维度:项目名称、所属行业、项目编号、项目范围、预算金额(万元)、中标金额(万元)
- 时间维度:报名截止日期、开标日期、投标截止日期、合同签订时间
- 主体维度:采购单位信息、招标代理机构、中标单位信息(招标公告来源)、中标单位信息(国家企业公示网站来源)
- 公网爬虫模式(核心字段展示) 按以下字段分组展示:
- 项目名称、采购单位、预算金额、发布时间、截止日期、资质要求、评分办法、行业分类
- 企查查 API 模式(官方结构化字段):HTML 日报由
qcc_tender_query.py直接生成(--html),字段严格对齐 ApiCode:958 官方返回参数并标注英文原名。按以下字段分组展示:- 基础维度:省份、城市、项目名称(Title)、项目编号(ProjectNo)、行业分类(ChannelName/IndustryDesc)
- 金额维度:预算金额(BudgetAmt)
- 时间维度:发布时间(PublishDate)、开标时间(OpenDate)、标书截止(ObtainEndDate)、投标截止(BidEndDate)、合同截止(ContractEndTime)
- 主体维度:采购单位(BidInviUnitList)、代理单位(AgentUnitList)、中标单位(WinBidUnitList)
- 进度与链接:招标进度(BidProgressList)、原文链接(ContentUrl)
- 通用规则:若开启资质匹配,新增「资质匹配度」列,用不同颜色标注匹配等级(绿=完全符合 / 黄=部分符合 / 红=不符合);整体排版美观清晰,支持按行业、区域分组查看。
6. 异常处理机制
通用异常
- 数据采集 / 导出失败、文件解析失败:自动重试 1 次,仍失败则推送报错并保留操作日志。
- 推送渠道异常:自动切换备用推送渠道,同时推送失败提醒。
剑鱼模式专属异常
- 账号密码登录失败:立即暂停执行,推送报错提醒,引导用户重新核对账号密码。
- 单日导出额度不足:推送提醒告知用户当日限量额度已用完,保留筛选条件次日自动重试。
公网爬虫模式专属异常
- 目标网站访问失败、反爬拦截:自动切换备用采集渠道,仍失败则推送报错并提示用户稍后重试。
- 采集数据量为空:推送提醒告知当前条件下无匹配招标信息,建议放宽筛选条件。
企查查 API 模式专属异常
- 认证失败(Token无效 / 密钥错误):立即暂停执行,推送报错提醒用户核对 APPKEY 和 SecretKey 是否正确。
- IP 白名单限制(403/401):推送报错提示 IP 被拒,引导用户参考下方「IP 白名单解决方案」配置。
- 调用次数超限(余额不足):推送提醒告知用户企查查 API 额度已用完,保留筛选条件次日自动重试或提示用户充值。
- 查询结果为空:推送提醒告知当前关键词和筛选条件下无匹配招标信息,建议调整关键词或放宽条件。
- 网络超时/连接失败:自动重试 1 次,仍失败则推送报错提醒。
附录:IP 白名单问题及解决方案
问题背景
企查查开放平台出于安全考虑,可能要求用户在「应用管理」中配置 IP 白名单——只有白名单内的 IP 地址才能成功调用 API。
WorkBuddy 环境的 IP 情况
- WorkBuddy 本地运行时,出口 IP = 你当前的公网 IP(家庭宽带/公司网络)
- 如果使用云服务器、代理、VPN 等,出口 IP 可能不同
解决方案(按推荐顺序)
方案 A:查询当前 IP 并加入白名单(推荐)
- 访问 https://www.ip.cn 或 https://ip.sb 查看当前公网 IP
- 登录企查查开放平台 → 应用管理 → 找到你的应用 → IP 白名单设置
- 将查询到的 IP 地址填入白名单(支持填写 IP 段,如
1.2.3.4/32) - 保存后等待 1-2 分钟生效
方案 B:联系客服开通免白名单
部分账号等级(企业版/高级版)可以联系企查查客服申请关闭 IP 白名单校验:
- 企查查客服电话:登录开放平台查看
- 说明场景:「通过自动化工具调用 API,IP 不固定」
- 客服可能在后台为你关闭该限制
方案 C:使用固定代理服务器
如果 IP 经常变动,可以考虑:
- 购买一个固定出口 IP 的代理服务
- 将代理服务器 IP 加入白名单
- 在请求时通过代理发送(
qcc_tender_query.py可扩展--proxy参数)
快速诊断脚本
如果遇到 403/401 错误,可以用以下命令快速确认是否为 IP 问题:
# 查看 IP
curl https://ip.sb
# 测试企查查连通性(替换你的 key 和 keyword)
curl -v "https://api.qichacha.com/TenderCheck/GetList?key=YOUR_KEY&keyword=测试"
如果返回 403 Forbidden 或 401 Unauthorized,基本可确认为 IP 白名单问题。
Scan to join WeChat group