platform-assistant/scripts/extract_address_from_pdf.py
Sisyphus bd213eb5ec feat: 采集器地址提取优化 - 省份关键词匹配、标题兜底、跨天去重
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-26 16:24:32 +08:00

133 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
从招标公告 PDF 中提取地址信息。
用法: python3 extract_address_from_pdf.py <pdf文件路径> [公告标题]
输出: JSON {"region": "省/市", "regionDetail": "详细地址"}
"""
import fitz
import re
import json
import sys
# 全国省份/直辖市/自治区名称(含简称、别称)
PROVINCES = [
'北京', '天津', '上海', '重庆',
'河北', '山西', '辽宁', '吉林', '黑龙江',
'江苏', '浙江', '安徽', '福建', '江西', '山东',
'河南', '湖北', '湖南', '广东', '海南',
'四川', '贵州', '云南', '陕西', '甘肃', '青海',
'台湾', '内蒙古', '广西', '西藏', '宁夏', '新疆',
]
TWO_CHAR_PROVS = [p for p in PROVINCES if len(p) == 2]
SPECIAL_PROVS = ['内蒙古', '广西', '新疆', '西藏', '宁夏']
def extract_province_best(text):
"""从任意文本中提取最可能的省份(先关键词匹配,避免正则在多字省份上截断)"""
if not text:
return ''
text_clean = text.replace(' ', '')
# 1. 特殊多字省份(优先,避免"新疆维吾尔自治区"被截)
for prov in SPECIAL_PROVS:
if prov in text_clean:
return prov
# 2. 直辖市
for prov in ['北京', '天津', '上海', '重庆']:
if prov in text_clean:
return prov
# 3. "XX省"(如"陕西省"、"广东省")
m = re.search(r'([\u4e00-\u9fff]{2}省)', text)
if m:
return m.group(1)[:2]
# 4. 两字省份关键词
for prov in TWO_CHAR_PROVS:
if prov in text_clean:
return prov
return ''
def extract_city_from_text(text):
"""从文本中提取城市名(XX市)"""
if not text:
return ''
m = re.search(r'([\u4e00-\u9fff]{2,4}(?:市))', text)
if m:
city = m.group(1)
if city not in ['北京市', '天津市', '上海市', '重庆市']:
return city
return ''
def extract_address(pdf_path: str, title: str = '') -> dict:
pdf_text = ''
try:
doc = fitz.open(pdf_path)
for page in doc:
pdf_text += page.get_text()
doc.close()
except Exception:
pdf_text = ''
result = {'region': '', 'regionDetail': ''}
# ── 1. 从PDF正文中提取详细地址 ──
addr_patterns = [
r'建设地点[::]\s*(.+?)(?:\n|$)',
r'项目地点[::]\s*(.+?)(?:\n|$)',
r'交货地点[::]\s*(.+?)(?:\n|$)',
r'施工地点[::]\s*(.+?)(?:\n|$)',
r'项目地址[::]\s*(.+?)(?:\n|$)',
r'供货地点[::]\s*(.+?)(?:\n|$)',
r'到货地点[::]\s*(.+?)(?:\n|$)',
r'送货地点[::]\s*(.+?)(?:\n|$)',
r'工程地点[::]\s*(.+?)(?:\n|$)',
r'实施地点[::]\s*(.+?)(?:\n|$)',
r'服务地点[::]\s*(.+?)(?:\n|$)',
r'收货地址[::]\s*(.+?)(?:\n|$)',
r'所在地[::]\s*(.+?)(?:\n|$)',
]
addr = ''
for p in addr_patterns:
m = re.search(p, pdf_text)
if m:
addr = m.group(1).strip()
if len(addr) >= 4:
break
addr = ''
if addr:
result['regionDetail'] = addr
prov = extract_province_best(addr)
if prov:
result['region'] = prov
else:
city = extract_city_from_text(addr)
if city:
result['region'] = city
# ── 2. 正文没提取到region,从整个PDF文本中搜索 ──
if not result['region'] and pdf_text:
prov = extract_province_best(pdf_text)
if prov:
result['region'] = prov
# ── 3. 还没提取到,从标题中搜索 ──
if not result['region'] and title:
prov = extract_province_best(title)
if prov:
result['region'] = prov
return result
if __name__ == '__main__':
if len(sys.argv) < 2:
print(json.dumps({'region': '', 'regionDetail': ''}))
sys.exit(0)
pdf_path = sys.argv[1]
title = sys.argv[2] if len(sys.argv) > 2 else ''
result = extract_address(pdf_path, title)
print(json.dumps(result, ensure_ascii=False))