auto: 2026-07-26 18:32 (ESD_LAPTOP)
This commit is contained in:
@@ -49,4 +49,9 @@ APP_PUBLIC_BASE_URL=http://localhost:5173
|
||||
|
||||
# Gitea 토큰 66a40de16156431db0dfbf86149f8bafcec4832f
|
||||
# 국토정보플랫폼 API키 : C7111C0D1C2D353F37EDE33145E2A286DF8D8EC6CA
|
||||
# 브이월드 API키 : 3DBD7306-7DBD-38BB-B292-267C5ED7AC6B
|
||||
# 브이월드 API키 : 3DBD7306-7DBD-38BB-B292-267C5ED7AC6B
|
||||
|
||||
# 브이월드 도엽 자동 다운로드 (세션 만료 시 자동 재로그인)
|
||||
VWORLD_LOGIN_ID=usdd6595
|
||||
VWORLD_LOGIN_PW=f4sQ2L3^^Bn7ZY
|
||||
VWORLD_SESSION_COOKIE=
|
||||
@@ -24,9 +24,14 @@ import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import base64
|
||||
import http.cookiejar
|
||||
|
||||
from config.config_system import (
|
||||
MAP_SHEETS_DIR,
|
||||
MAP_SHEETS_INDEX_PATH,
|
||||
VWORLD_LOGIN_ID,
|
||||
VWORLD_LOGIN_PW,
|
||||
VWORLD_SESSION_COOKIE,
|
||||
)
|
||||
|
||||
@@ -43,6 +48,48 @@ VWORLD_DATASET_URL = "https://www.vworld.kr/dtmk/dtmk_ntads_s002.do?dsId=30205"
|
||||
VWORLD_MAP_URL = "https://map.vworld.kr/map/dtkmap.do"
|
||||
_SEARCH_API = "https://map.vworld.kr/ws3dmap/getDisitalMapList.do"
|
||||
_DOWNLOAD_API = "https://map.vworld.kr/dtkmap/digitalDownload.do"
|
||||
_LOGIN_API = "https://map.vworld.kr/map/im_usrlogin_a004.do"
|
||||
|
||||
# 프로세스 내 로그인 세션 캐시 (만료 시 자동 재로그인으로 갱신)
|
||||
_session_cookie_cache: str = ""
|
||||
|
||||
|
||||
def vworld_login() -> str:
|
||||
"""id/pw로 브이월드 지도서비스 로그인, Cookie 헤더 문자열 반환.
|
||||
|
||||
로그인 폼은 Base64 인코딩 필드(usrIdeE/usrPwdE)를 받는 단순 POST — 캡차 없음 실측.
|
||||
"""
|
||||
if not (VWORLD_LOGIN_ID and VWORLD_LOGIN_PW):
|
||||
raise PermissionError(".env에 VWORLD_LOGIN_ID / VWORLD_LOGIN_PW 설정 필요")
|
||||
|
||||
jar = http.cookiejar.CookieJar()
|
||||
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
|
||||
opener.open(VWORLD_MAP_URL, timeout=30).read()
|
||||
|
||||
form = urllib.parse.urlencode(
|
||||
{
|
||||
"prevUrl": "",
|
||||
"nextUrl": "",
|
||||
"usrIdeE": base64.b64encode(VWORLD_LOGIN_ID.encode()).decode(),
|
||||
"usrPwdE": base64.b64encode(VWORLD_LOGIN_PW.encode()).decode(),
|
||||
"callback": "loginSuccessCall",
|
||||
"isJson": "1",
|
||||
"usrIde": "",
|
||||
"usrPwd": "",
|
||||
}
|
||||
).encode()
|
||||
req = urllib.request.Request(
|
||||
_LOGIN_API,
|
||||
data=form,
|
||||
headers={"Referer": VWORLD_MAP_URL, "X-Requested-With": "XMLHttpRequest"},
|
||||
)
|
||||
body = opener.open(req, timeout=30).read().decode("utf-8", errors="replace")
|
||||
if '"result" : "success"' not in body and '"result":"success"' not in body:
|
||||
raise PermissionError(f"브이월드 로그인 실패: {body.strip()[:200]}")
|
||||
|
||||
global _session_cookie_cache
|
||||
_session_cookie_cache = "; ".join(f"{c.name}={c.value}" for c in jar)
|
||||
return _session_cookie_cache
|
||||
|
||||
|
||||
def search_sheet_files(sheet_no: str, version: int = 2, scale: int = 5000) -> list[dict]:
|
||||
@@ -66,24 +113,8 @@ def search_sheet_files(sheet_no: str, version: int = 2, scale: int = 5000) -> li
|
||||
return data.get("RSLT_DATA") or []
|
||||
|
||||
|
||||
def download_sheet(sheet_no: str, session_cookie: str = "", version: int = 2) -> dict:
|
||||
"""도엽 파일 다운로드 후 영구저장소 등록. 로그인 세션 쿠키 필수.
|
||||
|
||||
session_cookie: 로그인된 map.vworld.kr 브라우저의 Cookie 헤더 문자열
|
||||
(예: "JSESSIONID=..; SSCSID=.."). 생략 시 .env의 VWORLD_SESSION_COOKIE 사용.
|
||||
세션은 서버 유휴 타임아웃으로 만료됨 — 만료 시 PermissionError 발생, 쿠키 갱신 필요.
|
||||
"""
|
||||
session_cookie = session_cookie or VWORLD_SESSION_COOKIE
|
||||
if not session_cookie:
|
||||
raise PermissionError(
|
||||
f"도엽 {sheet_no}: 세션 쿠키 없음 — "
|
||||
".env VWORLD_SESSION_COOKIE 설정 또는 수동 다운로드 필요"
|
||||
)
|
||||
rows = search_sheet_files(sheet_no, version=version)
|
||||
if not rows:
|
||||
raise LookupError(f"도엽 {sheet_no}: 브이월드 검색 결과 없음")
|
||||
row = rows[0]
|
||||
|
||||
def _fetch_sheet_payload(row: dict, session_cookie: str) -> bytes:
|
||||
"""도엽 파일 바이너리 요청. zip 시그니처가 아니면 PermissionError(세션 만료)."""
|
||||
params = urllib.parse.urlencode({"DS_SQ": row["ds_sq"], "FILE_SQ": row["file_no"]})
|
||||
req = urllib.request.Request(
|
||||
f"{_DOWNLOAD_API}?{params}",
|
||||
@@ -91,11 +122,35 @@ def download_sheet(sheet_no: str, session_cookie: str = "", version: int = 2) ->
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=300) as resp:
|
||||
payload = resp.read()
|
||||
|
||||
if payload[:2] != b"PK":
|
||||
# zip 시그니처 아님 → 로그인 만료 등으로 알림 HTML이 온 경우
|
||||
snippet = payload[:200].decode("utf-8", errors="replace")
|
||||
raise PermissionError(f"도엽 {sheet_no}: 다운로드 실패(로그인 세션 확인 필요): {snippet}")
|
||||
raise PermissionError(f"다운로드 실패(로그인 세션 확인 필요): {snippet}")
|
||||
return payload
|
||||
|
||||
|
||||
def download_sheet(sheet_no: str, session_cookie: str = "", version: int = 2) -> dict:
|
||||
"""도엽 파일 다운로드 후 영구저장소 등록. 세션 만료 시 id/pw로 자동 재로그인 1회 재시도.
|
||||
|
||||
쿠키 우선순위: 인자 > 프로세스 캐시 > .env VWORLD_SESSION_COOKIE > 자동 로그인.
|
||||
"""
|
||||
rows = search_sheet_files(sheet_no, version=version)
|
||||
if not rows:
|
||||
raise LookupError(f"도엽 {sheet_no}: 브이월드 검색 결과 없음")
|
||||
row = rows[0]
|
||||
|
||||
cookie = session_cookie or _session_cookie_cache or VWORLD_SESSION_COOKIE
|
||||
payload = None
|
||||
if cookie:
|
||||
try:
|
||||
payload = _fetch_sheet_payload(row, cookie)
|
||||
except PermissionError:
|
||||
payload = None # 세션 만료 → 자동 재로그인 폴백
|
||||
if payload is None:
|
||||
cookie = vworld_login()
|
||||
try:
|
||||
payload = _fetch_sheet_payload(row, cookie)
|
||||
except PermissionError as e:
|
||||
raise PermissionError(f"도엽 {sheet_no}: 재로그인 후에도 실패: {e}") from e
|
||||
|
||||
Path(MAP_SHEETS_DIR).mkdir(parents=True, exist_ok=True)
|
||||
tmp_path = Path(MAP_SHEETS_DIR) / f"_dl_{row['file_nm']}"
|
||||
|
||||
@@ -434,8 +434,10 @@ STORAGE_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "sto
|
||||
MAP_SHEETS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "map_sheets")
|
||||
MAP_SHEETS_INDEX_PATH = os.path.join(MAP_SHEETS_DIR, "map_sheets_index.json")
|
||||
|
||||
# 브이월드 지도서비스 도엽 자동 다운로드용 로그인 세션 쿠키 (.env에 설정, 만료 시 갱신)
|
||||
# 예: VWORLD_SESSION_COOKIE="JSESSIONID=..; SSCSID=.."
|
||||
# 브이월드 지도서비스 도엽 자동 다운로드 — 세션 만료 시 id/pw로 자동 재로그인 (.env)
|
||||
VWORLD_LOGIN_ID = os.getenv("VWORLD_LOGIN_ID", "")
|
||||
VWORLD_LOGIN_PW = os.getenv("VWORLD_LOGIN_PW", "")
|
||||
# 선택: 수동 발급 세션 쿠키 (예: "JSESSIONID=..; SSCSID=.."). 비어 있으면 자동 로그인 사용.
|
||||
VWORLD_SESSION_COOKIE = os.getenv("VWORLD_SESSION_COOKIE", "")
|
||||
|
||||
# 시스템 리소스 로그 (루트 log 폴더에 단일 파일, 1개월 보관)
|
||||
|
||||
Reference in New Issue
Block a user