Update app.py
Browse files
app.py
CHANGED
|
@@ -109,37 +109,46 @@ LOCAL_GOV_KEYWORDS = [
|
|
| 109 |
|
| 110 |
# 지역 매칭을 위한 상세 키워드 (지역명 -> 매칭 키워드들)
|
| 111 |
REGION_MATCH_KEYWORDS = {
|
| 112 |
-
"서울": ["서울", "서울특별시", "서울시"
|
| 113 |
-
|
| 114 |
-
"
|
| 115 |
-
"
|
| 116 |
-
"
|
| 117 |
-
"
|
| 118 |
-
"
|
| 119 |
-
"
|
| 120 |
-
"
|
|
|
|
|
|
|
| 121 |
"남양주", "화성", "평택", "의정부", "시흥", "파주", "김포", "광명", "광주시",
|
| 122 |
"군포", "하남", "오산", "이천", "안성", "의왕", "양평", "여주", "과천",
|
| 123 |
"구리", "포천", "동두천", "가평", "연천"],
|
| 124 |
-
"강원": ["강원", "강원도", "강원특별자치도", "
|
|
|
|
| 125 |
"속초", "삼척", "홍천", "횡성", "영월", "평창", "정선", "철원", "화천",
|
| 126 |
"양구", "인제", "고성", "양양"],
|
| 127 |
-
"충북": ["충북", "충청북도", "
|
|
|
|
| 128 |
"진천", "괴산", "음성", "단양"],
|
| 129 |
-
"충남": ["충남", "충청남도", "
|
|
|
|
| 130 |
"당진", "금산", "부여", "서천", "청양", "홍성", "예산", "태안"],
|
| 131 |
-
"전북": ["전북", "전라북도", "전북특별자치도", "전
|
|
|
|
| 132 |
"김제", "완주", "진안", "무주", "장수", "임실", "순창", "고창", "부안"],
|
| 133 |
-
"전남": ["전남", "전라남도", "
|
|
|
|
| 134 |
"구례", "고흥", "보성", "화순", "장흥", "강진", "해남", "영암", "무안",
|
| 135 |
"함평", "영광", "장성", "완도", "진도", "신안"],
|
| 136 |
-
"경북": ["경북", "경상북도", "
|
|
|
|
| 137 |
"상주", "문경", "경산", "군위", "의성", "청송", "영양", "영덕", "청도",
|
| 138 |
"고령", "성주", "칠곡", "예천", "봉화", "울진", "울릉"],
|
| 139 |
-
"경남": ["경남", "경상남도", "
|
|
|
|
| 140 |
"양산", "의령", "함안", "창녕", "고성", "남해", "하동", "산청", "함양",
|
| 141 |
"거창", "합천"],
|
| 142 |
-
"제주": ["제주", "제주도", "제주특별자치도", "제주시", "서귀포"]
|
| 143 |
}
|
| 144 |
|
| 145 |
|
|
@@ -160,24 +169,52 @@ def extract_region_from_text(text: str) -> Optional[str]:
|
|
| 160 |
if kw in region_text:
|
| 161 |
return region
|
| 162 |
|
| 163 |
-
# 2. 텍스트 전체에서 지역 키워드 검색
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
for kw in keywords:
|
| 166 |
-
# 정확한 매칭을 위해 키워드가 단어 경계에 있는지 확인
|
| 167 |
if kw in text:
|
| 168 |
# "광주"의 경우 "경기도 광주시"와 "광주광역시" 구분 필요
|
| 169 |
-
if
|
| 170 |
-
if "
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
# 경기도 광주시인 경우 경기로 처리
|
| 174 |
-
if "경기" in text:
|
| 175 |
-
return "경기"
|
| 176 |
-
else:
|
| 177 |
-
return "광주"
|
| 178 |
else:
|
| 179 |
return region
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
return None
|
| 182 |
|
| 183 |
|
|
@@ -524,14 +561,19 @@ def fetch_announcements(
|
|
| 524 |
|
| 525 |
try:
|
| 526 |
# API에서 더 많은 데이터 요청 (필터링 후 충분한 결과를 위해)
|
| 527 |
-
# 기관유형
|
| 528 |
-
|
| 529 |
org_type != "전체" or
|
| 530 |
region != "전체(지역)" or
|
| 531 |
status_filter == "진행중"
|
| 532 |
)
|
| 533 |
-
|
| 534 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
|
| 536 |
params = {
|
| 537 |
"crtfcKey": API_KEY,
|
|
@@ -602,17 +644,17 @@ def fetch_announcements(
|
|
| 602 |
# 수행기관
|
| 603 |
exec_org = item.get("excInsttNm", "") or ""
|
| 604 |
|
| 605 |
-
# 지역 추출 (사업명 > 소관기관 > 수행기관 순으로 확인)
|
| 606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
if not item_region:
|
| 608 |
item_region = extract_region_from_text(author)
|
| 609 |
if not item_region:
|
| 610 |
item_region = extract_region_from_text(exec_org)
|
| 611 |
-
# 해시태그에서도 지역 정보 확인
|
| 612 |
-
if not item_region:
|
| 613 |
-
hash_tags = item.get("hashTags", "")
|
| 614 |
-
if hash_tags:
|
| 615 |
-
item_region = extract_region_from_text(hash_tags)
|
| 616 |
|
| 617 |
# 기관유형 분류
|
| 618 |
item_org_type = classify_org_type(author)
|
|
|
|
| 109 |
|
| 110 |
# 지역 매칭을 위한 상세 키워드 (지역명 -> 매칭 키워드들)
|
| 111 |
REGION_MATCH_KEYWORDS = {
|
| 112 |
+
"서울": ["서울", "서울특별시", "서울시", "SBA", "서울산업진흥원", "서울경제진흥원",
|
| 113 |
+
"서울신용보증재단", "서울테크노파크", "서울창업허브", "SH공사"],
|
| 114 |
+
"부산": ["부산", "부산광역시", "부산시", "부산경제진흥원", "부산테크노파크"],
|
| 115 |
+
"대구": ["대구", "대구광역시", "대구시", "대구경북", "대구테크노파크"],
|
| 116 |
+
"인천": ["인천", "인천광역시", "인천시", "인천경제자유구역", "인천테크노파크"],
|
| 117 |
+
"광주": ["광주", "광주광역시", "광주시", "광주전남", "광주테크노파크"], # 경기도 광주시와 구분 필요
|
| 118 |
+
"대전": ["대전", "대전광역시", "대전시", "대전테크노파크", "대전경제통상진흥원"],
|
| 119 |
+
"울산": ["울산", "울산광역시", "울산시", "울산테크노파크", "울산경제진흥원"],
|
| 120 |
+
"세종": ["세종", "세종특별자치시", "세종시", "세종테크노파크"],
|
| 121 |
+
"경기": ["경기", "경기도", "경기TP", "경기테크노파크", "경기경제과학진흥원",
|
| 122 |
+
"수원", "성남", "고양", "용인", "부천", "안산", "안양",
|
| 123 |
"남양주", "화성", "평택", "의정부", "시흥", "파주", "김포", "광명", "광주시",
|
| 124 |
"군포", "하남", "오산", "이천", "안성", "의왕", "양평", "여주", "과천",
|
| 125 |
"구리", "포천", "동두천", "가평", "연천"],
|
| 126 |
+
"강원": ["강원", "강원도", "강원특별자치도", "강원TP", "강원테크노파크", "강원경제진흥원",
|
| 127 |
+
"춘천", "원주", "강릉", "동해", "태백",
|
| 128 |
"속초", "삼척", "홍천", "횡성", "영월", "평창", "정선", "철원", "화천",
|
| 129 |
"양구", "인제", "고성", "양양"],
|
| 130 |
+
"충북": ["충북", "충청북도", "충북TP", "충북테크노파크", "충청북도기업진흥원",
|
| 131 |
+
"청주", "충주", "제천", "보은", "옥천", "영동", "증평",
|
| 132 |
"진천", "괴산", "음성", "단양"],
|
| 133 |
+
"충남": ["충남", "충청남도", "충남TP", "충남테크노파크", "충남경제진흥원",
|
| 134 |
+
"천안", "공주", "보령", "아산", "서산", "논산", "계룡",
|
| 135 |
"당진", "금산", "부여", "서천", "청양", "홍성", "예산", "태안"],
|
| 136 |
+
"전북": ["전북", "전라북도", "전북특별자치도", "전북TP", "전북테크노파크", "전북경제통상진흥원",
|
| 137 |
+
"전주", "군산", "익산", "정읍", "남원",
|
| 138 |
"김제", "완주", "진안", "무주", "장수", "임실", "순창", "고창", "부안"],
|
| 139 |
+
"전남": ["전남", "전라남도", "전남TP", "전남테크노파크", "전남경제통상진흥원",
|
| 140 |
+
"목포", "여수", "순천", "나주", "광양", "담양", "곡성",
|
| 141 |
"구례", "고흥", "보성", "화순", "장흥", "강진", "해남", "영암", "무안",
|
| 142 |
"함평", "영광", "장성", "완도", "진도", "신안"],
|
| 143 |
+
"경북": ["경북", "경상북도", "경북TP", "경북테크노파크", "경북경제진흥원",
|
| 144 |
+
"포항", "경주", "김천", "안동", "구미", "영주", "영천",
|
| 145 |
"상주", "문경", "경산", "군위", "의성", "청송", "영양", "영덕", "청도",
|
| 146 |
"고령", "성주", "칠곡", "예천", "봉화", "울진", "울릉"],
|
| 147 |
+
"경남": ["경남", "경상남도", "경남TP", "경남테크노파크", "경남경제진흥원",
|
| 148 |
+
"창원", "진주", "��영", "사천", "김해", "밀양", "거제",
|
| 149 |
"양산", "의령", "함안", "창녕", "고성", "남해", "하동", "산청", "함양",
|
| 150 |
"거창", "합천"],
|
| 151 |
+
"제주": ["제주", "제주도", "제주특별자치도", "제주시", "서귀포", "제주TP", "제주테크노파크"]
|
| 152 |
}
|
| 153 |
|
| 154 |
|
|
|
|
| 169 |
if kw in region_text:
|
| 170 |
return region
|
| 171 |
|
| 172 |
+
# 2. 텍스트 전체에서 지역 키워드 검색 (우선순위 있게)
|
| 173 |
+
# 광역시/특별시 먼저 체크 (더 구체적인 것 우선)
|
| 174 |
+
priority_regions = ["서울", "부산", "대구", "인천", "광주", "대전", "울산", "세종"]
|
| 175 |
+
|
| 176 |
+
for region in priority_regions:
|
| 177 |
+
keywords = REGION_MATCH_KEYWORDS.get(region, [])
|
| 178 |
for kw in keywords:
|
|
|
|
| 179 |
if kw in text:
|
| 180 |
# "광주"의 경우 "경기도 광주시"와 "광주광역시" 구분 필요
|
| 181 |
+
if region == "광주":
|
| 182 |
+
if "경기" in text or "경기도" in text:
|
| 183 |
+
continue # 경기도 광주시이므로 스킵
|
| 184 |
+
return "광주"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
else:
|
| 186 |
return region
|
| 187 |
|
| 188 |
+
# 3. 도 단위 지역 체크
|
| 189 |
+
province_regions = ["경기", "강원", "충북", "충남", "전북", "전남", "경북", "경남", "제주"]
|
| 190 |
+
for region in province_regions:
|
| 191 |
+
keywords = REGION_MATCH_KEYWORDS.get(region, [])
|
| 192 |
+
for kw in keywords:
|
| 193 |
+
if kw in text:
|
| 194 |
+
return region
|
| 195 |
+
|
| 196 |
+
return None
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def extract_region_from_hashtags(hash_tags: str) -> Optional[str]:
|
| 200 |
+
"""해시태그 문자열에서 지역 정보 추출 (쉼표로 구분된 형태)"""
|
| 201 |
+
if not hash_tags:
|
| 202 |
+
return None
|
| 203 |
+
|
| 204 |
+
# 해시태그는 "2026,금융,서울,중소벤처기업부" 형태
|
| 205 |
+
tags = [t.strip() for t in hash_tags.split(",")]
|
| 206 |
+
|
| 207 |
+
# 정확히 지역명과 일치하는 태그 찾기
|
| 208 |
+
region_names = list(REGION_MATCH_KEYWORDS.keys())
|
| 209 |
+
|
| 210 |
+
for tag in tags:
|
| 211 |
+
if tag in region_names:
|
| 212 |
+
return tag
|
| 213 |
+
# 약간의 변형도 체크
|
| 214 |
+
for region in region_names:
|
| 215 |
+
if region in tag or tag in REGION_MATCH_KEYWORDS.get(region, []):
|
| 216 |
+
return region
|
| 217 |
+
|
| 218 |
return None
|
| 219 |
|
| 220 |
|
|
|
|
| 561 |
|
| 562 |
try:
|
| 563 |
# API에서 더 많은 데이터 요청 (필터링 후 충분한 결과를 위해)
|
| 564 |
+
# 지역+기관유형 필터링 시 API가 완벽하게 필터링하지 못하므로 최대한 많이 가져옴
|
| 565 |
+
needs_client_filter = (
|
| 566 |
org_type != "전체" or
|
| 567 |
region != "전체(지역)" or
|
| 568 |
status_filter == "진행중"
|
| 569 |
)
|
| 570 |
+
|
| 571 |
+
if needs_client_filter:
|
| 572 |
+
# 클라이언트 필터링 필요 시 항상 최대 500건 요청
|
| 573 |
+
api_fetch_count = 500
|
| 574 |
+
else:
|
| 575 |
+
# 필터 없을 때는 페이지에 필요한 만큼만
|
| 576 |
+
api_fetch_count = min(500, rows * page + 50)
|
| 577 |
|
| 578 |
params = {
|
| 579 |
"crtfcKey": API_KEY,
|
|
|
|
| 644 |
# 수행기관
|
| 645 |
exec_org = item.get("excInsttNm", "") or ""
|
| 646 |
|
| 647 |
+
# 지역 추출 (해시태그 > 사업명 > 소관기관 > 수행기관 순으로 확인)
|
| 648 |
+
# 해시태그가 가장 정확한 지역 정보를 담고 있음
|
| 649 |
+
hash_tags = item.get("hashTags", "")
|
| 650 |
+
item_region = extract_region_from_hashtags(hash_tags)
|
| 651 |
+
|
| 652 |
+
if not item_region:
|
| 653 |
+
item_region = extract_region_from_text(title)
|
| 654 |
if not item_region:
|
| 655 |
item_region = extract_region_from_text(author)
|
| 656 |
if not item_region:
|
| 657 |
item_region = extract_region_from_text(exec_org)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
|
| 659 |
# 기관유형 분류
|
| 660 |
item_org_type = classify_org_type(author)
|