RAG에 웹 크롤링 데이터를 연결하는 방법에 대한 구체적인 안내

RAG(Retrieval-Augmented Generation)을 위한 웹 크롤링 데이터 처리 방법과 실전 코드를 자세히 살펴보세요. 데이터 품질이 RAG 성능에 미치는 영향, 해시스크래퍼 MCP, LangChain 등도 다룹니다.

3,233
RAG에 웹 크롤링 데이터를 연결하는 방법에 대한 구체적인 안내
目次

ウェブクローリングデータをRAGに接続する実践ガイド

"当社のデータで回答するAIチャットボットを作りたいです。" — 最近、このようなリクエストを本当に多く受けます。ChatGPTは賢いですが、当社独自の最新データを基に回答させるにはRAGが必要です。そして、RAGのパフォーマンスは最終的にデータの品質にかかっています。

この記事では、ウェブクローリングで収集したデータをRAGパイプラインに接続する全体のプロセスを実践コードと共にまとめます。

目次

RAGとは何か?

RAG(Retrieval-Augmented Generation)は簡単に言えば"検索 + 生成"です。

LLMが回答を生成する前に、外部データベースから関連文書を最初に検索してコンテキストとして提供する方法です。GPTが学習していない最新情報、内部文書、ドメイン固有データを活用できるため、幻覚(hallucination)を大幅に減らすことができます。

肝心なのはここです: LLMは賢いですが、知らないことは知りません。 RAGはLLMが"知らないこと"を検索で補完する構造です。

全体パイプラインフロー

RAGパイプラインの全体的なフローは5段階に分かれます:

웹 크롤링 → 텍스트 청킹 → 임베딩 변환 → 벡터DB 저장 → LLM 쿼리

各ステップを1つずつコードと共に見ていきましょう。

ステップ1: クローリング — 元データ収集

RAGの出発点はデータです。ウェブから収集した製品情報、ニュース、技術文書、コミュニティの投稿などがすべてRAGのソースになり得ます。

ここで重要なのはクリーンなテキスト抽出です。HTMLタグ、広告、ナビゲーションメニューが混ざったデータをそのまま入れると、RAGのパフォーマンスが大幅に低下します。

import requests
from bs4 import BeautifulSoup

def crawl_page(url):
    """단순 크롤링 예시 — 실전에서는 봇 차단, JS 렌더링 등 고려 필요"""
    response = requests.get(url, headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
    })
    soup = BeautifulSoup(response.text, "html.parser")

    # 본문 텍스트만 추출 (네비게이션, 광고 등 제거)
    for tag in soup(["nav", "header", "footer", "script", "style", "aside"]):
        tag.decompose()

    text = soup.get_text(separator="\n", strip=True)
    return text

しかし、現実はそんなに単純ではありません。JavaScriptでレンダリングされるSPA、ログインが必要なページ、ボットブロッキングされたサイトではrequestsだけでは限界があります。

ステップ2: チャンキング — テキストを適切なサイズに分割

収集した文書をまるごとエンベッドすると検索の正確性が低下します。適切なサイズのチャンク(chunk)に分割することが重要です。

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 청크당 최대 500자
    chunk_overlap=50,      # 청크 간 50자 겹침 (문맥 유지)
    separators=["\n\n", "\n", ". ", " "]  # 분할 우선순위
)

documents = splitter.split_text(crawled_text)
print(f"{len(documents)}개 청크 생성")

チャンキングのヒント:
- 小さすぎると文脈が途切れ、大きすぎると検索の正確性が低下します
- 韓国語テキストでは500〜800文字のチャンクが一般的に良いパフォーマンスを示します
- chunk_overlapを設定して文が切れるのを防いでください

ステップ3: エンベッディング + ベクターDB保存

チャンクをベクター(数値配列)に変換し、ベクターDBに保存します。ここではOpenAIのエンベッディング + ChromaDBの組み合わせを使用しています。

from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

# 임베딩 모델 초기화
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# 벡터DB에 저장
vectorstore = Chroma.from_texts(
    texts=documents,
    embedding=embeddings,
    collection_name="crawled_data",
    persist_directory="./chroma_db"
)

