From 1ddaecf4efc6558e1dda0fe165761d0f7b57ac53 Mon Sep 17 00:00:00 2001 From: Taehoon Date: Thu, 26 Feb 2026 17:49:22 +0900 Subject: [PATCH] =?UTF-8?q?crawler=5Fapi.py=20-=20=ED=81=B4=EB=A6=AD?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crawler_api.py | 67 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/crawler_api.py b/crawler_api.py index fc3c696..82471a1 100644 --- a/crawler_api.py +++ b/crawler_api.py @@ -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"