3个最流行的开源大模型网络爬虫框架

在传统网络爬虫中,主要的挑战一直是手动操作的工作量。使用像 Beautiful Soup(BS4)和 Selenium 这样的工具时,我们需要为每个新网站编写解析代码,需要适配和适应不同的 HTML 结构。这种不断的修改既耗时又容易出错。然而,当出现了大模型之后就没那么复杂了。随着具备视觉功能的大型语言模型(LLM)的出现,我们现在可以创建几乎通用的网络爬虫代理,大大简化和自动化了这一过程。

在这篇博客中,我们的主要关注点是探讨三种强大的工具:ScrapeGraph、FireCrawl 和 AgentQL。这些创新的库在革命性地改变网络爬虫领域方面发挥了关键作用,提供了先进的功能,使我们能够创建高效且多功能的爬虫代理。通过深入的讨论和实际示例,我们将详细探讨这些工具如何简化网络爬虫过程,并实现构建由 LLM 模型驱动的爬虫代理的目标。

ScrapeGraph

ScrapeGraphAI 是一个开源框架,它利用大型语言模型(LLM)和直接图逻辑的力量来简化网络爬虫过程。使用 ScrapeGraphAI,为网站、文档和 XML 文件创建爬虫管道变得轻而易举。你只需指定要提取的信息,其余的工作由库来处理。其直观的界面和先进的功能使其成为开发人员寻求高效、精确的网络爬虫解决方案时的首选。

import os
from dotenv import load_dotenv
from scrapegraphai.graphs import SmartScraperGraph
from scrapegraphai.utils import prettify_exec_info
import json

# 从.env加载环境变量
load_dotenv()

# 从环境变量加载openai key
openai_key = os.getenv("OPENAI_APIKEY")

# 配置SmartScraperGraph
graph_config = {
   "llm": {
      "api_key": openai_key,
      "model": "gpt-3.5-turbo",
   },
}

# 创建SmartScraperGraph并运行
smart_scraper_graph = SmartScraperGraph(
   prompt="列举所有的产品和他们的价格",
   # 接收一个html网页页面
   source="https://s.taobao.com/search?page=1&q=iphone",
   config=graph_config
)

# 执行爬虫并保存结果
result = smart_scraper_graph.run()
with open("results.json", 'w', encoding='utf-8') as f:
      json.dump(result, f, indent=4)

我们导入了必要的模块和库,如 os、dotenv、SmartScraperGraph 和 json。首先,我们从 .env 文件中加载环境变量,这是安全存储像 API 密钥等敏感信息的常见做法。graph_config 字典包含 SmartScraperGraph 所需的配置设置。在本例中,它包含 OpenAI API 密钥并指定使用的 GPT 模型(gpt-3.5-turbo)。接下来,我们创建 SmartScraperGraph 类的实例,向其提供提示(查询)、源(要爬取的网页 URL)和配置设置。在 smart_scraper_graph 实例上调用 run() 方法来执行爬虫并从网页中提取数据。提取的数据存储在 result 变量中。最后,使用 json.dump() 方法将提取的数据保存到名为 “results.json” 的 JSON 文件中,供进一步处理或分析。

{
    "products": [
        {
            "Name": "Apple/苹果 iPhone 13 Pro Max苹果13promax 苹果13 pro 手机",
            "Price": "¥3238.00"
        },
        {
            "Name": "新款Apple/苹果 iPhone 15 Pro Max 苹果5G手机15ProMax 国行正品",
            "Price": "¥7428.00"
        },
        {
            "Name": "Apple/苹果 iPhone 15 支持移动联通电信5G 双卡双待手机",
            "Price": "¥6999.00"
        },
        {
            "Name": "Apple/苹果 iPhone14ProMax双卡原装正品苹果14Promax全网通全新",
            "Price": "¥5999.00"
        },
        {
            "Name": "Apple/苹果 iPhone 15 Pro Max",
            "Price": "¥9999.00"
        }
    ]
}

