#!/usr/bin/env python3 """ 从招标公告 PDF 中提取地址信息。 用法: python3 extract_address_from_pdf.py [公告标题] 输出: 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))