유료 검색/스크레이핑 API 없이 로컬 에이전트에 웹 접속 권한 부여하기: SearXNG + Scrapling
요약
유료 API 없이 로컬 에이전트에 웹 접속 기능을 부여하기 위해 SearXNG와 Scrapling을 결합한 구현 방법을 소개합니다. SearXNG로 검색을 수행하고 Scrapling과 Trafilatura를 통해 웹 페이지를 마크다운 형식으로 추출하는 워크플로우를 다룹니다.
핵심 포인트
- SearXNG를 활용한 자체 호스팅 메타 검색 엔진 구축
- Scrapling을 이용한 빠른 경로 및 스텔스 브라우징 추출 방식
- Trafilatura를 통한 HTML의 마크다운 변환 및 데이터 정규화
- SSRF 방지 및 PDF 처리 등 보안과 확장성 고려
Tavily, Serper, Firecrawl 등을 사용하지 않고 로컬 우선 (local-first) 에이전트에 웹 접속 권한을 부여하고 싶었습니다.
이 에이전트 경로를 위해, 유료 API 키가 필요 없고, 제가 직접 제어할 수 있는 검색 서비스와 직접 실행할 수 있는 페이지 추출 기능이 필요했습니다.
결과적으로 제가 만든 것은 두 가지 도구인 web_search와 web_extract입니다. 거창한 것은 없습니다. 대부분 좋은 오픈 소스(open-source) 조각들을 서로 연결한 것입니다.
- 검색(Search) -> SearXNG
SearXNG는 자체 호스팅이 가능한 메타 검색 엔진(metasearch engine)입니다. 저는 이를 Docker에서 실행하고 에이전트가 해당 JSON 엔드포인트(endpoint)를 가리키도록 설정했습니다.
검색 호출은 대략 다음과 같습니다:
text GET {SEARXNG_URL}/search?q=<query>&format=json&pageno=1
그 다음 결과의 개수를 제한하고 {title, url, description} 형식으로 정규화(normalize)합니다.
description은 단순히 SearXNG의 스니펫(snippet)입니다. 페이지 콘텐츠가 아닙니다.
설정은 기본적으로 다음과 같습니다:
text SEARXNG_URL=http://localhost:8080
주의사항:
SearXNG의 settings.yml에서 search.formats에 json을 추가하세요.
공용 SearXNG 인스턴스는 일반적으로 프로그래밍 방식의 사용에는 적합하지 않습니다.
SearXNG는 검색 전용입니다. 에이전트가 페이지를 읽어야 할 때는 추출(extraction) 기능을 사용하세요.
- 추출(Extract) -> Scrapling + Trafilatura
검색 스니펫만으로는 충분하지 않습니다. 에이전트는 실제 페이지를 읽어야 합니다.
web_extract를 위해 저는 Scrapling을 사용하여 두 가지 경로를 사용합니다:
빠른 경로(Fast path): Fetcher.get(url, impersonate="chrome"). 브라우저를 사용하지 않습니다. 일반적인 페이지에 적합합니다.
스텔스 경로(Stealth path): 빠른 경로가 비어 있거나, 차단되었거나, 챌린지(challenge)가 의심되는 경우 실제 헤드리스 브라우저(headless browser)를 시도합니다:
python StealthyFetcher.fetch( url, headless=True, solve_cloudflare=True, block_webrtc=True, hide_canvas=True, )
스텔스 경로는 시도일 뿐, 보장된 우회 방법은 아닙니다. 페이지에 여전히 CAPTCHA나 Cloudflare 벽이 나타나면 결과가 차단되었거나 부분적(blocked/partial)이라고 표시합니다.
HTML을 확보하면, Trafilatura가 이를 링크와 표가 포함된 마크다운(Markdown)으로 변환합니다. 마크다운은 모델이 원본 HTML보다 훨씬 처리하기 쉽습니다. 또한 Trafilatura가 추출을 충분히 하지 못한 페이지를 위해 가시적 텍스트(visible-text) 폴백(fallback)도 유지합니다.
기타 중요한 요소들:
PDF: PDF URL은 pypdf를 통해 처리됩니다.
챌린지 탐지(Challenge detection): CAPTCHA/보안 페이지는 실제 콘텐츠로 취급되는 대신 플래그(flag)가 지정됩니다.
SSRF 가드(guard): 요청된 URL과 리다이렉트(redirect)가 프라이빗/내부(private/internal) 범위인지 확인합니다. 최종 URL도 확인합니다. 주의사항: 이는 모든 브라우저 하위 요청(subrequest)에 대한 네트워크 수준의 가드는 아닙니다.
선택적 요약(Optional summarization): 페이지 내용이 너무 큰 경우, 컨텍스트(context)로 돌아가기 전에 설정 가능한 보조 모델(auxiliary model)을 통해 요약될 수 있습니다.
이 조합을 사용하는 이유
이 경로를 위해 유료 검색/스크레이핑(scrape) API 키가 필요하지 않습니다.
쿼리는 내 계정에 연결된 벤더(vendor) API가 아니라, 나의 SearXNG 인스턴스를 통해 전달됩니다.
SearXNG는 여전히 업스트림(upstream) 엔진에 접속하므로, 이것이 "제3자 접촉이 전혀 없는" 상태를 의미하지는 않습니다.
대부분의 페이지는 빠른 경로(fast path)를 사용합니다. 브라우저는 필요한 경우에만 작동합니다.
최종 출력물은 HTML 수프(soup)가 아닌 마크다운(Markdown)입니다.
솔직한 트레이드오프(tradeoffs)
스텔스(stealth) 경로는 느립니다. 폴백(fallback) 용도로 유지하세요.
SearXNG의 품질은 활성화된 업스트림 엔진과 속도 제한(rate limits)에 따라 달라집니다.
유료 검색 API가 여전히 더 나을 수 있습니다. 하지만 제 용도로는 충분히 좋았습니다.
Cloudflare/브라우저 스크레이핑은 항상 변화하는 타겟입니다.
이것이 최적의 설정이라고 주장하는 것은 아닙니다. 그저 저에게 잘 작동했고, 셀프 호스팅(self-hostable)이 가능한 설정일 뿐입니다.
다른 분들은 무엇을 사용하고 계신지 궁금합니다. 셀프 호스팅 검색을 위해 SearXNG보다 더 나은 것을 찾으셨거나, 까다로운 페이지를 위해 전체 브라우저를 대신할 더 가벼운 대안을 찾으신 분이 계신가요?
비슷한 시도를 하고 계신 분이 있다면 더 자세한 내용을 기꺼이 공유하겠습니다.
submitted by /u/luke_pacman
[link] [comments]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/LocalLLaMA의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기