如果你想从不同的来源爬取数据,只需在代码中更改 URL。SmartScraperGraph 的灵活性允许你在不显著修改代码的情况下,针对各种网站或网页。这意味着你可以根据具体需求调整爬虫过程,轻松从各种来源收集数据。虽然 SmartScraperGraph 在处理某些网站的弹出窗口或拦截器时可能遇到限制,但需要注意的是,SmartScraperGraph 是一个开源库,这意味着你可以根据具体要求对其进行定制。

FireCrawl

Firecrawl 作为一个强大的解决方案,配备了一系列功能,旨在克服网络爬虫工作中的固有挑战。它高效地管理代理、缓存、速率限制等复杂性,确保数据检索过程的顺畅。Firecrawl 的爬取能力扩展到网站的所有可访问子页面,无论是否存在站点地图,保证全面的数据提取。即使面对通过 JavaScript 动态渲染的内容,Firecrawl 也能非常高效的地捕获每一条有价值的信息。其输出经过 Markdown 格式化,简化了与大型语言模型(LLM)和其他应用程序的集成。

你可以注册 Firecrawl 的免费套餐,获得基本的爬虫功能。通过在这注册,你可以爬取最多 500 个页面,限制为每分钟 5 次爬取以及 1 个并发爬取任务。

下面是一个使用FireCrawl爬取

from firecrawl import FirecrawlApp
from openai import OpenAI
from dotenv import load_dotenv
import os
import json
import pandas as pd
from datetime import datetime

def scrape_data(url):
    load_dotenv()
    # 初始化FirecrawlApp实例
    app = FirecrawlApp(api_key=os.getenv('FIRECRAWL_API_KEY'))

    # 爬取单个URL
    scraped_data = app.scrape_url(url, {'pageOptions': {'onlyMainContent': True}})

    # 检查是否爬取到了markdown数据
    if 'markdown' in scraped_data:
        return scraped_data['markdown']
    else:
        raise KeyError("The key 'markdown' does not exist in the scraped data.")

def save_raw_data(raw_data, timestamp, output_folder='output'):
    # 确保输出文件夹存在
    os.makedirs(output_folder, exist_ok=True)

    # 保存原始数据到Markdown文件
    raw_output_path = os.path.join(output_folder, f'rawData_{timestamp}.md')
    with open(raw_output_path, 'w', encoding='utf-8') as f:
        f.write(raw_data)
    print(f"Raw data saved to {raw_output_path}")

def format_data(data, fields=None):
    load_dotenv()
    # 初始化OpenAI实例
    client = OpenAI(api_key=os.getenv('OPENAI_APIKEY'))

    # 如果未提供字段列表,则使用默认字段
    if fields is None:
        fields = ["名称","价格","地址", "链接"]

    # 定义系统消息内容
    system_message = f"""你是一个智能文本提取和转换助手。你的任务是从给定的文本中提取结构化信息,并将其转换为纯JSON格式。JSON
    应仅包含从文本中提取的结构化数据,不包含任何额外的评论、解释或无关的信息。你可能会遇到无法找到所需字段数据的情况,或者数据会以外语形式出现。请处理以下文本,并以纯JSON格式提供输出,JSON前后不应有任何文字。:"""

    # 定义用户消息内容
    user_message = f"请提供要处理的文本和需要提取的信息字段。:\nPage content:\n\n{data}\n\nInformation to extract: {fields}"

    response = client.chat.completions.create(
        model="gpt3.5",
        response_format={"type": "json_object"},
        messages=[
            {
                "role": "system",
                "content": system_message
            },
            {
                "role": "user",
                "content": user_message
            }
        ]
    )

    # 检查API响应是否包含选择数据
    if response and response.choices:
        formatted_data = response.choices[0].message.content.strip()
        print(f"Formatted data received from API: {formatted_data}")

        try:
            parsed_json = json.loads(formatted_data)
        except json.JSONDecodeError as e:
            print(f"JSON decoding error: {e}")
            print(f"Formatted data that caused the error: {formatted_data}")
            raise ValueError("The formatted data could not be decoded into JSON.")

        return parsed_json
    else:
        raise ValueError("The OpenAI API response did not contain the expected choices data.")