print(f"{len(documents)}개 청크를 벡터DB에 저장 완료")

他のベクターDB(Pinecone、Weaviate、Qdrantなど)もLangChainで同じインターフェースを使用できます。

ステップ4: RAGクエリ — LLMに質問する

これでユーザーの質問が入力されると、ベクターDBから関連文書を検索し、LLMにコンテキストと共に渡します。

from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

# LLM 설정
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# RAG 체인 구성
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)

# 질문하기
answer = qa_chain.invoke("이 제품의 최신 가격은 얼마인가요?")
print(answer["result"])

注意: RetrievalQAはLangChainのレガシーチェーンインターフェースです。最新バージョンではLangGraphベースのRAG構成を推奨しますが、概念理解のために最も直感的なこの方法で説明しています。

これがRAGの全体的なフローです。クローリング → チャンキング → エンベッディング → ベクターDB → LLMクエリ。構造自体は単純です。

データの品質がRAGのパフォーマンスを左右する

RAGで最も過小評価される部分は入力データの品質です。

どれだけ優れたエンベッディングモデルやLLMを使っても、入力データが汚れていると結果も汚れます。実際、RAGのパフォーマンスの問題のほとんどはモデルではなくデータの問題から発生します。

一般的なデータ品質の問題:
- ナビゲーション、サイドバーテキストが本文と混在 → 検索時にノイズが発生
- 不完全なクローリング(JSレンダリング失敗) → 主要情報の欠落
- 重複データ → 検索結果の偏り
- エンコードの壊れ、特殊文字のエラー → エンベッディングの品質低下

Garbage In, Garbage Out. クローリング段階でクリーンなデータを確保することがRAGパイプライン全体の成功を決定します。

ハッシュスクレイパーMCP + LangChain連携

直接クローラを作成するとボットブロッキング、JSレンダリング、プロキシ管理などの副次的な問題に時間を奪われます。ハッシュスクレイパーのMCP(Model Context Protocol)サーバを使用すると、クローリングインフラの心配なしにRAGパイプラインに集中できます。

# 해시스크래퍼 MCP를 LangChain Document Loader로 활용하는 예시
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

# 해시스크래퍼 API로 데이터 수집 — 봇 차단 사이트도 OK
import requests

response = requests.post(
    "https://api.hashscraper.com/api/scrape",
    json={
        "url": "https://target-site.com/products",
        "format": "markdown"            # markdown 또는 text
    },
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json; version=2"
    }
).json()

# LangChain Document로 변환
docs = [
    Document(
        page_content=response["data"]["content"],
        metadata={
            "url": response["data"]["url"],
            "title": response["data"]["title"]
        }
    )
]

# 이후는 동일: 청킹 → 임베딩 → 벡터DB → RAG
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
vectorstore = Chroma.from_documents(chunks, embeddings)

ハッシュスクレイパーをRAGデータソースとして使用する理由:
- ボットブロッキングサイトでも安定してクリーンなテキスト抽出
- JavaScriptレンダリング、ログイン処理の自動化
- プロキシ管理、IPローテーション内蔵 — インフラに気を配る必要なし
- 標準化されたデータ出力 → チャンキング/エンベッディング品質向上

まとめ

RAGパイプラインは構造的には単純です。しかし、実践でパフォーマンスを左右するのは最終的に最初のステップであるデータ収集の品質です。

クローラを直接作成し、メンテナンスすることに時間を費やす代わりに、検証済みのクローリングインフラの上でRAGロジックに集中することがより効率的な選択肢となるかもしれません。

クローリングデータベースRAGパイプラインの構築が必要な場合は、ハッシュスクレイパーで相談してみてください。データ収集からAI連携まで、実践経験に基づいてお手伝いします。

コメント

コメントする

メールアドレスは公開されず、返信通知にのみ使用されます。

続けて読む

Get notified of new posts

We'll email you when 해시스크래퍼 기술 블로그 publishes new content.

Your email will only be used for new post notifications.