RAG를 활용한 웹소설 추천 시스템 구현하기(2)
대충 시스템이 굴러가니 이제 데이터를 수집해야 한다. 수집 기준은
- 시리즈/카카오페이지/리디북스의 유료연재작
- 제목/작가/소개글/플랫폼/url(세부페이지)/키워드
- 장르는 키워드에 포함하여 수집
- 성인판/단행본/개정판 여부는 상관하지 않고 데이터 전처리 시 동일한 제목/작가일 경우 중복 작품으로 처리.
- 완결/연재 여부는 수집하지 않음
로 일단 정했는데 무료 연재까지 수집할 경우에는 케이스가 너무 많아져서(작가 사정으로 연재 중단되는 경우 등) 유료 연재작으로 골랐고 플랫폼도 유료 연재작이 많은 곳으로 세 곳을 골랐다.
이렇게 기준을 정했으면 이제 남은 것은... 수집이다.
📌 험난한 Skyvern 사용 실패 후기
사실 원래 크롤링을 하려면 Selenium/PlaywrightBeautifulSoup/Scrapy 등을 많이 사용한다. 원래 크롤링 관련한 라이브러리에 대한 건 밑에 따로 정리해두었다.
하지만 이번에 조금 골이 아팠던 건 여러 플랫폼에서 데이터를 수집해야 하는데 각기 다른 웹 구조를 갖고 있다는 거였다.
아무리 AI 도움을 받는다고 해도 플랫폼 3개 크롤러 만들기는 약간 힘들고 귀찮은 일이긴 해서..(중간에 웹사이트 구조 바뀐다든가 이런 것들 고려해보면...)
그래서 조금 더 쉽게 데이터를 추출할 방도가 없을까 하다가 링크드인에서 Skyvern에 대한 소개를 보고 이번에 한번 적용해보았다.
크롤링 라이브러리 정리
1. BeautifulSoup
- 유형: HTML 파서 (Parser)
- 속도: 매우 빠름 (브라우저 없이 텍스트만 처리)
- 동적 페이지(JS): 불가능
- 난이도: 하
- 특징:
requests라이브러리와 함께 사용하며 가볍고 문법이 간단함. - 주 용도: 뉴스 기사, 블로그 등 단순 정적 페이지 수집.
2. Selenium
- 유형: 브라우저 자동화 도구 (Legacy)
- 속도: 느림 (실제 브라우저 구동으로 무거움)
- 동적 페이지(JS): 가능 (완벽 지원)
- 난이도: 중
- 특징: 가장 오래된 도구로 자료가 방대하지만, 드라이버 버전 관리가 필요하고 설정이 번거로울 수 있음.
- 주 용도: 구형 브라우저 테스트, 오래된 예제 코드를 참고해야 할 때.
3. Playwright
- 유형: 브라우저 자동화 도구 (Modern)
- 속도: 빠름 (Selenium 대비 가볍고 최적화됨)
- 동적 페이지(JS): 가능 (매우 안정적)
- 난이도: 중
- 특징: 설치가 간편하고 자동 대기(Auto-wait) 기능이 있어 코드가 간결함. 최신 웹 기술 호환성이 좋음.
- 주 용도: 로그인, 무한 스크롤 등이 필요한 최신 동적 웹사이트 크롤링.
4. Scrapy
- 유형: 크롤링 프레임워크
- 속도: 매우 빠름 (비동기 처리 지원)
- 동적 페이지(JS): 제한적 (추가 설정 필요)
- 난이도: 상
- 특징: 데이터 수집부터 저장까지 구조화된 파이프라인 제공. 대규모 병렬 처리에 특화됨.
- 주 용도: 쇼핑몰 전체 상품, 포털 사이트 등 대규모 데이터 수집.
❓Skyvern 이란?
공식 문서에 따르면, 'Skyvern은 LLM과 컴퓨터 비전 기술을 활용해 브라우저 기반의 워크플로우를 자동화하는 도구'라고 한다.
더 쉽게 말하자면, 프롬프트 + 컴퓨터 비전+ LLM을 활용해서 웹사이트의 현재 화면에 있는 요소를 실시간으로 분석하고 이해하며 자동으로 작업을 처리하는 도구다.
웹사이트 구조를 이해하고 파서를 처리하는 부분을 따로 넣을 필요 없이 원하는 웹사이트 주소 + 프롬프트를 넣어서 작업을 진행하면 된다.
📌 작동 구조 및 원리
작동 프로세스는 밑과 같다.
- Draw Bounding Boxes (바운딩 박스 그리기) 웹페이지의 스크린샷을 캡처한 후, 클릭 가능한 요소들(버튼, 입력창, 드롭다운 등) 주변에 바운딩 박스를 그린다. 이렇게 하면 LLM이 어떤 요소가 어디에 있는지 시각적으로 파악할 수 있다.
- Parse HTML + Extract the Image (HTML 파싱 및 이미지 추출) 페이지의 HTML 구조를 분석하고, 스크린샷 이미지를 함께 추출한다. 이 두 가지 정보를 결합하면 페이지의 시각적 레이아웃과 구조적 의미를 모두 이해할 수 있다.
- Extract Interactable Elements (인터랙션 가능 요소 추출) 사용자가 상호작용할 수 있는 요소들(텍스트 입력창, 버튼, 체크박스, 드롭다운 등)을 식별하고 목록화한다. 각 요소에 고유 식별자를 부여한다.
- Call LLM to Plan Actions (LLM에게 액션 계획 요청) 추출된 정보와 원래 목표("보험 견적 생성")를 LLM에게 전달한다. LLM은 현재 화면을 분석하고 다음에 수행할 액션들을 계획한다. 예를 들어 "날짜 입력창에 2010/03/10 입력 → Next 버튼 클릭" 같은 구체적인 지시를 생성한다.
- Execute Actions (액션 실행) LLM이 계획한 액션들을 실제로 실행한다. Playwright나 Selenium 같은 브라우저 자동화 도구를 사용해 클릭, 타이핑, 선택 등의 작업을 수행한다.
- Repeat (반복) 새로운 페이지가 로드되면 Step 1로 돌아가 전체 과정을 반복한다. 최종 목표(보험 견적 생성 완료)에 도달할 때까지 이 사이클을 계속한다.
flowchart TB
subgraph Prompt["📝 Prompt: Go to Geico and generate an insurance quote"]
end
subgraph Skyvern["🤖 Skyvern"]
Step1["Step 1: Draw Bounding Boxes"]
Step2["Step 2: Parse HTML + extract the image"]
Step3["Step 3: Extract interactable elements"]
Step4["Step 4: Call a LLM to plan actions"]
Step5["Step 5: Execute actions"]
Step6["Step 6: Repeat"]
Step1 --> Step2
Step2 --> Step3
Step3 --> Step4
Step4 --> Step5
Step5 --> Step6
Step6 --> Step1
end
subgraph Browser1["🌐 Geico.com - License Date"]
Q1["When did you get your license?"]
Input1["📅 YYYY/MM/DD"]
Next1["Next"]
end
subgraph LLM["🧠 LLM"]
LLMQuestion["We're trying to generate an<br/>insurance quote. What should<br/>we do here?"]
Action1["Action 1: Fill in license<br/>date of: 2010/03/10"]
Action2["Action 2: Click Next"]
LLMQuestion --> Action1
Action1 --> Action2
end
subgraph Browser2["🌐 Geico.com - Car Info"]
Q2["What's the make model year of your car?"]
Make["Make ▼"]
Model["Model ▼"]
Year["Year ▼"]
Next2["Next"]
end
Prompt --> Step1
Step1 -.-> Browser1
Step3 -.-> LLM
Step5 -.-> Browser2
style Prompt fill:#f9f9f9,stroke:#333,stroke-width:2px
style Skyvern fill:#e8f4e8,stroke:#333,stroke-width:2px
style Browser1 fill:#fff3e0,stroke:#333,stroke-width:1px
style Browser2 fill:#fff3e0,stroke:#333,stroke-width:1px
style LLM fill:#e3f2fd,stroke:#333,stroke-width:1px
ℹ️ 필요 사항
- Python 3.11
- Poetry
- PostgreSQL 14
✅ 장점
공식 문서에 따르면 이 라이브러리는 밑과 같은 장점을 가진다.
처음 보는 웹사이트도 작동 가능: 별도의 커스텀 코드 없이도 시각적 요소를 필요한 행동과 매핑하여 워크플로우를 완료할 수 있습니다.
레이아웃 변경에 강력함: 미리 정해진 XPath나 셀렉터를 찾아다니는 방식이 아니기 때문에, 웹사이트 구조가 변경되어도 유연하게 대처합니다.
높은 확장성: 워크플로우 수행에 필요한 상호작용을 스스로 추론할 수 있어, 하나의 워크플로우 로직을 수많은 웹사이트에 범용적으로 적용할 수 있습니다.
❌ 단점
실제로 설치를 진행해보고 공식 사이트에서 테스트해본 단점은 밑과 같다
속도가 기대한 만큼 빠르지 않다: 일단 llm이 추출된 정보와 원래 목표("보험 견적 생성")를 바탕으로 현재 화면을 분석하고 다음에 수행할 액션들을 계획하는 과정이 시간이 조금 많이 걸렸다. 10개 크롤링하는데 이 정도면 그냥 이전 방식으로 하는게 낫지 않나..? 싶을 정도로. 그전에 바운딩 박스로 인식하는 부분도 시간이 걸리긴 했는데 llm 질의과정이 들어가니까 확실히 더 느려지더라.
프롬프트를 굉장히 자세히 넣어야 한다.: 일단 공식 사이트에서 테스트하면 내가 요청한 작업에 대해 세부적인 프롬프트를 다시 생성할 수 있다. '카카오 페이지의 전체 웹소설 목록에서 제목/작가/소개글/플랫폼/url(세부페이지)/키워드를 수집해줘. 이때 목록 페이지에는 제목과 이미지만 있으니 세부 페이지로 이동하여 다른 정보를 수집하고 다시 목록 페이지로 돌아와 limit까지 작업을 진행해."를 공식 사이트에서 수정한 프롬프트는 밑과 같다.
당신의 주요 목표는 KakaoPage 웹사이트의 전체 웹소설 목록에서 정보를 수집하는 것입니다.
각 웹소설의 상세 페이지에 방문하여 모든 정보를 수집해야 합니다.
목록 페이지에는 제목과 이미지 정도만 있으므로, 목록 페이지 정보만으로는 절대 수집하지 마세요.
웹소설 1개당 수행해야 할 단계
1. 목록(List) 페이지에서 각 웹소설 카드를 식별합니다.
* 카드에 있는 제목, 작가명을 기록합니다.
* 상세 페이지로 이동하는 링크(URL)를 확인합니다.
2. 상세 페이지로 이동합니다.
상세 페이지에서 다음 정보를 반드시 수집하세요:
전체 소개글(시놉시스)(필수)
장르 태그 및 키워드
상세 페이지의 전체 URL
3. 수집 후 목록 페이지로 돌아옵니다.
* 뒤로가기(back) 또는 제공된 내비게이션을 사용하세요.
4. 다음 웹소설 카드로 이동하여 반복합니다.
5. {limit}개의 웹소설 정보를 모두 수집할 때까지 반복합니다.
추가 조건 및 주의사항
목록 페이지는 무한 스크롤 구조입니다.
화면 끝까지 스크롤하면 자동으로 새로운 웹소설이 로딩됩니다.
스크롤 후 1~2초 기다렸다가 다음 콘텐츠가 로딩되는지 확인하세요.
광고나 배너는 제외하고 수집하세요.
중복된 제목은 결과에서 제거하세요.
각 웹소설 정보는 반드시 상세 페이지에서 수집해야 합니다.
웹소설 1개당 아래 정보를 모두 수집하세요:
* 제목 (Title): 웹소설의 정확한 제목
* 작가 (Author): 작가명 또는 필명
* 소개글 (Introduction): 상세 페이지의 전체 소개글
* URL: 상세 페이지의 전체 주소
* 태그/키워드 (Tags/Keywords): 장르 태그 및 모든 키워드
다음 조건을 만족하면 작업이 완료됩니다:
* 총 {limit}개의 고유한 웹소설 정보를 수집함
* 모든 웹소설의 상세 정보를 성공적으로 가져옴
* 목록 페이지로 정상적으로 돌아옴
* 스크롤해도 새로운 콘텐츠가 더 이상 로딩되지 않거나
* 중복된 제목만 반복적으로 나타나면 작업을 종료
보면 알겠지만 진짜 세부적으로 작업을 명령해야 한다.
진짜로 사용할 경우에는 공식 사이트처럼 다른 llm을 붙여서 세부 프롬프트를 짜는 부분을 만들어야 할 것 같다 싶었다.
🔚 결과
결과적으로 서버를 띄우진 못했다.
docker을 사용해서 환경구성까진 했는데 PostgreSQL에서 데이터베이스 오류가 계속 나기도 했고 ollama 모델 다운 기다리다가 속이 터져서 그냥 원복시키고 짜던 크롤러를 그냥 다시 짜기로 해서...
아쉬워서 공식 사이트에서 테스트해보면서 이전 방식이랑 합쳐서 웹 페이지 구조 변경으로 오류 날때 자동으로 크롤러를 수정하는 방향 쪽으로 가면 보완이 될 것 같다는 생각을 했다.