GPT-4o를 활용한 웹 스크래핑: 강력하지만, 비용이 많이 듦
(blancas.io)- OpenAI API의 structured outputs로 HTML 테이블을 구조화 데이터로 뽑는 AI 보조 웹 스크래퍼를 실험했고, GPT-4o는 일부 복잡한 테이블에서 기대보다 좋은 추출 품질을 보임
- Weather.com의 10일 예보처럼 표시 구조가 복잡한 테이블에서는 낮/밤 예보를 구분하고, 화면에 보이지 않던 값도 HTML 소스에서 찾아냄
- Wikipedia의 Human Development Index처럼 반복 값이 병합된 행으로 들어간 표에서는 컬럼별 값 개수가 어긋나 테이블 재구성이 어려웠고, 프롬프트 수정만으로 해결되지 않음
- API 호출 비용을 줄이기 위해 XPath 생성도 시도했지만 잘못되거나 빈 결과를 내는 XPath가 나왔고, 먼저 데이터를 추출한 뒤 그 값을 기준으로 XPath를 요청하는 방식이 더 안정적이었음
- 이틀 실험에 $24를 썼으며, HTML 속성을 줄이자 문자 수가 절반으로 줄고 성능 저하는 보이지 않았지만 비용 때문에 추가 검증은 제한됨
structured outputs로 HTML 테이블 추출
- OpenAI API의 structured outputs 기능으로 AI 보조 웹 스크래퍼를 구현함
- 첫 실험은 HTML 문자열을 GPT-4o에 넘기고 테이블 데이터를 구조화된 형태로 추출하도록 요청하는 방식이었음
- 사용한 Pydantic 모델은 테이블 이름과 컬럼 목록을 담는
ParsedTable, 컬럼 이름과 값 목록을 담는ParsedColumn구조임 - 시스템 프롬프트에서는 HTML 테이블에서 구조화 데이터를 추출하는 전문 웹 스크래퍼 역할을 부여함
- GPT-4o mini도 시도했지만 결과가 훨씬 나빠 이후 실험은 GPT-4o로 진행함
복잡한 테이블에서 보인 강점
- 단순 테이블 이후 Weather.com의 10일 날씨 예보 테이블을 입력함
- 이 테이블은 맨 위에 큰 행 하나가 있고, 나머지 9일은 더 작은 행으로 구성됨
- GPT-4o는 나머지 9일의 낮/밤 예보 구조를 인식해
Day/Night컬럼을 추가함 - 처음에는 추출된
Condition컬럼이 환각처럼 보였지만, HTML 소스 확인 결과 화면에 보이지 않는 태그로 실제 존재했음
병합된 행에서 발생한 실패
- Wikipedia의 Human Development Index 테이블은 겉보기에는 단순하지만, 반복 값이 있는 행이 병합되어 GPT-4o가 제대로 처리하지 못함
- 모델은 지시대로 개별 컬럼을 추출했지만, 컬럼마다 값 개수가 달라 결과를 테이블로 표현하기 어려웠음
- “병합된 행은 여러 JSON 값으로 추출해 모든 컬럼의 행 수가 같게 하라”는 식으로 시스템 프롬프트를 바꿨지만 작동하지 않음
- 컬럼 대신 행 단위 추출을 지시하는 프롬프트는 아직 시도하지 않음
XPath 생성 방식의 한계
- 매번 OpenAI API를 호출하면 비용이 커질 수 있어, 추출 데이터 대신 XPath를 반환하도록 시도함
- 목표는 동일 페이지에서 업데이트된 데이터를 다시 가져올 때 모델 호출 없이 XPath로 스크래핑하는 것임
- 프롬프트는 HTML 내용과 컬럼명을 받아 Selenium의
driver.find_elements(By.XPATH, xpath)에서 평가 가능한 XPath 문자열을 반환하도록 구성함 - 이 방식은 안정적이지 않았음
- GPT-4o가 잘못된 XPath를 반환하는 경우가 있었음
- Selenium을 언급하는 문장으로 일부 완화됐지만, 잘못된 데이터나 빈 결과를 반환하는 XPath도 나왔음
데이터 추출과 XPath 생성을 결합
- 다음 시도는 먼저 GPT-4o가 데이터를 추출하고, 그 결과를 참조값으로 삼아 XPath를 요청하는 방식임
- 이 조합은 처음부터 XPath만 요청하는 방식보다 훨씬 잘 작동함
- 생성된 XPath가 결과를 반환하지 않는 경우가 있어, 결과가 없으면 다시 시도하는 단순 재시도 로직을 추가함
- 테스트한 테이블에서는 이 재시도 방식이 효과가 있었음
- 새 문제도 남아 있음
- 첫 단계의 데이터 추출이 이미지를 텍스트로 바꾸는 경우가 있었음
- 예를 들어 위쪽 화살표 이미지가
arrow-upwards같은 텍스트로 추출될 수 있음 - 두 번째 단계는 실제 HTML에 없는 텍스트를 찾게 되어 실패함
- 이 문제는 별도로 수정하지 않음
비용과 HTML 정리
- GPT-4o로 웹 스크래핑을 하면 작은 HTML 테이블도 많은 문자를 포함해 비용이 커질 수 있음
- 이틀 동안 실험하며 이미 $24를 지출함
- 비용을 줄이기 위해 모델에 전달하기 전 HTML 문자열에서 불필요한 데이터를 제거하는 정리 로직을 추가함
- 생성된 XPath가 주로 사용하던
class,id,data-testid를 제외한 모든 속성을 제거하자 테이블 문자 수가 절반으로 줄어듦 - 이 정리 후 성능 저하는 보이지 않았고, 추출 품질이 실제로 개선될 수도 있다고 봄
- 현재 XPath 생성 단계는 테이블의 컬럼마다 모델 호출을 한 번씩 수행함
- 한 번에 여러 XPath를 생성하는 개선도 가능하지만 아직 시도하거나 성능을 평가하지 않음
데모와 추가 실험 아이디어
- GPT-4o의 추출 품질은 인상적이었지만, OpenAI에 지불해야 할 비용도 크게 느껴짐
- Streamlit으로 만든 간단한 데모는 https://orange-resonance-9766.ploomberapp.io에서 확인 가능함
- 소스 코드는 GitHub에 공개되어 있으며, 완성도 높은 도구를 기대하지 말라는 단서가 붙어 있음
- 더 많은 테이블을 테스트하고 싶었지만 OpenAI 비용 증가 때문에 소수의 테이블만 실험함
- 시간이 더 있다면 다음을 시도할 계획임
- 현재 데모는 사용자가 URL과 초기 XPath를 입력하는 일회성 프로세스이며, 사용자가 추출할 테이블을 클릭하고 샘플 행을 제공하는 방식이 더 나은 사용자 경험이 될 수 있음
- 복잡한 테이블에서는 단일 XPath만으로 전체 컬럼을 추출하기 어려울 수 있어, LLM이 Python 같은 프로그램을 반환하도록 요청하는 방식을 실험하고 싶음
- GPT-4o 사용 비용이 비싸고 불필요한 데이터를 많이 전달하고 있다고 느껴, HTML 정리 방식을 더 실험할 필요가 있음
댓글과 토론
Hacker News 의견들
-
HTML을 바로 LLM에 넣기보다 먼저 Markdown 같은 단순한 형식으로 바꿨을 때 가장 잘 됐음
써본 도구로는 Extractus https://github.com/extractus와 dom-to-semantic-markdown https://github.com/romansky/dom-to-semantic-markdown가 있음
클라우드에서 도는 Magic Loops https://magicloops.dev/ 내부에서는 Apify https://apify.com/와 Firecrawl https://www.firecrawl.dev/을 쓰고, Chrome Extension에서는 dom-to-semantic-markdown을 씀
지금은 특정 사이트용 XPath를 사용자 보조 흐름으로 생성한 뒤, LLM 호출 전에 필요한 요소만 뽑는 방식을 실험 중이며, 문제를 단순화하니 GPT-4o mini로도 꽤 괜찮았음- 우리가 만드는 https://browserbase.com/도 봐주면 좋겠음
헤드리스 브라우저에서 Chrome Extension을 실행할 수 있어서, 데이터를 밖으로 꺼내기 전에 브라우저 안에서 의미론적 Markdown 변환을 할 수 있음
브라우저의 iFrame 가능한 실시간 화면도 있어서, 사용자가 생성 중인 XPath에 대한 피드백을 바로 받을 수 있음: https://docs.browserbase.com/features/session-live-view#give... - Markdown으로 바꾸는 것과, HTML에서 태그 속성 제거, 링크 풀기, 표시되지 않는 요소 제거 정도만 하는 방식을 비교해봤는지 궁금함
내 경험상 성능은 Markdown과 꽤 비슷했고, 변환은 더 쉽고 예외도 적었음 - Semantic Markdown https://hackmd.io/@sparna/semantic-markdown-draft은 처음 들어봤는데, Markdown 문서 안에 RDF 데이터를 넣는 방식처럼 보임
찾은 페이지가 “Alpha Draft”라서 Semantic Markdown 콘텐츠 말뭉치가 많지는 않을 듯하고, 학습 데이터 부족 때문에 LLM 이해에 방해될 수도 있음
그래도 충분히 읽기 쉬운 형식이라 LLM이 구조화 메타데이터를 괄호 설명처럼 취급하면 꽤 잘 처리할 수 있을 것 같음 - 비슷한 일을 다른 맥락에서 해봤는데, 실행 그래프를 나타내는 복잡한 JSON을 먼저 더 단순한 Graphviz dot 형식으로 바꾼 뒤 LLM에 넣었고 꽤 괜찮았음
- 우리가 만드는 https://browserbase.com/도 봐주면 좋겠음
-
OpenAI가 최근 Batch API를 발표해서 프롬프트를 모두 준비한 뒤 일괄 실행할 수 있고, 비용도 50%로 줄어듦: https://platform.openai.com/docs/guides/batch
예전에 GPT-4o mini와 많이 썼고, 3,000개 항목을 5분 안에 처리할 수 있었음
실시간이 필요 없는 애플리케이션에는 꽤 좋을 수 있음- 오픈소스 추론 서버들도 곧 그 엔드포인트를 지원했으면 좋겠음
vLLM은 같은 형식의 “offline batch mode” 지원을 일부 추가했지만, 아직 OpenAI 엔드포인트 구현까지는 못 간 상태임 - OpenAI의 제안은 좋지만, 비슷한 정밀도와 재현율을 내는 전통적인 텍스트 추출 방식과 비교하면 아직도 한두 자릿수 정도 비싸다고 봄
- OpenAI가 정말 잘한 결정이고, Azure 같은 다른 클라우드 도구들도 같은 기능을 제공했으면 좋겠음
너무 자연스러운 기능임
- 오픈소스 추론 서버들도 곧 그 엔드포인트를 지원했으면 좋겠음
-
구조화된 콘텐츠, 예를 들어 항목 목록이나 단순 표에는 굳이 LLM이 필요하지 않음
최근 어떤 웹사이트든 자동으로 작동하는 웹 스크래퍼 https://easyscraper.com를 만들었고 초기 버전은 AI로 만들었지만, 결국 요소 속성과 위치 기반 휴리스틱이 더 빠르고 싸고 정확했음
대부분의 웹사이트에서는 비-AI 접근이 매우 잘 작동하므로, 데이터가 비정형이거나 페이지 데이터를 바탕으로 출력 형식을 유도해야 하는 경우처럼 AI가 정말 필요한지 먼저 확인하는 게 좋음- LLM은 일반 스크래핑을 깨뜨릴 수 있는 웹사이트 업데이트에 더 강함
글쓴이처럼 LLM에 XPath를 생성하게 한 뒤, 평소에는 그 XPath로 일반 스크래핑을 하고, 깨지면 LLM으로 XPath를 갱신하게 만들 수 있음
그래도 데이터가 다시 흐르지 않거나 파이프라인 뒤쪽에서 예상치 못한 형식 때문에 깨지면 그때 사람에게 알리면 됨 - 직접 선택해서 무엇을 스크래핑할지 지정할 수 있는 도구는 처음 봄
왜 이런 도구가 없었는지 항상 궁금했음
- LLM은 일반 스크래핑을 깨뜨릴 수 있는 웹사이트 업데이트에 더 강함
-
“HTML reducer” 같은 게 이미 있는지 궁금함
페이지 소스를 그대로 보면 무작위 JavaScript, 광고, 불필요한 속성, 렌더링용 과도한 중첩 때문에 토큰의 90%가 쓰레기임
DOM 파서로 순회하면서 텍스트가 있는 노드, HTML 구조, 필요한 태그 속성(class/id 정도)만 남기면 비용을 크게 줄일 수 있을 것 같고, XPath 방식도 더 잘 작동할 수 있음
Readability도 쓰지만 DOM 구조를 잃고, JavaScript가 많은 웹사이트나 “continue reading”처럼 텍스트를 확장하는 페이지에서는 품질이 떨어짐
이런 용도의 표준에 가까운 도구가 뭔지 궁금함- Ribbon에서 내부용으로 이런 걸 만들었음
관심이 있으면 오픈소스로 공개할 수 있고, 리듀서를 거친 뒤 LLM 출력이 훨씬 좋아지는 게 놀라웠음 - Jina.ai가 이 용도로 꽤 괜찮은 무료 API를 제공함
아무 URL 앞에 https://r.jina.ai/를 붙이면, LLM에 넣기 좋은 그 페이지의 주요 콘텐츠 Markdown 버전을 돌려줌
예시는 https://r.jina.ai/https://simonwillison.net/2024/Sep/2/anato...이고 원문 페이지는 https://simonwillison.net/2024/Sep/2/anatomy-of-a-textual-us...임
코드는 오픈소스라 직접 실행할 수 있음: https://github.com/jina-ai/reader
TypeScript로 작성됐고 Puppeteer와 https://github.com/mozilla/readability를 사용함
나는 Markdown 변환 없이 Readability만 써서 페이지 제목과 본문을 추출했고, Playwright와 shot-scraper로 실행하는 레시피도 있음: https://shot-scraper.datasette.io/en/stable/javascript.html#... - Skyvern에서 쓰려고 비슷한 걸 만들었음: https://github.com/Skyvern-AI/skyvern/blob/0d39e62df6c516e0a...
vimium에서 가져와 수정한 방식이고, HTML을 중요한 부분만 남기도록 증류하면서 여러 예외 상황도 처리함 - Readability를 거치면 됨: https://github.com/mozilla/readability
- 후속 글을 쓰는 중인데, 알고 보니 모든 HTML 태그 제거만 해도 잘 작동하고 비용을 크게 줄일 수 있었음
- Ribbon에서 내부용으로 이런 걸 만들었음
-
글쓴이가 작업과 글쓰기의 99%를 해놓고, 나머지 1%인 ollama나 llama.cpp 기반 엔진을 내려받아 괜찮은 로컬 LLM을 테스트하지 않은 게 놀라움
이 사용 사례에서는 7B나 30B 모델도 잘할 수 있고, 실행 비용도 충분히 낮아서 GPT-4o가 필요 없을 수 있음- 좋은 제안임
이제 LLM을 만져보기 시작했고, 로컬 호스팅 모델도 살펴볼 예정임
- 좋은 제안임
-
Kadoa https://kadoa.com에서 AI 자동 웹 스크래핑을 해왔고, 초기 실험은 글과 비슷했음
비싸고 느린 GPT-3만 있던 시절부터 시작해서, 대규모로 비용 효율적인 해법이 필요했음
최종적으로는 추출 단계에서 LLM을 매번 쓰는 대신 코드 생성으로 CSS 선택자나 XPath 추출 코드를 만들고, 이후 웹사이트 변경에 맞춰 스크래퍼 코드를 조정하게 했음
정제와 변환에는 작은 파인튜닝 LLM을 쓰고, 검증에는 역검색 같은 전통적 방법과 함께 LLM-as-a-judge로 데이터 품질을 평가함
몇 개의 단순한 데이터 소스에 적용하는 것과 수천 개 웹사이트에서 안정적이고 확장 가능하며 비용 효율적으로 돌리는 것은 완전히 다른 문제였고, 전통적인 ETL 엔지니어링과 작고 잘 평가된 LLM 단계를 섞는 방식이 맞았음 -
스크래핑하고 싶은 HTML 예시를 주고 BeautifulSoup 코드 조각을 요청하면 꽤 잘 됐음
보통 긁어오려는 구조는 그대로 유지되는데, 그걸 파싱하는 난해한 문자열을 직접 짜는 일이 지루함
실제 파싱을 LLM에 맡기는 건 과한 데다 환각으로 결과가 오염될 위험도 있음 -
HTML을 먼저 전처리하면 더 싸고 좋은 결과를 얻을 수 있음
아직 언급되지 않은 것 같은데, 개인적으로 trafilatura https://trafilatura.readthedocs.io/en/latest/로 좋은 결과를 봤음- trafilatura에 크게 동의함
LLM에는 텍스트만 보내면 되므로 비용을 엄청 줄일 수 있음
최근 프로젝트 https://github.com/philippe2803/contentmap에서도 사용했는데, 도메인 XML 사이트맵을 출발점으로 삼아 임의의 웹사이트에 대한 벡터 저장소를 만드는 간단한 Python 라이브러리임
도메인마다 HTML 구조가 다르기 때문에 실제 콘텐츠만 뽑고 HTML 태그 등을 제거해야 했는데, Trafilatura가 몇 줄 코드로 거의 모든 URL에서 그 일을 해줌
- trafilatura에 크게 동의함
-
주황색 태그가 이렇게 많이 붙은 글은 처음 보는 수준임
NewsCatcher에서는 GPT-4o로 많은 테스트를 하고 있고, 10만 개 이상의 뉴스 웹사이트를 크롤링한 뒤 뉴스 콘텐츠를 파싱해야 함
어떤 기사에서든 데이터를 뽑는 규칙 기반 모델은 꽤 잘 작동했고, GPT로 더 개선할 방법은 찾지 못했음
더 흥미로운 건 크롤링 쪽으로, 뉴스 기사가 게시될 수 있는 모든 위치를 알아야 하고 때로는 50개 이상의 하위 섹션이 있음
웹사이트별 구조는 자주 바뀌지 않으니, 많은 프로젝트는 추출 코드를 생성하는 방식으로 충분할 수 있다고 봄
그래서 LLM으로 HTML 파싱 코드를 생성하는 쪽을 보고 있으며, 관심 있으면 artem [at] newscatcherapi.com으로 연락하면 됨- 취미 프로젝트에서 이걸 써보고 싶음
셀프 가입이 있었으면 좋겠음
- 취미 프로젝트에서 이걸 써보고 싶음
-
웹 스크래핑은 공동창업자와 내가 지금의 openpipe.ai를 만들게 된 실제 계기였음
GPT-4는 이 일을 정말 잘하지만 너무 비쌈
다만 특정 종류의 사이트를 스크래핑하는 능력은 훨씬 싼 파인튜닝 모델로 증류하기가 꽤 쉽고, 그 종류의 사이트에서는 안정적으로 잘 긁어옴- Kyle, 이걸 더 일찍 말했어야 했음
우리도 꽤 오래 이 문제를 해왔고, 어디까지 왔는지 보여주려고 연락하겠음
- Kyle, 이걸 더 일찍 말했어야 했음