LangChainのWebBaseLoaderでWebページの内容が取得できない場合の対処法

LangChainには、Webページをドキュメントとして取得する「WebBaseLoader」が用意されています。Webページによっては、WebBaseLoaderで内容が取得できません。

## WebBaseLoaderでページを取得してみる

例えば、OpenAI社のNewsのページを取得してみます。

https://openai.com/index/introducing-openai-o1-preview/

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/11c6ebfa-28c5-4e58-d710-72cb41ad5006.png)


ライブラリーをインストールします。

```shell:Shell
pip install langchain langchain-community beautifulsoup4
```

以下のコードを実行します。

```python:app.py
import json
import os

os.environ["USER_AGENT"] = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
)

from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader(
    web_path="https://openai.com/index/introducing-openai-o1-preview/",
)
documents = loader.load()

for document in documents:
    print("--- page_content ---")
    print(document.page_content[0:100])
    print("-" * 20)

    print(json.dumps(document.metadata, indent=2, ensure_ascii=False))
```

```
--- page_content ---












Please turn JavaScript on and reload the page.Please enable Cookies and reload the page.
--------------------
{
  "source": "https://openai.com/index/introducing-openai-o1-preview/",
  "language": "No language found."
}
```

JavaScriptとクッキーを有効にしてねというメッセージしか取得できませんでした。

HTMLソースを取得し確認すると、「noscript」タグの内容だけが取得できており、ブラウザ上に表示されるコンテンツは取得できませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/60bb55fd-743f-d827-e183-2e35a21d1e0a.png)

このように、WebBaseLoaderではJavaScriptで動的に表示コンテンツを作成しているページはうまく内容を取得できません。

## AsyncChromiumLoaderを使う

このような動的なページを取得する際には、「AsyncChromiumLoader」を使うと動作が改善します。
内部でplaywrightを使用しているのでインストールします。

```shell:Shell
pip install playwright
```

playwrightに必要なブラウザモジュールをインストールします。

```shell:Shell
playwright install-deps
playwright install chromium
```

これで準備が整いました。WebBaseLoaderの代わりにAsyncChromiumLoaderを使用します。


```python:app.py
import json

from langchain_community.document_loaders import AsyncChromiumLoader

loader = AsyncChromiumLoader(
    urls=["https://openai.com/index/introducing-openai-o1-preview/"],
    user_agent="Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36",
)
documents = loader.load()


for document in documents:
    print("--- page_content ---")
    print(document.page_content[0:100])
    print("-" * 20)

    print(json.dumps(document.metadata, indent=2, ensure_ascii=False))
```
```
--- page_content ---
<!DOCTYPE html><html lang="en-US" dir="ltr" style="--document-width: 1280px; --gutter-size: max(20px
--------------------
{
  "source": "https://openai.com/index/introducing-openai-o1-preview/"
}
```

WebBaseLoaderでは、HTML内のテキストのみを勝手にパースして取得しますが、AsyncChromiumLoaderはHTML全体が返却されます。

どちらが良いとは言えないですが、WebBaseLoaderと同様の動作にしたい場合は、BeautifulSoupを間に挟むと良いでしょう。

```python:app.py
import json

from bs4 import BeautifulSoup
from langchain_community.document_loaders import AsyncChromiumLoader

loader = AsyncChromiumLoader(
    urls=["https://openai.com/index/introducing-openai-o1-preview/"],
    user_agent="Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36",
)
documents = loader.load()


def get_text(page_content: str):
    soup = BeautifulSoup(page_content, features="html.parser")
    return soup.get_text()


for document in documents:
    print("--- page_content ---")
    print(get_text(document.page_content)[0:100])
    print("-" * 20)

    print(json.dumps(document.metadata, indent=2, ensure_ascii=False))
```

```
--- page_content ---
Introducing OpenAI o1 | OpenAISkip to main contentResearchProductsSafetyCompanyIntroducing OpenAI o1
--------------------
{
  "source": "https://openai.com/index/introducing-openai-o1-preview/"
}
```

いい感じになりました。