Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
133 lines
4.1 KiB
Python
133 lines
4.1 KiB
Python
#!/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))
|