在我担任数据工程师的早期(可以追溯到 2016 年),我负责从不同的网站抓取数据。网络抓取就是利用自动化的工具从网站(通常是从其 HTML)中获取大量数据。
我记得围绕应用程序进行构建,深入研究 HTML 代码,并试图找出抓取所有数据的最佳解决方案。我的主要挑战之一是处理网站的频繁更改:例如,我抓取的亚马逊页面每隔一到两周就会更改一次。
当我开始阅读有关大型语言模型 (LLM) 的文章时,我突然想到的一个想法是,“我能否避免使用 LLM 从网页中构建数据时遇到的所有陷阱?
让我们看看我能不能。
当时,我使用的主要工具是 Requests、BeautifulSoup 和 Selenium。每个服务都有不同的用途,并针对不同类型的 Web 环境。
当我尝试从网站中提取数据时,这些工具是必不可少的。然而,它们也带来了一些挑战:代码、标签和结构元素必须定期更新以适应网站布局的变化,使长期维护变得复杂。
大型语言模型 (LLM) 是下一代计算机程序,可以通过读取和分析大量文本数据来学习。在这个年龄段,他们天赋异禀,能够以类似人类的叙述方式写作,使他们成为处理语言和理解人类语言的有效代理人。在那种情况下,出色的能力闪耀着光芒,文本上下文非常重要。
在实现 LLM 时,可以在很大程度上优化 Web 抓取过程。我们需要从网页中获取 HTML 代码并将其输入到 LLM 中,LLM 将提取它所引用的对象。因此,这种策略有助于使维护变得容易,因为标记结构可以演变,但内容本身通常不会改变。
以下是这种集成系统的架构:
此工作流有助于使用 LLM 将 HTML(非结构化数据)转换为结构化数据,从而解决非标准设计或动态修改 Web 源 HTML 等问题。
这是为示例选择的静态网页。这个想法是抓取那里列出的所有活动并以结构化的方式呈现它们。
此方法将从静态网页中提取原始 HTML,并在 LLM 处理它之前对其进行清理。
from bs4 import BeautifulSoupbr
import requestsbr
br
br
def extract_html_from_url(url):br
try:br
# Fetch HTML content from the URL using requestsbr
response = requests.get(url)br
response.raise_for_status() # Raise an exception for bad responses (4xx and 5xx)br
br
# Parse HTML content using BeautifulSoupbr
soup = BeautifulSoup(response.content, "html.parser")br
excluded_tagNames = ["footer", "nav"]br
# Exclude elements with tag names 'footer' and 'nav'br
for tag_name in excluded_tagNames:br
for unwanted_tag in soup.find_all(tag_name):br
unwanted_tag.extract()br
br
# Process the soup to maintain hrefs in anchor tagsbr
for a_tag in soup.find_all("a"):br
href = a_tag.get("href")br
if href:br
a_tag.string = f"{a_tag.get_text()} ({href})"br
br
return ' '.join(soup.stripped_strings) # Return text content with preserved hrefsbr
br
except requests.exceptions.RequestException as e:br
print(f"Error fetching data from {url}: {e}")br
return None
下一步是定义我们将从网页中抓取的 Pydantic 对象。需要创建两个对象:
from pydantic import BaseModel, Fieldbr
from typing import Optionalbr
br
class Activity(BaseModel):br
title: str = Field(description="The title of the activity.")br
rating: float = Field(description="The average user rating out of 10.")br
reviews_count: int = Field(description="The total number of reviews received.")br
travelers_count: Optional[int] = Field(description="The number of travelers who have participated.")br
cancellation_policy: Optional[str] = Field(description="The cancellation policy for the activity.")br
description: str = Field(description="A detailed description of what the activity entails.")br
duration: str = Field(description="The duration of the activity, usually given in hours or days.")br
language: Optional[str] = Field(description="The primary language in which the activity is conducted.")br
category: str = Field(description="The category of the activity, such as 'Boat Trip', 'City Tours', etc.")br
price: float = Field(description="The price of the activity.")br
currency: str = Field(description="The currency in which the price is denominated, such as USD, EUR, GBP, etc.")br
br
br
class ActivityScrapper(BaseModel):br
Activities: list[Activity] = Field("List of all the activities listed in the text")
最后,我们有了 LLM 的配置。我们将使用 LangChain 库,它提供了一个出色的入门工具包。
这里的一个关键组件是 .从本质上讲,这会将我们的对象转换为指令,如 所示,并且还解析 LLM 的输出以检索相应的对象列表。PydanticOutputParserPrompt
from langchain.prompts import PromptTemplatebr
from langchain.output_parsers import PydanticOutputParserbr
from langchain_openai import ChatOpenAIbr
from dotenv import load_dotenvbr
br
load_dotenv()br
br
llm = ChatOpenAI(temperature=0)br
output_parser = PydanticOutputParser(pydantic_object = ActivityScrapper)br
br
prompt_template = """br
You are an expert making web scrapping and analyzing HTML raw code.br
If there is no explicit information don't make any assumption.br
Extract all objects that matched the instructions from the following htmlbr
{html_text}br
Provide them in a list, also if there is a next page link remember to add it to the object.br
Please, follow carefulling the following instructionsbr
{format_instructions}br
"""br
br
prompt = PromptTemplate(br
template=prompt_template,br
input_variables=["html_text"],br
partial_variables={"format_instructions": output_parser.get_format_instructions}br
)br
br
chain = prompt | llm | output_parser
最后一步是调用链并检索结果。
url = "https://www.civitatis.com/es/budapest/"br
html_text_parsed = extract_html_from_url(url)br
activites = chain.invoke(input={br
"html_text": html_text_parsedbr
})br
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='€'),br
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='€')br
...br
]
我使用此处提供的 Streamlit 创建了一个快速演示。
在第一部分中,您将了解该模型。您可以根据需要添加任意数量的行,并指定每个属性的名称、类型和描述。这将自动生成一个 Pydantic 模型,用于 Web 抓取组件。
下一部分允许您输入 URL 并通过单击网页上的按钮来抓取所有数据。抓取完成后会出现一个下载按钮,允许您以 JSON 格式下载数据。
随意玩吧!
LLM 为从非结构化数据(如网站、PDF 等)中高效提取数据提供了新的可能性。LLM的网页抓取自动化不仅可以节省时间,还可以确保检索到的数据的质量。
但是,将原始 HTML 发送到 LLM 可能会增加令牌成本并使其效率低下。由于 HTML 通常包含各种标签、属性和内容,因此成本会迅速上升。
因此,对 HTML 进行预处理和清理,删除所有不必要的元数据和未使用的信息至关重要。这种方法将有助于将 LLM 用作 Web 的数据提取器,同时保持合理的成本。
适合工作的工具!
原文标题:Enhancing Web Scraping With Large Language Models: A Modern Approach
原文链接:https://dzone.com/articles/enhancing-web-scraping-with-large-language-models
作者:Nacho Corcuera
编译:LCR