def save_formatted_data(formatted_data, timestamp, output_folder='output'):
    # Ensure the output folder exists
    os.makedirs(output_folder, exist_ok=True)

    # 保存格式化数据到JSON文件
    output_path = os.path.join(output_folder, f'sorted_data_{timestamp}.json')

    with open(output_path, 'w', encoding='utf-8') as f:
        json.dump(formatted_data, f, indent=4)
    print(f"Formatted data saved to {output_path}")

    # 检查格式化数据是否为字典且只包含一个键
    if isinstance(formatted_data, dict) and len(formatted_data) == 1:
        key = next(iter(formatted_data))  # Get the single key
        formatted_data = formatted_data[key]

    # 转换格式化数据为pandas DataFrame
    df = pd.DataFrame(formatted_data)

    # 准换格式化数据为pandas DataFrame
    if isinstance(formatted_data, dict):
        formatted_data = [formatted_data]

    df = pd.DataFrame(formatted_data)

    # 保存格式化数据到CSV文件
    # excel_output_path = os.path.join(output_folder, f'sorted_data_{timestamp}.xlsx')
    df.to_csv(f"{timestamp}.csv", index=False)

if __name__ == "__main__":
    # 爬取的URL
    url = 'https://bj.ke.com/ershoufang/rs/'

    try:
        # 生成时间戳
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

        # 爬取数据
        raw_data = scrape_data(url)

        # 保存原始数据
        save_raw_data(raw_data, timestamp)

        # 格式化数据
        formatted_data = format_data(raw_data)

        #   保存格式化数据
        save_formatted_data(formatted_data, timestamp)
    except Exception as e:
        print(f"An error occurred: {e}")

  • 导入库 导入必要的库,例如 firecrawlOpenAIdotenvosjsonpandas 和 datetime,以便实现爬取和数据处理所需的各种功能。
import firecrawl
import OpenAI
import dotenv
import os
import json
import pandas as pd
from datetime import datetime

  • 爬取数据 scrape_data() 函数利用你的 API 密钥初始化一个 FirecrawlApp,并使用 Firecrawl 爬取一个 URL。它检索网页的主要内容并以 Markdown 格式返回。
def scrape_data(api_key, url):
    app = firecrawl.FirecrawlApp(api_key)
    content = app.scrape(url)
    return content

  • 保存原始数据 save_raw_data() 函数将原始的 Markdown 数据保存到文件中,为便于识别,文件名附加时间戳。
def save_raw_data(data):
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    filename = f"raw_data_{timestamp}.md"
    with open(filename, 'w') as file:
        file.write(data)

  • 格式化数据 format_data() 函数利用 OpenAI 的 GPT 模型从原始数据中提取结构化信息。它构建系统和用户消息,提示模型从文本中提取指定字段。提取的数据以 JSON 格式返回。
def format_data(api_key, raw_data):
    openai.api_key = api_key
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=f"从文本中提取结构化数据:\n\n{raw_data}",
        max_tokens=1000
    )
    return json.loads(response.choices[0].text)

  • 保存格式化数据 save_formatted_data() 函数将格式化的数据以 JSON 格式保存到文件中,文件名附加时间戳。此外,它将 JSON 数据转换为 pandas DataFrame 并保存为 CSV 文件,以便进一步分析。
def save_formatted_data(data):
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    json_filename = f"formatted_data_{timestamp}.json"
    csv_filename = f"formatted_data_{timestamp}.csv"

    with open(json_filename, 'w') as json_file:
        json.dump(data, json_file)

    df = pd.DataFrame(data)
    df.to_csv(csv_filename, index=False)

  • 主函数 在主函数中,指定要爬取的 URL(url)。脚本然后尝试爬取数据、保存原始数据、格式化数据并保存格式化数据。如果在这些步骤中的任何一步发生错误,它将被捕获并显示。
