RAGのためのクリーンウェブテキスト:フェッチ、抽出、チャンクパイプライン
Web Data Collection Specialist
重要なポイント:
- RAGの品質はコーパスの品質です。 リトリーバルの回答は、インデックスされたテキストと同じくらい良いものでなければなりません。ほとんどのパイプラインの失敗は、表示されなかったページ、埋め込まれたナビゲーションクローム、または途中で分割されたチャンクに起因します。
- フェッチは不安定なステージです。 現代のページはJavaScriptでレンダリングされ、ボットチェックされています。単純なHTTP GETでは空のシェルかチャレンジページが返され、そのゴミは静かにあなたのベクトルストアに流れ込みます。
- 1つのPOSTでレンダリングされたページが返されます。 Scrapeless Web Unlockerは、URLを受け取り、完全にレンダリングされたHTMLを
{"code": 200, "data": "<html…>"}として返します。レンダリングとアンチボット処理はサーバーサイドで実行されます。 - 抽出は減算です。 テキストを読む前にスクリプト、スタイル、ナビゲーション、フッターを削除します。残ったものが埋め込みに値する文章です。
- オーバーラップでチャンクし、由来を保持します。 オーバーラップのある固定単語ウィンドウは境界をまたいでコンテキストを保持し、すべてのチャンクはそのソースURLを含むべきです。由来なしのリトリーバルは監査できません。
- 使い始めるのは無料です。 新しいScrapelessアカウントには無料トライアルのクレジットが含まれます — app.scrapeless.comにサインアップしてください。
パイプラインの概要
RAGシステムはテキストのチャンクを取得し、モデルに提供します。下流のすべてはインデックスに入ったものを受け継ぎます。このガイドは、受け入れ側をエンドツーエンドで構築します:
- フェッチ — Web Unlockerを利用してURLリストの完全にレンダリングされたHTMLを取得し、JavaScriptで構築されたページとボットチェックされたサイトが実際のコンテンツを返すようにします。
- 抽出 — ページクロームを取り除き、文章を保持します。
- チャンク — オーバーラップのある固定単語ウィンドウに分割し、どの埋め込みモデルおよびベクトルストアにも適した形式にします。
出力はcorpus.jsonl:行ごとに1つのチャンク、そのソースURLと位置を持っています — すべての埋め込みワークフローが受け入れる中立的な形式です。ステージ2と3は純粋な変換であり、ネットワークに触れるのはステージ1だけです。
なぜフェッチステージが最初に壊れるのか
ウェブテキストの受け入れにおいては、3つの失敗モードが支配しており、すべてはリトリーバル品質が低下するまで見えません:
- クライアントサイドレンダリング。 単純なGETが返すHTMLはローダーシェルで、記事はJavaScriptを通じて後で到着します。あなたの抽出器は空の
<div id="root">を読み、何もインデックスしません。 - アンチボットインタースティシャル。 チャレンジページは、"ブラウザをチェックしています"という文章を伴うHTTP 200を返します — これが美しく埋め込まれ、安心してリトリーブされます。
- ソフト404。 スタイル付きの"見つかりません"ページをレンダリングする無効なURL、再度200ステータスで。
解決策は、サーバーサイドでそれらのレイヤーをレンダリングおよびクリアするインフラストラクチャを通じてフェッチすることです。Universal Scraping API ウェブアンロッカーは正にそれを行います:URLごとに1つのPOST、レンダリングされたHTMLが返されます。
前提条件
- ScrapelessアカウントとAPIキー — app.scrapeless.comでサインアップします。
- Python 3.10以上と
requestsおよびbeautifulsoup4。 - インジェストする権利のあるURLリスト(下記のソーシングノートを参照)。
bash
export SCRAPELESS_API_KEY=your_api_token_here
ステージ1 — レンダリングされたページをフェッチ
アンロッカーエンドポイントへのURLごとの1つのPOST。レスポンスは、dataにレンダリングされたドキュメントを持つJSONとなります:
python
# fetch.py — URLリスト -> pages/*.html (完全にレンダリングされた)
import os
import pathlib
import requests
ENDPOINT = "https://api.scrapeless.com/api/v1/unlocker/request"
HEADERS = {
"Content-Type": "application/json",
"x-api-token": os.environ["SCRAPELESS_API_KEY"],
}
URLS = [
"https://www.scrapeless.com/ja/blog/best-llm-scrapers-2026",
"https://www.scrapeless.com/ja/blog/google-ai-overview-scraper-api-2026",
]
pathlib.Path("pages").mkdir(exist_ok=True)
for url in URLS:
resp = requests.post(
ENDPOINT,
headers=HEADERS,
json={
"actor": "unlocker.webunlocker",
"input": {"url": url, "type": "html", "redirect": True, "method": "GET"},
},
timeout=120,
)
resp.raise_for_status()
html = resp.json()["data"]
name = url.rstrip("/").rsplit("/", 1)[-1] + ".html"
pathlib.Path("pages", name).write_text(html, encoding="utf-8")
print(f"{url} -> pages/{name} ({len(html):,} バイト)")
健全なフェッチでは、各記事ページごとに何百キロバイトものレンダリングされたドキュメントが得られます。数キロバイトは通常、シェルまたはインタースティシャルを意味し、インデックスに到達する前に確認する価値があります。
無料プランでAPIキーを取得:app.scrapeless.com
ステージ2と3 — 文章を抽出し、由来を持つチャンクに分割する
抽出は減算です:決して散文でない要素を取り除き、残ったものからテキストを読み取ります。チャンク処理は、重複のある固定単語ウィンドウであり、各チャンクはその元のURLと位置を保持します。
python
# build_corpus.py — pages/*.html -> corpus.jsonl (出所のあるチャンク)
import json
import pathlib
from bs4 import BeautifulSoup
CHUNK_WORDS = 220 # ウィンドウサイズ
OVERLAP_WORDS = 40 # 次のチャンクに持ち越す単語数
STRIP_TAGS = ["script", "style", "noscript", "nav", "header", "footer", "aside", "form", "svg"]
def extract_text(html: str) -> str:
soup = BeautifulSoup(html, "html.parser")
for tag in soup(STRIP_TAGS):
tag.decompose()
root = soup.find("article") or soup.find("main") or soup.body or soup
text = root.get_text(" ", strip=True)
return " ".join(text.split())
def chunk(words: list[str]):
step = CHUNK_WORDS - OVERLAP_WORDS
for start in range(0, max(len(words) - OVERLAP_WORDS, 1), step):
yield start, " ".join(words[start:start + CHUNK_WORDS])
total = 0
with open("corpus.jsonl", "w", encoding="utf-8") as out:
for page in sorted(pathlib.Path("pages").glob("*.html")):
text = extract_text(page.read_text(encoding="utf-8"))
words = text.split()
for start, body in chunk(words):
out.write(json.dumps({
"source": page.stem,
"word_offset": start,
"n_words": len(body.split()),
"text": body,
}) + "\n")
total += 1
print(f"{page.name}: {len(words):,} words")
print(f"{total} チャンク -> corpus.jsonl")
出力される内容は、チャンクごとに1行です:
json
// 説明のサンプル — live build_corpus.pyの実行からのスキーマ; テキストは省略
{
"source": "best-llm-scrapers-2026",
"word_offset": 180,
"n_words": 220,
"text": "…俳優は構造化されたフィールドとして答えとその引用を返します…"
}
ここから、任意の埋め込みワークフローが引き継ぎます:corpus.jsonlを読み込み、textを埋め込み、sourceとword_offsetをメタデータとしてベクトルを保存します。出所のフィールドは、悪い取得をページとその位置に戻すことを可能にします。
責任を持ったソーシング
トレーニングまたは検索コーパスは、そのソースから法的重みを引き継ぎます。使用権のある公にアクセス可能なページのみを取り込む;各サイトの利用規約とロボットディレクティブを確認してからURLリストに追加する;ホストごとのリクエスト量を控えめに保つ;および著作権で保護されたテキストは再出版するのではなく取得して帰属するものとして扱う。コーパスにサードパーティのコンテンツが含まれている場合は、sourceフィールドを無傷のまま維持することが、引用とコピーの違いになります。
よくある質問
Q: URLに対してrequests.getを使えばいいのでは?
静的なページには効果的です。JavaScriptレンダリングされたりボットチェックされたりするサイトでは、静かにインデックスを汚染するシェルや中間ページを返します — ロック解除ツールは正にそれに存在します。
Q: チャンクはどれくらいの大きさが良いですか?
ここでの220/40のウィンドウは、文トランスフォーマークラスの埋め込みモデルには適切なデフォルトです。モデルのコンテキストや検索の細かさに合わせて調整し、アイデアが境界をまたいで生き残るように適度な重複を保ってください。
Q: インデックスに届く前に悪い取得をどう検出しますか?
サイズと内容のチェック:レンダリングされた記事ページは大きく(作業例のページは数百キロバイトに達します)、記事であるべきページから数百単語未満の抽出されたテキストはログを取る価値のある赤信号です。
Q: 重度のアンチボットベンダーの背後でページを取得できますか?
ロック解除ツールの仕事は、サーバー側でアンチボット層をクリアすることです。特定のページがまだクリアできない場合は、それを取得不可能として扱い、そのページを除外してください — 欠落したページは回復可能ですが、汚染されたインデックスはそうではありません。
Q: プロキシは必要ですか?
いいえ。出口とレンダリングはアクター内部で処理されます;あなたが送信するPOSTは全体の統合です。
Q: 埋め込みやベクトルストアはどこで登場しますか?
corpus.jsonlの下流で、すでに使用しているスタックとともに。このパイプラインは、クリーンで出所がタグ付けされたチャンクで意図的に終了します — すべての埋め込みツールが受け入れるフォーマットです。
結論:クリーンなインジションとクリーンなアウトプット
取り込みパイプラインは三つの短いファイルに集約されます:ロック解除ツールを介してレンダリングされたHTMLを取得し、クロームを減算し、重複と出所を持つチャンクに分けます。どれも華やかさはなく、すべてが検索結果が本当の記事テキストから得られるか、インデックスに滑り込んだローダーシェルから得られるかを決定します。URLリストを支援者が知るべきソースに向け、その実行をスケジュールし、生のページを保持してください — それらは監査の追跡記録です。
RAG取り込みパイプラインの構築の準備はできていますか?
コミュニティに参加して無料プランを取得し、データパイプラインを構築している開発者とつながりましょう:Discord · Telegram。
app.scrapeless.com で無料試用クレジットにサインアップし、取得コーパスが必要とするページをフェッチステージに指定してください。現在の料金プランについては、料金ページをご覧ください。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



