crawler_api.py - 클릭방식으로 변환

This commit is contained in:
2026-02-26 17:49:22 +09:00
parent fad1a13d94
commit 1ddaecf4ef

View File

@@ -9,6 +9,7 @@ from fastapi.responses import StreamingResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from playwright.async_api import async_playwright
from dotenv import load_dotenv
from analyze import analyze_file_content
load_dotenv()
@@ -25,10 +26,37 @@ app.add_middleware(
allow_headers=["*"],
)
@app.get("/")
@app.get("/dashboard")
async def get_dashboard():
return FileResponse("dashboard.html")
@app.get("/mailTest")
async def get_mail_test():
return FileResponse("mailTest.html")
@app.get("/attachments")
async def get_attachments():
sample_path = "sample"
if not os.path.exists(sample_path):
os.makedirs(sample_path)
files = []
for f in os.listdir(sample_path):
f_path = os.path.join(sample_path, f)
if os.path.isfile(f_path):
files.append({
"name": f,
"size": f"{os.path.getsize(f_path) / 1024:.1f} KB"
})
return files
@app.get("/analyze-file")
async def analyze_file(filename: str):
return analyze_file_content(filename)
@app.get("/")
async def root():
return FileResponse("index.html")
@app.get("/sync")
async def sync_data():
async def event_generator():
@@ -97,18 +125,18 @@ async def sync_data():
modal_sel = "article.archive-modal"
if await page.locator(modal_sel).is_visible():
yield f"data: {json.dumps({'type': 'log', 'message': ' - [로그] 모달 발견. 데이터 추출 중...'})}\n\n"
# 사용자 제공 정밀 셀렉터 기반 추출
date_sel = "body > article.archive-modal > div > div > div.modal-body > div.log-wrap > div.log-item-wrap.log-body.scrollbar.scroll-container > div.date > div.text"
user_sel = "body > article.archive-modal > div > div > div.modal-body > div.log-wrap > div.log-item-wrap.log-body.scrollbar.scroll-container > div.user > div.text"
act_sel = "body > article.archive-modal > div > div > div.modal-body > div.log-wrap > div.log-item-wrap.log-body.scrollbar.scroll-container > div.activity > div.text"
yield f"data: {json.dumps({'type': 'log', 'message': ' - [로그] 모달 발견. 데이터 로딩 대기...'})}\n\n"
# .log-body 내부의 데이터만 타겟팅하도록 수정
date_sel = "article.archive-modal .log-body .date .text"
user_sel = "article.archive-modal .log-body .user .text"
act_sel = "article.archive-modal .log-body .activity .text"
# 데이터가 나타날 때까지 반복 대기
# 데이터가 나타날 때까지 최대 15초 대기
success_log = False
for _ in range(10):
for _ in range(15):
if await page.locator(date_sel).count() > 0:
raw_date = (await page.locator(date_sel).first.inner_text()).strip()
if raw_date and "활동시간" not in raw_date:
if raw_date:
success_log = True
break
await asyncio.sleep(1)
@@ -148,32 +176,33 @@ async def sync_data():
await asyncio.sleep(0.5)
if popup_page:
yield f"data: {json.dumps({'type': 'log', 'message': ' - [구성] 창 발견. 데이터 로딩 대기 (최대 80초)...'})}\n\n"
target_selector = "#composition-list h6"
yield f"data: {json.dumps({'type': 'log', 'message': ' - [구성] 창 발견. 데이터 로딩 대기 (최대 30초)...'})}\n\n"
# 사용자 제공 정밀 선택자 적용 (nth-child(3)가 실제 데이터)
target_selector = "#composition-list h6:nth-child(3)"
success_comp = False
# 최대 80초간 까지 대기
for _ in range(80):
# 최대 30초간 데이터가 나타날 때까지 대기
for _ in range(30):
h6_count = await popup_page.locator(target_selector).count()
if h6_count > 5: # 일정 개수 이상의 목록이 나타나면 로딩 시작으로 간주
if h6_count > 0:
success_comp = True
break
await asyncio.sleep(1)
if success_comp:
yield f"data: {json.dumps({'type': 'log', 'message': ' - [구성] 데이터 감지됨. 15초간 최종 렌더링 대기...'})}\n\n"
await asyncio.sleep(15) # 완전한 로딩을 위한 강제 대기
yield f"data: {json.dumps({'type': 'log', 'message': ' - [구성] 데이터 감지됨. 최종 렌더링 대기...'})}\n\n"
await asyncio.sleep(10) # 렌더링 안정화를 위한 대기
# 유연한 데이터 수집
# 모든 h6:nth-child(3) 요소를 순회하며 숫자 합산
locators_h6 = popup_page.locator(target_selector)
h6_count = await locators_h6.count()
current_total = 0
for j in range(h6_count):
text = (await locators_h6.nth(j).inner_text()).strip()
# 텍스트 내에서 숫자만 추출 (여러 줄일 경우 마지막 줄 기준)
nums = re.findall(r'\d+', text.split('\n')[-1])
if nums:
val = int(nums[0])
if val < 5000: current_total += val
current_total += int(nums[0])
file_count = current_total
yield f"data: {json.dumps({'type': 'log', 'message': f' - [구성] 성공 ({file_count}개)'})}\n\n"