# LangChainとLlamaIndex: Webページローダーとread_urlツール

> LangChainとLlamaIndexのWebページローダー: read_urlツール、ドキュメントローダー、クロールから構築した小さなRAGインデックスを、すべてPythonで。KYC不要、暗号資産で支払い。

- URL: https://webreaderapi.com/ja/docs/langchain/
- 最終更新: 2026-10-22
- 発行者: WebReaderAPI (https://webreaderapi.com)

PythonのAIスタックでAPIを使う方法は2つあります。ページをドキュメントとして読み込んで検索に使うか、エージェントに`read_url`ツールを与えるかです。同じ20行のつなぎ込みコードがLangChainとLlamaIndexの両方で機能します。

**概要 — WebページをLangChainやLlamaIndexにクリーンなMarkdownとして読み込むには？**

**WebReaderAPI**の`/read`エンドポイントを呼び出し、各レスポンスを`Document`でラップします。Markdownをcontentに、`final_url`とタイトルをmetadataに入れます。エージェントには同じ呼び出しを`read_url`ツールとして公開します。ページは1ページ1クレジット、ブラウザ使用時は2クレジットで、2026年10月時点では$0.07/1Kページから利用できます。当社のパッケージは不要です。KYC不要、暗号資産で支払い。

## 生のHTMLではなくMarkdownドキュメントを使うのはなぜですか？

検索はテキストに対して機能し、Markdownは重要な構造を残しつつそれ以外を削除するからです。見出しが残るので、スプリッターはトピックの変化で分割できます。リストやテーブルが残るので、手順や仕様がまとまったまま保たれます。ナビゲーション、Cookieバナー、広告は削除されるため、埋め込みを汚染したりトークンを無駄に消費したりしません。

メタデータは同じ呼び出しから取得します。引用元としての`final_url`、フィルタリング用の`title`、`published_at`、`language`です。

*reader.py: 共通ヘルパー (コードと同じ場所に保存してください)*

```python
import os

import requests

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


class PageError(Exception):
    pass


def read_page(url, **options):
    resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
    body = resp.json()
    if resp.status_code != 200:
        raise PageError(body["error"]["code"])
    return body

```

## LangChainのドキュメントローダーを構築するには？

各ページを`langchain_core.documents.Document`に変換し、`langchain-text-splitters`パッケージの`MarkdownHeaderTextSplitter`で見出しごとに分割します。各チャンクはメタデータに見出しパスを保持し、ソースURLを追加することで、すべての回答が元のページを指し示せるようになります。

robots.txtで拒否されたページなどスキップされたページは、コードとともに`PageError`を発生させます。ログに記録して処理を続けてください。

*LangChain: ページの読み込みと分割*

```python
from langchain_core.documents import Document
from langchain_text_splitters import MarkdownHeaderTextSplitter

from reader import PageError, read_page

splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[("#", "h1"), ("##", "h2"), ("###", "h3")])


def load_pages(urls):
    chunks = []
    for url in urls:
        try:
            page = read_page(url)
        except PageError as exc:
            print("skipped", url, exc)
            continue
        for part in splitter.split_text(page["markdown"]):
            part.metadata.update({"source": page["final_url"], "title": page.get("title", "")})
            chunks.append(part)
    return chunks


docs = load_pages(["https://example.com/docs/install", "https://example.com/docs/configure"])
print(len(docs), "chunks", isinstance(docs[0], Document) if docs else "")

```

## LangChainエージェントにread_urlツールを与えるには？

`langchain_core.tools`の`@tool`で関数をデコレートします。docstringがモデルに読まれるツールの説明になるため、ツールが何を返すか、いつ使うかを書いてください。プレーンテキストを返します。タイトル行、ソース、Markdownを並べ、モデルに合わせて`max_chars`で切り詰めます。

ツール呼び出しをサポートする任意のチャットモデルにツールをバインドするか、エージェントのコンストラクタに渡します。モデルはその後、会話内または検索結果のURLを使って`read_url`を呼び出します。

*LangChain: read_urlツール*

```python
import os

from langchain.chat_models import init_chat_model
from langchain_core.tools import tool

from reader import PageError, read_page


@tool
def read_url(url: str, max_chars: int = 20000) -> str:
    """Read a public web page and return its main content as Markdown, with its title and source URL."""
    try:
        page = read_page(url, max_chars=max_chars)
    except PageError as exc:
        return "Could not read the page: " + str(exc)
    return "# " + (page.get("title") or url) + "\nSource: " + page["final_url"] + "\n\n" + page["markdown"]


model = init_chat_model(os.environ.get("CHAT_MODEL", "openai:gpt-4.1-mini")).bind_tools([read_url])
reply = model.invoke("Summarise https://example.com/blog/first-post in three bullet points.")
print(reply.tool_calls)

```

## LlamaIndexで同じことをするには？

LlamaIndexは独自の`Document`クラスを使い、`text`と`metadata`、そして関数をツールに変換する`FunctionTool.from_defaults`を利用します。ヘルパーは同じままです。ドキュメントから`VectorStoreIndex.from_documents`でインデックスを構築します。これはLlamaIndexの設定で指定した埋め込みモデルを使用します。

*LlamaIndex: クロールのインデックス化とread_urlツール*

```python
import os
import time

import requests
from llama_index.core import Document, VectorStoreIndex
from llama_index.core.tools import FunctionTool

from reader import HEADERS, PageError, read_page


def crawl_pages(start_url, limit=200):
    job = requests.post("https://webreaderapi.com/api/v1/crawl", headers=HEADERS, json={"url": start_url, "limit": limit}, timeout=30).json()
    pages, cursor = [], None
    while True:
        params = {"cursor": cursor} if cursor else {}
        res = requests.get("https://webreaderapi.com/api/v1/jobs/" + job["job_id"], headers=HEADERS, params=params, timeout=30).json()
        pages += [r for r in res["results"] if "error" not in r]
        cursor = res.get("next_cursor")
        if not cursor and res["status"] == "done":
            return pages
        if not cursor:
            time.sleep(5)


pages = crawl_pages("https://docs.example.com/")
docs = [Document(text=p["markdown"], metadata={"source": p["final_url"], "title": p.get("title", "")}) for p in pages]
index = VectorStoreIndex.from_documents(docs)
print(index.as_query_engine().query("How do I install the product?"))


def read_url(url: str, max_chars: int = 20000) -> str:
    """Read a public web page and return its main content as Markdown."""
    try:
        return read_page(url, max_chars=max_chars)["markdown"]
    except PageError as exc:
        return "Could not read the page: " + str(exc)


read_tool = FunctionTool.from_defaults(fn=read_url, name="read_url")
print(os.environ.get("CHAT_MODEL", "set CHAT_MODEL for your agent"), read_tool.metadata.name)

```

## 小さなRAGインデックスにはどのくらいの規模のクロールが必要ですか？

思うより少なくて済みます。製品のドキュメントは数百ページであることが多く、`include_paths`をドキュメントのセクションに設定したクロールはその部分だけを読みます。1ページ1クレジットなら、300ページのドキュメントサイトは約300クレジットで、10,000ページ ($3.75) なら何度でも更新できます。

情報源の変化頻度に合わせたスケジュールで再クロールし、前回の実行と`final_url`および`word_count`を比較して、変更されたページだけを再埋め込みします。プリペイドクレジットに有効期限はありません。未使用のページは使うまでアカウントに残ります。

## LangChainとLlamaIndex: よくある質問

### LangChainやLlamaIndex用のWebReaderAPIパッケージはありますか？

いいえ。このページのヘルパーとツールが統合のすべてであり、自由に改変できます。WebReaderAPIはプレーンなHTTPS APIで、任意のPython HTTPクライアントから呼び出せます。

### 必要なLangChainのバージョンは？

`langchain_core`、`langchain-text-splitters`、`init_chat_model`を備えた最近のバージョンであればどれでも構いません。ツールデコレーターと`Document`クラスは最近のリリースで安定しています。

### 変更されていないページの再読み取りを避けるには？

`final_url`、`word_count`、各読み取りの日付を保存します。スケジュールに従って再読み取りし、ページの語数またはコンテンツハッシュが変わった場合にのみ再埋め込みします。再読み取りごとに1クレジットかかるので、情報源に合ったペースを選んでください。

### 一部のページが拒否された場合は？

`blocked_by_robots`などのコードとともに`PageError`が発生し、クレジットは消費されません。サイトが当社のリクエストを拒否した場合、その回答をそのまま返します。CAPTCHAを解いたり、アカウントにログインしたり、ブロックやペイウォールを回避しようとしたりすることは決してありません。

---

WebReaderAPI: WebReaderAPIはAIエージェント向けのWebリーダーAPIで、あらゆる公開WebページをクリーンなMarkdownに変換します。暗号資産で支払い、本人確認書類のアップロードは不要です。$0.07/1Kページから購入できるプリペイドクレジットに有効期限はなく、robots.txtを尊重します。KYC不要 — 身分証もセルフィーも書類も不要。暗号資産で支払い。
