공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98 lines
4.5 KiB
Python
98 lines
4.5 KiB
Python
import os
|
|
import time
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
from requests.adapters import HTTPAdapter
|
|
from urllib3.util import Retry
|
|
from openpyxl import load_workbook
|
|
|
|
|
|
def fill_board_counts_only(file_path, output_path):
|
|
# 1. 원본 서식을 그대로 유지하기 위해 openpyxl로 로드
|
|
wb = load_workbook(file_path)
|
|
ws = wb.active
|
|
|
|
# openpyxl 열 번호 지정 (1부터 시작)
|
|
K_col = 11 # K열 (URL)
|
|
L_col = 12 # L열 (게시판구분)
|
|
M_col = 13 # M열 (수량/건수)
|
|
|
|
# 강력한 웹 크롤링 세션 구성
|
|
session = requests.Session()
|
|
retry_strategy = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
|
|
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
session.mount("http://", adapter)
|
|
session.mount("https://", adapter)
|
|
session.headers.update({
|
|
"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",
|
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
"Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
})
|
|
|
|
total_rows = ws.max_row
|
|
print(f"총 {total_rows}행이 확인되었습니다. 87행부터 L/M열 수집을 시작합니다...\n")
|
|
|
|
# 87행부터 마지막 행까지 순차적으로 탐색
|
|
for current_row in range(87, total_rows + 1):
|
|
target_url = str(ws.cell(row=current_row, column=K_col).value).strip()
|
|
|
|
# URL 주소가 비어있거나 올바르지 않으면 패스
|
|
if not target_url.startswith("http") or target_url == "None":
|
|
print(f"[{current_row} / {total_rows}행] -> URL이 없거나 올바르지 않습니다. (건너뜀)")
|
|
continue
|
|
|
|
print(f"[{current_row} / {total_rows}행] 처리 중... -> URL: {target_url}")
|
|
|
|
# 1. K열 URL이 /list.do로 끝나는지 검사
|
|
# 쿼리 스트링(?...)이 붙어 있을 수 있으므로 분리 후 매칭
|
|
url_path = target_url.split('?')[0]
|
|
|
|
if url_path.endswith("/list.do"):
|
|
# L열에 '게시판' 작성
|
|
ws.cell(row=current_row, column=L_col).value = "게시판"
|
|
|
|
# 방화벽 우회를 위한 안전 딜레이
|
|
time.sleep(0.5)
|
|
|
|
try:
|
|
session.headers.update({"Referer": target_url})
|
|
response = session.get(target_url, timeout=15)
|
|
|
|
if response.status_code == 200:
|
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
|
|
# 2. <div class="total"> 전체 <span>숫자</span> 건 </div> 탐색
|
|
total_div = soup.find("div", class_="total")
|
|
if total_div and total_div.find("span"):
|
|
count_text = total_div.find("span").get_text(strip=True)
|
|
|
|
# 숫자가 정상적이라면 M열에 정수형 숫자로 입력
|
|
if count_text.isdigit():
|
|
ws.cell(row=current_row, column=M_col).value = int(count_text)
|
|
print(f" -> [성공] '게시판' 마킹 및 건수 기입 완료: {count_text}건")
|
|
else:
|
|
ws.cell(row=current_row, column=M_col).value = count_text
|
|
print(f" -> [주의] 텍스트 건수 기입: {count_text}")
|
|
else:
|
|
print(" -> [알림] 게시판 형태이나 total 건수 태그를 찾지 못했습니다.")
|
|
else:
|
|
print(f" -> [실패] HTTP 응답 에러 코드: {response.status_code}")
|
|
|
|
except Exception as e:
|
|
print(f" -> [오류] 웹페이지 접속 및 파싱 실패: {e}")
|
|
else:
|
|
# /list.do로 끝나지 않는 일반 페이지 처리
|
|
ws.cell(row=current_row, column=L_col).value = "페이지"
|
|
print(" -> 일반 '페이지'입니다. (건수 수집 패스)")
|
|
|
|
# 스타일 유지 상태로 안전하게 저장
|
|
wb.save(output_path)
|
|
print(f"\n✨ 수집이 전부 완료되었습니다! 파일 저장 완료:\n{output_path}")
|
|
|
|
|
|
# --- 실행 영역 ---
|
|
input_excel = r"C:\Users\hehih\ownCloud\알바\2주차\외교부\외교부_업데이트_활성화_r4.xlsx"
|
|
output_excel = r"C:\Users\hehih\ownCloud\알바\2주차\외교부\외교부_업데이트_활성화_r5.xlsx"
|
|
|
|
fill_board_counts_only(input_excel, output_excel)
|