RAG 인덱스를 만들다 보면 막히는 지점은 모델이 아니라 데이터다. 깨끗한 마크다운으로 본문을 뽑아야 청크·임베딩이 제대로 먹는데, 일반 크롤러는 JS 렌더링이 안 되거나 광고·푸터를 그대로 긁어온다. Crawl4AI는 이 문제를 LLM-친화 출력에 특화해서 푼다.
GitHub Python 트렌딩·Trendshift 1위, Apache 2.0 라이선스라 상업 RAG에 그대로 넣을 수 있다. 이 글은 설치, 비동기 멀티 URL, RAG 연결, 한국 사이트 주의점, Firecrawl·ScrapingBee 비교를 한 번에 정리한다.
📌 핵심 3줄 요약
- Crawl4AI는 Playwright 기반 비동기 크롤러로, JS 렌더링 페이지를 LLM-친화 마크다운으로 자동 추출한다.
- 설치는
pip install crawl4ai+crawl4ai-setup두 줄, 첫 크롤링은 코드 10줄로 끝난다. - Apache 2.0 라이선스라 폐쇄망·상업 RAG에 그대로 임베드 가능하며 Firecrawl 대비 API 호출 비용이 없다.
1. 왜 지금 Crawl4AI인가
BeautifulSoup·Scrapy는 정적 HTML 가정을 깔고 만들어졌다. 2026년 현실은 Next.js·SvelteKit·Vue로 만든 동적 페이지가 기본이고, RAG에 넣을 본문은 사이드바·광고·푸터를 제거한 순수 마크다운이 필요하다.
Crawl4AI는 Playwright 위에 LLM 후처리를 얹어 다음을 자동화한다.
- JS 실행 후 DOM 안정화 대기
- 본문 후보 영역만 추출(Readability·CSS 셀렉터·LLM 추출 전략 중 선택)
- 마크다운·JSON·구조화 데이터로 출력
- 비동기 멀티 URL 동시 크롤링
Apache 2.0이라 사내 폐쇄망 RAG·상업 SaaS에 그대로 임베드 가능하다. Firecrawl 같은 SaaS와 달리 API 호출 비용이 없다는 점이 가장 큰 차별점이다.
2. 설치와 환경 준비
설치는 두 단계로 끝난다. pip 패키지를 받고 crawl4ai-setup으로 Playwright 브라우저 바이너리를 내려받는다.
pip install -U crawl4ai
crawl4ai-setup
crawl4ai-setup이 Chromium·Firefox·WebKit을 자동으로 받고 PATH·의존성을 점검한다. 처음 한 번만 1~2분 걸린다. 수동 설치는 python -m playwright install chromium으로 대체할 수 있다.
권장 환경은 Python 3.10 이상, 메모리 4GB 이상, Linux·macOS다. Windows는 WSL2 안에서 돌리는 편이 Playwright 호환성 면에서 안전하다. RAG 인덱스 빌드 잡을 GitHub Actions로 돌렸을 때 ubuntu-latest 러너에서 가장 안정적이었다.
3. 첫 번째 크롤링 — AsyncWebCrawler 한 페이지
가장 단순한 사용법은 AsyncWebCrawler로 페이지 하나를 받아 마크다운으로 뽑는 것이다.
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(url="https://docs.crawl4ai.com/")
print(result.markdown[:2000])
print("links:", len(result.links["internal"]))
asyncio.run(main())
result 객체는 markdown, cleaned_html, links, media, metadata 필드를 가진다. RAG용으로는 markdown만 떼서 청크·임베딩 파이프라인에 넘기면 된다. 한 페이지가 보통 1.5~3초에 끝난다.
4. 고급 옵션 — BrowserConfig·CrawlerRunConfig
실전에서는 동적 페이지 대기·헤더·프록시 설정이 필요하다. AsyncWebCrawler는 두 가지 설정 객체를 받는다.
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
browser_cfg = BrowserConfig(headless=True, user_agent="Mozilla/5.0 RAGBot/1.0")
run_cfg = CrawlerRunConfig(
cache_mode=CacheMode.BYPASS,
wait_for="css:article", # article 렌더링까지 대기
page_timeout=20000,
word_count_threshold=50, # 50단어 미만 블록 제거
excluded_tags=["nav", "footer", "aside"],
)
async with AsyncWebCrawler(config=browser_cfg) as crawler:
result = await crawler.arun(url=target_url, config=run_cfg)
wait_for는 CSS 셀렉터·JS 표현식·시간(ms)을 받는다. SPA는 wait_for="js:() => document.querySelectorAll('article').length > 0"처럼 조건을 직접 쓰는 편이 가장 견고하다. cache_mode는 재크롤 비용을 아끼지만, 인덱스 갱신 잡에서는 BYPASS로 끄는 게 안전하다.
5. 비동기 멀티 URL → RAG 파이프라인
RAG에서는 한 번에 수십~수백 URL을 동시에 처리한다. arun_many로 동시성을 잡고, 결과 마크다운을 곧장 LangChain 스플리터·임베딩에 흘려보낸다.
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
URLS = [
"https://docs.crawl4ai.com/core/quickstart/",
"https://docs.crawl4ai.com/core/installation/",
"https://docs.crawl4ai.com/advanced/multi-url-crawling/",
]
async def crawl_batch(urls):
cfg = CrawlerRunConfig(word_count_threshold=30)
async with AsyncWebCrawler() as crawler:
results = await crawler.arun_many(urls=urls, config=cfg)
return [(r.url, r.markdown) for r in results if r.success]
docs = asyncio.run(crawl_batch(URLS))
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=80)
texts, metas = [], []
for url, md in docs:
for ch in splitter.split_text(md):
texts.append(ch); metas.append({"source": url})
FAISS.from_texts(texts, OpenAIEmbeddings(), metadatas=metas).save_local("./rag_index")
이 패턴이 곧 LLM-friendly 크롤링의 핵심이다. 정제된 마크다운만 들어가니 청크 경계가 깔끔하고 임베딩 노이즈가 줄어든다. 같은 50개 URL을 BeautifulSoup으로 긁어 만든 인덱스와 비교했을 때 답변의 출처 적중률이 체감상 2배 가까이 올라갔다.
6. 한국 사이트 크롤링 — 인코딩·robots 주의점
네이버 블로그·티스토리·언론사 포털을 긁을 때 함정이 몇 가지 있다.
- iframe 본문: 네이버 블로그는 본문이
iframe#mainFrame안이다.result.markdown이 비면 iframe 내부 URL(PostView.naver)을 직접 타깃으로 다시arun하는 편이 빠르다. - 인코딩: 구식 페이지는 응답 헤더에 인코딩이 빠져 있다. 깨지면
BrowserConfig에extra_args=["--lang=ko-KR"]을 추가한다. - robots.txt: 대형 포털은
Disallow영역이 광범위하다. Crawl4AI는 자동 강제하지 않으니urllib.robotparser로 사전 확인 로직을 직접 붙여야 안전하다. - 요청 간격: 동일 도메인 대량 요청은 차단 위험.
arun_many의semaphore_count를 2~3으로 낮춰 잡는다.
7. Crawl4AI vs Firecrawl vs ScrapingBee 비교
선택 기준이 가장 자주 묻는 부분이다. 세 도구의 포지션이 달라 용도에 따라 답이 갈린다.
| 항목 | Crawl4AI | Firecrawl | ScrapingBee |
|---|---|---|---|
| 형태 | 오픈소스 라이브러리 | SaaS + 오픈소스 | SaaS API |
| 라이선스 | Apache 2.0 | AGPL / 유료 | 상용 |
| 가격 | 무료(자체 호스팅) | 월 $19~$333 | 월 $49~$599 |
| JS 렌더링 | O (Playwright) | O | O |
| LLM-친화 출력 | 마크다운·JSON·LLM 추출 | 마크다운 기본 | HTML 위주, 별도 가공 |
| 자체 호스팅 | 가능 | 제한적 | 불가 |
| 적합 시나리오 | 사내 RAG·연구·대량 인덱스 | 빠른 PoC·관리형 운영 | 안티봇 우회·프록시 |
사내 데이터·폐쇄망·비용 민감 시나리오는 Crawl4AI가 압도적이다. 안티봇이 강한 e커머스에는 ScrapingBee 프록시 풀이 유리하고, 운영 인력 없이 빨리 띄우려면 Firecrawl 매니지드가 낫다.
8. 자주 묻는 질문
Q1. Crawl4AI는 무료인가요?
네. Apache 2.0 라이선스 오픈소스라 상업적 사용·재배포·수정 모두 가능하다. 비용은 자체 인프라뿐이다.
Q2. Crawl4AI와 Firecrawl의 차이는?
Crawl4AI는 라이브러리, Firecrawl은 SaaS 중심이다. 빠른 시작은 Firecrawl, 비용·커스터마이즈는 Crawl4AI가 맞다.
Q3. 자바스크립트 페이지도 크롤링되나요?
가능하다. Playwright 기반이라 React·Vue·Svelte SPA도 렌더링 후 본문을 추출하며 wait_for로 비동기 fetch까지 기다릴 수 있다.
Q4. 결과를 RAG에 어떻게 넣나요?
result.markdown을 LangChain·LlamaIndex 스플리터로 청크 분할 후 임베딩해 FAISS·Qdrant·pgvector에 저장한다. 5번 섹션 코드가 표준 패턴이다.
Q5. 어떤 사이트를 크롤링할 수 있나요?
공개 HTML 페이지 대부분이 가능하다. 로그인·캡차·강한 안티봇이 걸린 사이트는 별도 인증·프록시 설정이 필요하다. robots.txt와 이용약관 사전 확인이 원칙이다.
9. 다음 단계
Crawl4AI는 RAG 시대 데이터 수집의 표준 후보다. 오픈소스·Playwright·LLM-친화 출력을 한 번에 묶었고 코드 10줄로 첫 인덱스를 만들 수 있다.
다음 액션 두 가지를 권한다. 첫째, arun_many로 사내 문서 100~500개를 인덱싱해 검색 품질을 BeautifulSoup 인덱스와 비교한다. 둘째, LLMExtractionStrategy로 JSON 스키마 추출을 붙여 함수 호출(tool use)에 그대로 넘긴다. 크롤러보다 그 뒤의 청크·메타데이터 설계가 RAG 성능을 더 크게 좌우한다.
참고 자료
- Crawl4AI GitHub — https://github.com/unclecode/crawl4ai
- Crawl4AI 공식 문서 — https://docs.crawl4ai.com/
- Playwright Python — https://playwright.dev/python/
- Trendshift — https://trendshift.io/
- LangChain Text Splitters — https://python.langchain.com/docs/concepts/text_splitters/