4. 识别财报中的会计期间

任务说明

从一份财报文本中识别其对应的会计期间(如“2023年年度报告”)。输入为财报文本字符串,输出为结构化的期间信息:报告年度、报告类型(年度/半年度/一季度/三季度),以及标准化的期间描述。

实现思路

财报文本中,会计期间通常出现在标题、页眉或正文首段,常见格式包括:

  • “XX股份有限公司2023年年度报告”
  • “2023年第一季度报告”
  • “2023年半年度报告”
  • “2023年第三季度报告”

核心策略:用正则表达式匹配“20xx年”与报告类型关键词的组合,优先匹配标题行(前200个字符内),若未命中再全文搜索。报告类型关键词按优先级排序,避免“年度”被“半年度”错误截断。

完整代码

import re
from typing import Optional, Dict

def identify_report_period(text: str) -> Optional[Dict[str, str]]:
    """
    从财报文本中识别会计期间。
    返回: {"year": "2023", "period_type": "annual", "description": "2023年年度报告"}
    未识别到时返回 None。
    """
    # 报告类型关键词(按优先级从高到低排列)
    period_patterns = [
        ("annual", r"年度报告"),
        ("semi_annual", r"半年度报告"),
        ("q3", r"第三季度报告|三季度报告"),
        ("q1", r"第一季度报告|一季度报告"),
    ]

    # 年份匹配:20xx年
    year_pattern = r"(20[0-2]\d)年"

    # 优先在标题区域(前200字符)查找
    search_regions = [text[:200], text]

    for region in search_regions:
        for period_type, type_pattern in period_patterns:
            # 组合匹配:年份 + 报告类型
            combined = re.search(year_pattern + r"?" + type_pattern, region)
            if combined:
                # 提取年份(若组合中未捕获,则单独在附近查找)
                year_match = re.search(year_pattern, region[:combined.end()])
                if not year_match:
                    continue
                year = year_match.group(1)
                return {
                    "year": year,
                    "period_type": period_type,
                    "description": f"{year}年{combined.group(0).replace(year + '年', '')}"
                }

    # 兜底:单独找年份 + 报告类型(允许中间有其他字符)
    for period_type, type_pattern in period_patterns:
        type_match = re.search(type_pattern, text)
        if type_match:
            # 在类型词前50字符内找年份
            start = max(0, type_match.start() - 50)
            year_match = re.search(year_pattern, text[start:type_match.start()])
            if year_match:
                year = year_match.group(1)
                return {
                    "year": year,
                    "period_type": period_type,
                    "description": f"{year}年{type_match.group(0)}"
                }
    return None

# 最小调用示例
if __name__ == "__main__":
    sample = "江苏恒瑞医药股份有限公司2023年年度报告"
    result = identify_report_period(sample)
    print(result)
    # 预期输出: {'year': '2023', 'period_type': 'annual', 'description': '2023年年度报告'}

运行与验证

  1. 将上述代码保存为 identify_period.py,直接运行:python identify_period.py,观察输出是否为示例中的预期结果。
  2. 用以下测试用例验证识别准确性:
test_cases = [
    "万科企业股份有限公司2023年半年度报告",
    "中兴通讯2023年第一季度报告全文",
    "中国平安保险(集团)股份有限公司2023年第三季度报告",
    "某公司2022年年度报告摘要",
    "没有期间信息的普通文本"
]
for t in test_cases:
    print(t, "->", identify_report_period(t))

通过条件:前4条分别正确识别为半年度、一季度、三季度、年度;第5条返回 None。若某条未识别或类型错误,说明正则顺序或年份提取逻辑需调整。

关键说明

  • 正则中 year_pattern + "?" + type_pattern 的 ? 表示年份可省略,实际匹配时年份仍会被后续逻辑捕获,这样设计是为了兼容“年度报告”前无年份但正文有年份的情况。
  • 标题区域优先搜索可避免正文中“本报告期”等干扰词影响判断;若标题区未命中,再全文搜索并回溯年份。
  • “半年度”必须排在“年度”之前匹配,否则“半年度报告”会被“年度报告”错误截断为“年度”。