利用大型語言模型增強網絡爬蟲:現代方法

在我擔任資料工程師的初期(可追溯至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. 生成LLMs的提示:設計一個提示,告知LLM需要提取哪些信息。
  5. LLM處理:模型讀取HTML,理解其內容,並根據指令進行數據處理和結構化。
  6. 結構化數據輸出:LLM將以Pydantic模型定義的結構化對象形式提供輸出。

此工作流程利用大型語言模型(LLM)將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()

        # 處理湯以保留錨點標籤中的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對象。需要創建兩個對象:

  • 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自動化網頁爬取不僅節省時間,更保證所提取數據的品質。

然而,將原始HTML傳送給大型語言模型(LLM)可能會增加令牌成本並降低效率。由於HTML通常包含多種標籤、屬性和內容,成本可能迅速上升。

因此,對HTML進行預處理和清理,移除所有不必要的元數據和未使用的信息至關重要。這種方法將有助於將LLM用作網絡數據提取器,同時保持合理的成本。

適合的工具做適合的工作!

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