利用大型语言模型增强网络抓取:现代方法

在我作为数据工程师的早期阶段(追溯至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来规避以往在网页数据结构化过程中遇到的种种难题?”

让我们拭目以待。

网页抓取工具与技术

当时,我主要使用的工具包括RequestsBeautifulSoupSelenium。每个工具都有其独特用途,针对不同类型的网络环境设计。

  • Requests是一个Python库,用于轻松执行HTTP请求。该库能够对请求中提供的URL执行GET和POST操作,常用于获取可由BeautifulSoup解析的HTML内容。
  • BeautifulSoup 是一个用于解析HTML和XML文档的Python库,它通过页面源码构建解析树,便于轻松访问页面上的各种元素。通常,它与其他库如RequestsSelenium配合使用,后者提供HTML源代码。
  • Selenium主要用于涉及大量JavaScript的网站。与BeautifulSoup不同,Selenium不仅仅分析HTML代码:它通过模拟用户操作如点击和滚动来与网站互动,这有助于从动态生成内容的网站中提取数据。

这些工具在我尝试从网站提取数据时不可或缺。然而,它们也带来了一些挑战:代码、标签和结构元素需定期更新以适应网站布局的变化。

何为大型语言模型(LLMs)?

大型语言模型(LLMs)是下一代计算机程序,能够通过阅读和分析大量文本数据进行学习。在当今时代,它们具备惊人的能力,能以人类般的叙述方式写作,使其成为处理语言和理解人类语言的高效工具。在文本上下文极为重要的情况下,这种卓越能力尤为突出。

将LLMs整合到网络爬虫中

将大型语言模型(LLMs)融入网页抓取过程中,能极大优化其效率。我们需要从网页中提取HTML代码,并将其输入到LLM中,后者将识别并提取出所指的对象。因此,这种方法有助于简化维护工作,因为虽然标记结构可能会演变,但内容本身通常保持不变。

以下是这种集成系统架构的概览:

  1. 获取HTML:利用Selenium或Requests等工具抓取网页的HTML内容。Selenium适用于处理通过JavaScript加载的动态内容,而Requests则适合静态页面。
  2. 解析HTML:通过BeautifulSoup,我们将HTML解析为文本,从而去除HTML中的噪声(如页脚、页眉等)。
  3. 创建Pydantic模型:定义用于抓取的Pydantic模型,确保数据类型和结构符合预设模式。
  4. 生成LLM提示:设计提示语,告知LLM需要提取哪些信息。
  5. LLM处理:模型读取HTML,理解其内容,并根据指令进行数据处理和结构化。
  6. 输出结构化数据:LLM将以Pydantic模型定义的结构化对象形式提供输出结果。

此工作流程利用大型语言模型(LLMs)将HTML(非结构化数据)转换为结构化数据,解决诸如网页源HTML的非标准设计或动态修改等问题。

结合LangChain与BeautifulSoup及Pydantic

这是用于示例的静态网页,目的是抓取该页上列出的所有活动,并以结构化方式呈现。

该方法首先从静态网页提取原始HTML,并在LLM处理之前进行清洗。

Python

 

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()

        # 处理soup以保留锚点标签中的href属性
        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对象。需创建两个对象:

  • Activity: 这是一个Pydantic对象,代表与活动相关的所有元数据,其属性和数据类型已明确指定。我们已将某些字段标记为Optional ,以防它们并非所有活动都具备。提供描述、示例及任何元数据将有助于LLM更准确地定义属性。
  • ActivityScraper: 这是一个围绕Activity的Pydantic包装器。该对象的目的是确保LLM理解需要抓取多个活动。
Python

 

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.

Python

 

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

最后一步是调用链并检索结果。

Python

 

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秒。

Python

 

[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自动化网页抓取不仅节省时间,还确保了检索数据的质量。

然而,向大型语言模型(LLM)发送原始HTML可能会增加令牌成本并降低效率。由于HTML通常包含各种标签、属性和内容,成本可能迅速上升。

因此,对HTML进行预处理和清理至关重要,剔除所有不必要的元数据和未使用的信息。这种方法有助于将LLM作为网络数据提取器使用,同时控制成本在合理范围。

合适的工具用于合适的工作!

Source:
https://dzone.com/articles/enhancing-web-scraping-with-large-language-models