if __name__ == "__main__":
    api_key = os.getenv("FIRECRAWL_API_KEY")
    url = "https://example.com"  # 替换为要爬取的实际 URL

    try:
        raw_data = scrape_data(api_key, url)
        save_raw_data(raw_data)

        formatted_data = format_data(api_key, raw_data)
        save_formatted_data(formatted_data)

    except Exception as e:
        print(f"An error occurred: {e}")

JSON 格式的输出

[
    {
        "名称": "南向一居室户型好,中间楼层,老少居住皆宜。",
        "地址": "二里庄北里",
        "价格": "345万",
        "链接": "https://bj.ke.com/ershoufang/101125066609.html"
    },
    {
        "名称": "三里河 正规2 居室 随时可看 随时签约",
        "地址": "三里河三区",
        "价格": "798万",
        "链接": "https://bj.ke.com/ershoufang/101125059884.html"
    },
    {
        "名称": "骊龙园 3室2厅 南 北",
        "地址": "骊龙园",
        "价格": "559万",
        "链接": "https://bj.ke.com/ershoufang/101125057747.html"
    },
    {
        "名称": "南三环草桥马家堡南向一居室商品房社区",
        "地址": "玺萌鹏苑",
        "价格": "270万",
        "链接": "https://bj.ke.com/ershoufang/101125055143.html"
    },
    {
        "名称": "2018年次新小区 19号线10号线机场线草桥站",
        "地址": "今日草桥",
        "价格": "710万",
        "链接": "https://bj.ke.com/ershoufang/101125049365.html"
    },
    {
        "名称": "裕华园一区 1室1厅 南 北",
        "地址": "裕华园一区",
        "价格": "71万",
        "链接": "https://bj.ke.com/ershoufang/101125059731.html"
    },
    {
        "名称": "化工大院 南北通透两居室 不临街 平改坡 停车方便",
        "地址": "化工大院",
        "价格": "620万",
        "链接": "https://bj.ke.com/ershoufang/101125053322.html"
    },
    {
        "名称": "北辰福第二号院 高楼层采光视野好 满五唯一 无抵押",
        "地址": "北辰福第二号院",
        "价格": "409万",
        "链接": "https://bj.ke.com/ershoufang/101125048788.html"
    },
    {
        "名称": "海特花园西区三居室,户型方正,南北通透",
        "地址": "海特花园西区",
        "价格": "445万",
        "链接": "https://bj.ke.com/ershoufang/101125045278.html"
    },
    {
        "名称": "中国铁建国际城 3室1厅 南 北",
        "地址": "中国铁建国际城",
        "价格": "680万",
        "链接": "https://bj.ke.com/ershoufang/101125045266.html"
    }
]

使用 Firecrawl,我们可以毫不费力地爬取整个网页,而无需担心复杂的细节。只需更改 URL,我们就可以轻松地将爬取工作适应不同的来源。尽管 Firecrawl 在处理动态内容和速率限制等爬取挑战方面表现出色,但仍然需要爬取网站上的所有页面,并导航到后续页面直到到达最后一个页面。这时,AgentQL 提供了一个解决方案。

AgentQL

AgentQL for Web 通过使用自然语言查询提供了一种革命性的方法来与网页元素交互。借助 AgentQL,用户可以轻松定位和交互网页元素,而无需复杂的代码或特定选择器。这种直观的界面简化了网页自动化的过程,使用户能够轻松高效地执行任务。无论是点击按钮、填写表单还是浏览页面,AgentQL for Web 都简化了交互过程,使网页自动化对各类用户都变得易于访问。

from dotenv import load_dotenv
import agentql
#from agentql.sync_api import ScrollDirection
import csv

load_dotenv()

PRODUCTS = """
{
    results{
        products[]{
            product_name
            product_price
            num_reviews
            rating
        }
    }
}
"""
NEXT_PAGE_BTN ="""
{
    next_page_button_enabled
    next_page_button_disabled
}
"""
session = agentql.start_session("https://s.taobao.com/search?page=1&q=iphone")

