データエンジニアとしての初期(2016年頃)には、様々なウェブサイトからデータをスクレイピングする責任を負っていました。ウェブスクレイピングは、自動化されたツールを利用してウェブサイトから大量のデータを取得する手法であり、通常はそれらのHTMLからデータを抽出します。
I remember building around the application, digging into the HTML code, and trying to figure out the best solutions for scraping all the data. One of my main challenges was dealing with frequent changes to the websites: for example, the Amazon pages I was scraping changed every one to two weeks.
私が大規模言語モデル(LLMs)について学び始めたとき、「LLMsを使ってウェブページからのデータを構造化することで、以前スクレイピングで直面した課題を回避できないだろうか?」と考えました。
それが可能か試してみましょう。
ウェブスクレイピングのツールとテクニック
当時主に使用していたツールはRequests、BeautifulSoup、そしてSeleniumでした。これらのサービスはそれぞれ異なる目的を持ち、異なるタイプのウェブ環境を対象としています。
- RequestsはPythonのライブラリで、HTTPリクエストを簡単に行うことができます。このライブラリはURLに対してGETおよびPOST操作を実行し、BeautifulSoupで解析可能なHTMLコンテンツを取得するためによく使用されます。
- BeautifulSoupはPythonのライブラリで、HTMLおよびXMLドキュメントの解析に使用されます。これは、ページソースからパースツリーを構築し、ページ上の様々な要素に簡単にアクセスできるようにします。通常、RequestsやSeleniumなどの他のライブラリと組み合わせて使用され、HTMLソースコードを提供します。
- Seleniumは、多くのJavaScriptが関与するウェブサイトで主に使用されます。BeautifulSoupとは異なり、Seleniumは単にHTMLコードを分析するだけでなく、クリックやスクロールなどのユーザーの操作をエミュレートすることでウェブサイトと対話します。これにより、動的にコンテンツを生成するウェブサイトからのデータ抽出が容易になります。
これらのツールは、ウェブサイトからデータを抽出しようとしていた私にとって不可欠でした。しかし、それらはまたいくつかの課題をもたらしました:コード、タグ、構造的要素は、ウェブサイトのレイアウトの変化に対応するために定期的に更新される必要があり、長期的なメンテナンスを複雑にしました。
大規模言語モデル(LLMs)とは何ですか?
大規模言語モデル(LLMs)は、膨大な量のテキストデータを読み取り、分析して学習できる次世代のコンピュータプログラムです。現代において、彼らは人間のようなナレッジを書く驚異的な能力を持っており、言語を処理し、人間の言語を理解する効率的なエージェントとなることができます。その種の状況では、テキストの文脈が非常に重要であったため、優れた能力が光りました。
LLMsをウェブスクレイピングに統合する
ウェブスクレイピングプロセスは、LLM(大規模言語モデル)を組み込むことで大幅に最適化できます。ウェブページからHTMLコードを取得し、それをLLMに入力して、参照するオブジェクトを抽出します。この手法は、マークアップ構造が進化しても、コンテンツ自体が通常変化しないため、メンテナンスを容易にする効果があります。
このような統合システムのアーキテクチャは以下の通りです:
- HTML取得: SeleniumやRequestsなどのツールを使用して、ウェブページのHTMLコンテンツを取得します。SeleniumはJavaScriptで読み込まれた動的コンテンツを処理でき、Requestsは静的ページに適しています。
- HTML解析: BeautifulSoupを使用して、このHTMLをテキストとして解析し、HTMLのノイズ(フッターやヘッダーなど)を除去します。
- Pydanticモデルの作成: スクレイピングするPydanticモデルを定義します。これにより、入力されたデータが事前に定義されたスキーマに従って型付けされ、構造化されることが保証されます。
- LLMへのプロンプト生成: LLMに情報を抽出するための指示を伝えるプロンプトを設計します。
- LLMによる処理: モデルはHTMLを読み取り、理解し、データ処理と構造化のための指示に従って動作します。
- 構造化データの出力: LLMは、Pydanticモデルによって定義された構造化されたオブジェクトの形で出力を提供します。
このワークフローは、LLMを使用してHTML(非構造化データ)を構造化データに変換し、非標準的なデザインやウェブソースHTMLの動的な変更などの問題を解決します。
LangChainとBeautifulSoupおよびPydanticの統合
これは静的なウェブページです 例として選ばれました。そこにリストされているすべてのアクティビティをスクレイプして、構造化された形で提示するというアイデアです。
この方法では、LLMがそれを処理する前に静的なウェブページから生のHTMLを抽出してクリーンアップします。
from bs4 import BeautifulSoup
import requests
def extract_html_from_url(url):
try:
# requestsを使用してURLからHTMLコンテンツを取得
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad responses (4xx and 5xx)
# BeautifulSoupを使用してHTMLコンテンツを解析
soup = BeautifulSoup(response.content, "html.parser")
excluded_tagNames = ["footer", "nav"]
# 'footer'および'nav'のタグ名を持つ要素を除外
for tag_name in excluded_tagNames:
for unwanted_tag in soup.find_all(tag_name):
unwanted_tag.extract()
# アンカータグ内のhrefsを維持するためにスープを処理
for a_tag in soup.find_all("a"):
href = a_tag.get("href")
if href:
a_tag.string = f"{a_tag.get_text()} ({href})"
return ' '.join(soup.stripped_strings) # Return text content with preserved hrefs
except requests.exceptions.RequestException as e:
print(f"Error fetching data from {url}: {e}")
return None
次のステップは、ウェブページからスクレイプするPydanticオブジェクトを定義することです。作成する必要があるオブジェクトは2つです:
Activity
: これは、アクティビティに関連するすべてのメタデータを表すPydanticオブジェクトであり、その属性とデータタイプが指定されています。いくつかのフィールドにOptional
としてマークを付けています。それらがすべてのアクティビティに対して利用可能でない場合のためにです。説明、例、およびメタデータを提供することで、LLMは属性の定義をより良く理解できます。ActivityScraper
: これはActivity
をラップするPydanticウィザードです。このオブジェクトの目的は、LLMに複数のアクティビティをスクレイピングする必要があることを理解させることです。
from pydantic import BaseModel, Field
from typing import Optional
class Activity(BaseModel):
title: str = Field(description="The title of the activity.")
rating: float = Field(description="The average user rating out of 10.")
reviews_count: int = Field(description="The total number of reviews received.")
travelers_count: Optional[int] = Field(description="The number of travelers who have participated.")
cancellation_policy: Optional[str] = Field(description="The cancellation policy for the activity.")
description: str = Field(description="A detailed description of what the activity entails.")
duration: str = Field(description="The duration of the activity, usually given in hours or days.")
language: Optional[str] = Field(description="The primary language in which the activity is conducted.")
category: str = Field(description="The category of the activity, such as 'Boat Trip', 'City Tours', etc.")
price: float = Field(description="The price of the activity.")
currency: str = Field(description="The currency in which the price is denominated, such as USD, EUR, GBP, etc.")
class ActivityScrapper(BaseModel):
Activities: list[Activity] = Field("List of all the activities listed in the text")
最後に、LLMの設定があります。LangChainライブラリを使用します。これは、素晴らしいツールキットを提供し、始めるのに最適です。
A key component here is the PydanticOutputParser
. Essentially, this will translate our object into instructions, as illustrated in the Prompt
, and also parse the output of the LLM to retrieve the corresponding list of objects.
from langchain.prompts import PromptTemplate
from langchain.output_parsers import PydanticOutputParser
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(temperature=0)
output_parser = PydanticOutputParser(pydantic_object = ActivityScrapper)
prompt_template = """
You are an expert making web scrapping and analyzing HTML raw code.
If there is no explicit information don't make any assumption.
Extract all objects that matched the instructions from the following html
{html_text}
Provide them in a list, also if there is a next page link remember to add it to the object.
Please, follow carefulling the following instructions
{format_instructions}
"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["html_text"],
partial_variables={"format_instructions": output_parser.get_format_instructions}
)
chain = prompt | llm | output_parser
最後のステップは、チェーンを呼び出し、結果を取得することです。
url = "https://www.civitatis.com/es/budapest/"
html_text_parsed = extract_html_from_url(url)
activites = chain.invoke(input={
"html_text": html_text_parsed
})
activites.Activities
データはこのようになります。ウェブサイト全体をスクレイピングするのに46秒かかります。
[Activity(title='Paseo en barco al anochecer', rating=8.4, reviews_count=9439, travelers_count=118389, cancellation_policy='Cancelación gratuita', description='En este crucero disfrutaréis de las mejores vistas de Budapest cuando se viste de gala, al anochecer. El barco es panorámico y tiene partes descubiertas.', duration='1 hora', language='Español', category='Paseos en barco', price=21.0, currency='€'),
Activity(title='Visita guiada por el Parlamento de Budapest', rating=8.8, reviews_count=2647, travelers_count=34872, cancellation_policy='Cancelación gratuita', description='El Parlamento de Budapest es uno de los edificios más bonitos de la capital húngara. Comprobadlo vosotros mismos en este tour en español que incluye la entrada.', duration='2 horas', language='Español', category='Visitas guiadas y free tours', price=27.0, currency='€')
...
]
デモとフルリポジトリ
I have created a quick demo using Streamlit available here.
最初の部分では、モデルについて紹介されています。必要なだけ多くの行を追加し、各属性の名前、タイプ、説明を指定できます。これにより、ウェブスクレイピングコンポーネントで使用するためのPydanticモデルが自動的に生成されます。
次の部分では、URLを入力し、ウェブサイト上のボタンをクリックしてすべてのデータをスクレイピングできます。スクレイピングが完了すると、ダウンロードボタンが表示され、データをJSON形式でダウンロードできます。
どうぞお楽しみください!
結論
LLMは、ウェブサイトやPDFなどの非構造化データから効率的にデータを抽出する新たな可能性を提供します。LLMによるウェブスクレイピングの自動化は、時間を節約するだけでなく、取得されたデータの品質を保証します。
しかし、生のHTMLをLLMに送信することは、トークンコストを増加させ、非効率になる可能性があります。HTMLはしばしば様々なタグ、属性、コンテンツを含むため、コストが急速に上昇することがあります。
したがって、HTMLを事前処理してクリーンアップし、不要なメタデータや使用されていない情報をすべて削除することが重要です。このアプローチは、LLMをウェブのデータ抽出器として使用しながら、適度なコストを維持するのに役立ちます。
適切なツールを適切な作業に使用しましょう!
Source:
https://dzone.com/articles/enhancing-web-scraping-with-large-language-models