2. 金融文本命名实体识别:公司名与股票代码
任务与数据
从公告文本中抽取三类实体:公司全称(以“股份有限公司/有限公司”结尾)、公司简称(“证券简称:XXX”)、股票代码(6位数字,常见前缀 600/601/603/000/002/300)。输出一张映射表:简称 → (全称, 代码)。
依赖:Python 3.8+,仅用标准库 re、json,无需第三方包。
抽取流程
完整实现
import re, json
FULL_RE = re.compile(r'[\u4e00-\u9fa5]{2,20}(?:股份有限公司|有限公司)')
CODE_RE = re.compile(r'(?<!\d)((?:600|601|603|000|002|300)\d{3})(?!\d)')
ABBR_RE = re.compile(r'证券简称[::]\s*([\u4e00-\u9fa5A-Za-z0-9]{2,10})')
def extract(text):
fulls = FULL_RE.findall(text)
codes = CODE_RE.findall(text)
abbrs = ABBR_RE.findall(text)
# 取出现次数最多的全称作为主全称
full = max(set(fulls), key=fulls.count) if fulls else None
code = codes[0] if codes else None
abbr = abbrs[0] if abbrs else None
return {"简称": abbr, "全称": full, "代码": code}
def build_map(texts):
table = {}
for t in texts:
e = extract(t)
if e["简称"] and e["代码"]:
table[e["简称"]] = {"全称": e["全称"], "代码": e["代码"]}
return table
if __name__ == "__main__":
docs = [
"贵州茅台酒股份有限公司公告:证券简称:贵州茅台,证券代码:600519。",
"比亚迪股份有限公司 证券简称:比亚迪 股票代码:002594 关于年度报告。",
"宁德时代新能源科技股份有限公司,证券简称:宁德时代,代码 300750。",
]
table = build_map(docs)
print(json.dumps(table, ensure_ascii=False, indent=2))
运行与验证
运行:python extract.py。预期输出(由代码逻辑唯一确定):
{
"贵州茅台": {"全称": "贵州茅台酒股份有限公司", "代码": "600519"},
"比亚迪": {"全称": "比亚迪股份有限公司", "代码": "002594"},
"宁德时代": {"全称": "宁德时代新能源科技股份有限公司", "代码": "300750"}
}
通过条件:三个简称均被识别,全称以“股份有限公司/有限公司”结尾,代码为6位且前缀合法。若某条公告缺少简称或代码,该条不进入映射表(如代码中 if e["简称"] and e["代码"] 所保证)。
常见问题
- 全称误匹配:正则限定 2–20 个汉字,避免跨句吞并;若公告含多个全称,取出现次数最多者。
- 代码误匹配:用
(?<!\d)与(?!\d)边界,防止从更长数字串中截取。 - 简称缺失:部分公告用“证券简称”以外的表述,可扩展
ABBR_RE的别名列表。