session.driver.scroll_to_bottom()

pagination = session.query(NEXT_PAGE_BTN)
print("get pagination")
print(pagination.next_page_button_enabled)

with open("products.csv", "a",newline="") as file:
    fieldnames = ["product_name","product_price","num_reviews","rating"]
    writer = csv.DictWriter(file,fieldnames=fieldnames)
    writer.writeheader()
    
    print(f"enabled button : {pagination.to_data()['next_page_button_enabled']}")
    print(f"disabled button : {pagination.to_data()['next_page_button_disabled']}")
    
    while(
        pagination.to_data()['next_page_button_enabled'] and
        pagination.to_data()['next_page_button_disabled'] is None
    ):
        products = session.query(PRODUCTS)
        print("scraped product data")
        print(products.to_data())
        
        for product in products.to_data()['results']['products']:
            print(f"product: {product}")
            writer.writerow(product)
        print("data written to csv")

  • 查询定义:定义了两个 GraphQL 查询:PRODUCTS 用于检索产品详细信息,而 NEXT_PAGE_BTN 用于检查分页的下一页按钮是否可用。
  • 会话初始化:使用 agentql.start_session() 与目标 URL("https://s.taobao.com/search?page=1&q=iphone")建立会话。
  • 滚动到底部:使用 session.driver.scroll_to_bottom() 将浏览器窗口滚动到页面底部。
  • 分页检查:使用 NEXT_PAGE_BTN 查询确定下一页按钮的状态(启用或禁用),结果存储在 pagination 变量中。
  • CSV 文件初始化:创建一个名为“products.csv”的 CSV 文件,并定义产品信息的字段名称。
  • 数据提取循环:在下一页按钮启用且未找到禁用按钮的情况下:使用 PRODUCTS 查询提取产品数据。每个产品的详细信息写入 CSV 文件。该过程持续进行,直到爬取完所有页面。

结论

