DB_JOB/작업파일/완료_1-2주차/1주차(5.19~5.25)/고용노동부(완료)/페이지_마크찾기_allinone.py
hehihoho3 df16c98366 백업: DB수집 전체 스냅샷 (공공기관2 정리 전)
공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:15:40 +09:00

183 lines
8.2 KiB
Python

import os
import re
import time
import requests
import openpyxl
from bs4 import BeautifulSoup
from urllib.parse import urljoin
# -------------------------------------------------------------------------
# [설정 항목]
# -------------------------------------------------------------------------
DOMAIN = "https://www.moel.go.kr/"
# [필독] 현재 사용 중이신 원본 엑셀 파일명을 정확히 적어주세요.
INPUT_EXCEL_FILE = r"D:\01.프로젝트\DB수집\고용노동부\고용노동부_사이트맵_r2.xlsx"
OUTPUT_EXCEL_FILE = r"D:\01.프로젝트\DB수집\고용노동부\고용노동부_사이트맵_r3.xlsx" # 결과가 저장될 파일명
# -------------------------------------------------------------------------
# [헬퍼 함수] 공공누리 라이선스 유형 추출
# -------------------------------------------------------------------------
def extract_license_type(href):
if not href:
return None
if 'www.kogl.or.kr/info/licenseType' in href:
try:
start_idx = href.find('licenseType') + len('licenseType')
num = href[start_idx]
if num.isdigit():
return f"{num}유형"
except Exception:
pass
return None
def find_kogl_license(soup):
for a_tag in soup.find_all('a', href=True):
href = a_tag['href']
license_type = extract_license_type(href)
if license_type:
return license_type
return None
# -------------------------------------------------------------------------
# [메인 로직]
# -------------------------------------------------------------------------
def main():
if not os.path.exists(INPUT_EXCEL_FILE):
print(f"[오류] '{INPUT_EXCEL_FILE}' 파일이 해당 경로에 존재하지 않습니다.")
return
# 기존 엑셀 파일 로드
wb = openpyxl.load_workbook(INPUT_EXCEL_FILE)
ws = wb.active # 활성화된 시트 선택 (필요시 ws = wb['시트명'] 사용)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
# K열(11번째 열), 3번째 행부터 마지막 행까지 순회
# openpyxl에서 K열은 11번 열입니다.
start_row = 3
max_row = ws.max_row
print(f"[*] 엑셀 데이터를 로드했습니다. (3행부터 {max_row}행까지 탐색 시작)")
for row in range(start_row, max_row + 1):
url_cell_value = ws.cell(row=row, column=11).value # K열 데이터 추출
# URL 주소가 비어있거나 문자열이 아니면 패스
if not url_cell_value or not str(url_cell_value).strip().startswith("http"):
continue
url = str(url_cell_value).strip()
print(f"\n[{row}행 작업 시작] {url}")
# 타 도메인(예: safetyreport.go.kr, ifine.moel.go.kr) 대응용 도메인 추출
# 다른 도메인의 경우에도 해당 도메인 기준으로 절대경로 결합이 가능하도록 처리
current_domain = DOMAIN
if "ifine.moel.go.kr" in url:
current_domain = "https://ifine.moel.go.kr/"
elif "safetyreport.go.kr" in url:
current_domain = "https://www.safetyreport.go.kr/"
try:
res = requests.get(url, headers=headers, timeout=10)
if res.status_code != 200:
print(f"-> 접근 실패 (상태코드: {res.status_code})")
ws.cell(row=row, column=15, value="접근 실패") # O열(판별)에 에러 기록
continue
soup = BeautifulSoup(res.text, 'html.parser')
# 1. 게시판 판별 및 건수(수량) 추출
page_p = soup.find('p', class_='page')
total_span = page_p.find('span', class_='total') if page_p else None
is_board = False
total_count = 0
if total_span:
is_board = True
b_tag = total_span.find('b')
count_text = b_tag.get_text() if b_tag else total_span.get_text()
count_digits = re.sub(r'[^0-9]', '', count_text)
if count_digits:
total_count = int(count_digits)
board_type = "게시판" if is_board else "페이지"
print(f" - 판별: {board_type} / 수량: {total_count if is_board else '-'}")
license_found = None
mark_location = ""
# 2. 1차 탐색 (<div id="contents"> 내부 영역 탐색)
contents_div = soup.find('div', id='contents')
search_area = contents_div if contents_div else soup
list_license = find_kogl_license(search_area)
if list_license:
license_found = list_license
mark_location = "게시판"
print(f" => [목록 마크 발견] 유형: {license_found} (상세 skip)")
elif is_board:
# 목록에 없으면 상세 글 최대 5개 추출 후 탐색
print(" - 목록 내 마크 없음 -> 상세 글 5개 탐색 진행")
table = search_area.find('table', class_='tstyle_list')
detail_links = []
if table:
a_tags = table.select('td.txt_left strong.b_tit a')
for a in a_tags[:5]:
href = a.get('href', '')
if href:
detail_links.append(urljoin(current_domain, href))
# 대체 패턴 검색 (일반 링크 파싱)
if not detail_links:
for a in search_area.find_all('a', href=True):
href = a['href']
if 'View.do' in href or 'view.do' in href or 'seqRepeat=' in href:
full_url = urljoin(current_domain, href)
if full_url not in detail_links:
detail_links.append(full_url)
if len(detail_links) >= 5:
break
# 상세 페이지 탐색
for idx, detail_url in enumerate(detail_links, start=1):
try:
time.sleep(0.4)
detail_res = requests.get(detail_url, headers=headers, timeout=7)
if detail_res.status_code == 200:
d_soup = BeautifulSoup(detail_res.text, 'html.parser')
d_contents = d_soup.find('div', id='contents')
d_search_area = d_contents if d_contents else d_soup
detail_license = find_kogl_license(d_search_area)
if detail_license:
license_found = detail_license
mark_location = "게시물"
print(f" > 상세 ({idx}/5) 마크 발견: {license_found}")
break
except Exception:
pass
# 3. 데이터 매핑하여 기존 셀에 값 쓰기
# M열 = 13번째 열, O열 = 15번째 열, P열 = 16번째 열, Q열 = 17번째 열
ws.cell(row=row, column=13, value=total_count if is_board else "") # M열: 수량
ws.cell(row=row, column=15, value=board_type) # O열: 판별
ws.cell(row=row, column=16, value=mark_location) # P열: 마크 위치
ws.cell(row=row, column=17, value=license_found if license_found else "") # Q열: 유형
except Exception as e:
print(f" [Error] {row}행 처리 중 에러 발생: {e}")
ws.cell(row=row, column=15, value="에러 발생")
# 변경 내용 저장
wb.save(OUTPUT_EXCEL_FILE)
print(f"\n[작업 완료] 모든 결과가 '{OUTPUT_EXCEL_FILE}'에 덮어쓰기 완료되었습니다.")
if __name__ == "__main__":
main()