ScrapeGraph、Firecrawl 和 AgentQL 代表了最新一代的网络爬虫框架。每个框架都有自己的优势,可以满足不同的爬取需求。 ScrapeGraph 利用 LLM 和直接图形逻辑实现多功能爬取管道,Firecrawl 擅长高效处理复杂的 Web 场景,AgentQL 引入自然语言交互以实现无缝 Web 元素操作。这些框架共同简化了从网络中提取有价值数据的过程,为开发人员提供了强大的工具来轻松处理爬取任务。

  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 很抱歉,我是AI语言模型,无法提供源码。但是,您可以在网上搜索相关的Python网络爬虫系统的源码,或者参考一些开源网络爬虫框架,如Scrapy、BeautifulSoup等。同时,也可以学习相关的Python网络爬虫知识,自己编写网络爬虫程序。 ### 回答2: 基于Python的网络爬虫系统设计与实现源码,可以借助多种Python库实现。下面介绍其中比较常用的库及其应用。 1. requests库: 用于网络请求,可发送HTTP/HTTPS请求,支持GET/POST等请求方式,并能够自动保存Cookie。 2. BeautifulSoup库: 用于HTML/XML解析,可解析HTML/XML文本内容并提取需要的数据。 3. lxml库:同样用于HTML/XML解析,性能更好,但使用稍微复杂一些。可以用来解决一些BeautifulSoup不能实现的功能,比如按Xpath表达式提取数据。 4. re库:正则表达式库,用于字符串匹配和替换。用来处理复杂的文本内容提取。 5. PyQuery库:是jQuery的Python实现,用于HTML/XML解析,可以像jQuery一样操作树形结构的HTML/XML文本。 6. Scrapy框架: Scrapy是一个强大的web爬虫框架。Scrapy集成了大量模块,支持页面抓取、数据解析、存储、异步I/O、自定义扩展等功能。基于Scrapy框架,可以轻松实现高效、可扩展的web爬虫系统。 另外,要实现网络爬虫系统,还需要清楚爬虫的工作原理和流程,设计好数据库表结构,确立好数据存储方式等。需要做好反扒防护,以免在爬虫过程中出现IP被封等情况。综合以上因素,可以选择不同的模块,设计出个性化的、高效稳定的基于Python的网络爬虫系统,提高数据爬取、分析和处理的效率。 ### 回答3: 目前,python已经成为最具影响力的编程语言之一,其中网络爬虫是python的一个热门应用领域之一。基于python的网络爬虫系统设计和实现,可以通过编写代码从互联网上抓取数据,获取有价值的信息。下面是一个基于python的网络爬虫系统设计与实现的源码。 一、设计概述 本网络爬虫系统的设计主要包含了以下几个方面: 1. 爬取目标:将网络爬虫的目标进行分类,并制定相应的信息获取策略。 2. 网络爬虫框架:本系统使用了Scrapy框架进行网络数据抓取。 3. 数据解析:本系统使用Python内置的正则表达式库re进行数据解析。 4. 数据存储:本系统使用Mongodb数据库存储获取的数据。 二、系统实现 1. 外部输入:用户输入爬取目标的链接网址。 2. 网络爬虫框架:本系统使用Scrapy框架进行网络爬取,使用Scrapy首先需要创建一个项目,使用命令创建一个Scrapy项目: scrapy startproject project_name 该命令将自动生成一个project_name目录,该目录下包含了Scrapy框架需要的各种文件。 3. 目标策略制定:该步骤需要对目标进行分类,对不同的目标制定不同的策略。例如,制定抓取规则、获取数据条件和错误判断方法。 4. 数据解析:本系统使用Python内置的正则表达式库re进行数据解析,用正则表达式匹配需要的数据。 5. 数据存储:本系统使用Mongodb数据库存储获取的数据,连同Mongodb模板库pymongo — v3.7.1。 三、源码说明 1.运行Scrapy: 首先,在项目目录下使用命令运行Scrapy: scrapy crawl spider_name 其中spider_name为自定义的爬虫名称。 2.制定爬虫: 在项目目录下创建一个spiders目录,并在其中创建一个.py文件,文件中需要继承scrapy.Spider父类,并重写start_requests和parse方法。 3.目标策略制定: 在setting.py文件中定义目标的网址和供应商信息,用于请求: start_urls = [ 'http://www.xxx.com/item', ] headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"} proxies = [ 'http://221.204.119.228:33993', 'http://14.29.124.12:8080', 'http://110.73.6.167:8123' ] 在spider.py文件中,制定xpath进行解析各种元素信息: def parse_product(self, response): loader = ItemLoader(item=Product(), response=response) loader.add_xpath('name', '//*[@id="productTitle"]/text()') loader.add_xpath('brand', '//*[@id="brand"]/text()') loader.add_xpath('price', '//*[@id="priceblock_ourprice"]/@src') loader.add_xpath('currency', '//*[@id="priceblock_ourprice"]/text()') loader.add_xpath('asin', '//*[@id="averageCustomerReviews"]/div[1]/a/@href') loader.add_xpath('category', '//*[@id="wayfinding-breadcrumbs_container"]/ul/li[position() > 1]/a/text()') loader.add_value('url', response.url) loader.add_value('useragent', response.request.headers['User-Agent']) yield loader.load_item() 在pipelines.py文件中,按指定格式存储结果数据: def process_item(self, item, spider): self.collection.insert(dict(item)) return item 四、总结 本文介绍了一个基于python的网络爬虫系统设计与实现,针对不同的目标进行分类和制定不同的策略,使用python内置的正则表达式库进行数据解析,使用Mongodb数据库存储获取的数据。整个网络爬虫系统的实现主要依托于Scrapy框架,通过编写代码实现从互联网上抓取数据的功能,提供了一种集中、高效的数据采集机制,为数据采集业务提供了一种全面的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liugddx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值