百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Python网络爬虫框架的总结(python爬虫常用框架)

ccwgpt 2024-11-07 09:47 40 浏览 0 评论

前言

虽然使用reqeuests和bs4可以处理网站数据获取但是当你要爬去大批量的网页时,单独的使用reqeuests就显得力不从心了。因为这是两个层面的事情,解决的问题是不一样的。

网络爬虫

网络爬虫是一种强大的技术,通过查找一个或多个域名的所有 URL 来从 Web 网站收集需要的数据。Python 有一些流行的网络爬虫库和框架。

介绍简单的例子,使用两个库在 Python 中从头开始构建一个简单的网络爬虫:RequestsBeautiful Soup。接着使用 Scrapy 构建一个示例爬虫,从 IMDb 收集电影元数据,并查看 Scrapy 如何扩展到拥有数百万页面的网站。

什么是网络爬虫?

网络爬虫和网络抓取是两个不同但相关的概念。网页爬虫是网页抓取的一个组成部分,爬虫逻辑查找要由抓取工具代码处理的 URL。

网络爬虫从要访问的 URL 列表开始,称为种子。对于每个 URL,爬网程序会在 HTML 中查找链接,根据某些条件过滤这些链接,并将新链接添加到队列中。所有 HTML 或某些特定信息都被提取出来,由不同的管道进行处理。

其实简单的殂就是我们打开淘宝首页,有很多个链接可以点击,打开每个链接又会进入不同的链接,这些子链接也有更多的链接。爬虫可以处理这个形成蜘蛛网一样的链接。

网络爬虫策略

网络爬虫只会访问一部分网页,具体取决于爬虫预算,该预算可以是每个域的最大网页数、深度或执行时间。

许多网站都提供了一个机器人.txt文件,以指示可以抓取网站的哪些路径,哪些是禁止的。还有sitemap.xml,它比robots.txt更明确一些,专门指示机器人应该抓取哪些路径,并为每个URL提供额外的元数据。

流行的网络爬虫用例包括:

  • 搜索引擎(例如 Googlebot、Bingbot、Yandex Bot 等)收集了 Web 重要部分的所有 HTML。此数据已编制索引,使其可搜索。
  • 除了收集 HTML 之外,SEO 分析工具还收集元数据,例如响应时间、检测损坏页面的响应状态以及收集反向链接的不同域之间的链接。
  • 价格监控工具抓取电子商务网站以查找产品页面并提取元数据,尤其是价格。然后定期重新访问产品页面。
  • Common Crawl 维护一个开放的 Web 爬虫数据存储库。例如,2022 年 5 月的档案包含 34.5 亿个网页。


从头开始在 Python 中构建一个简单的网络爬虫

要在 Python 中构建一个简单的网络爬虫,至少需要一个库来从 URL 下载 HTML,另一个库来提取链接。Python 提供了用于执行 HTTP 请求的标准库 urllib 和用于解析 HTML 的 html.parser。可以在 Github 上找到一个仅使用标准库构建的示例 Python 爬虫。

还有其他流行的库,例如 RequestsBeautiful Soup,它们可以在编写 HTTP 请求和处理 HTML 文档时提供改进的开发人员体验。如果您想了解更多信息,可以查看有关最佳 Python HTTP 客户端的指南。

本地安装这两个库。

pip install requests bs4


import logging
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup

logging.basicConfig(
    format='%(asctime)s %(levelname)s:%(message)s',
    level=logging.INFO)

class Crawler:

    def __init__(self, urls=[]):
        self.visited_urls = []
        self.urls_to_visit = urls

    def download_url(self, url):
        return requests.get(url).text

    def get_linked_urls(self, url, html):
        soup = BeautifulSoup(html, 'html.parser')
        for link in soup.find_all('a'):
            path = link.get('href')
            if path and path.startswith('/'):
                path = urljoin(url, path)
            yield path

    def add_url_to_visit(self, url):
        if url not in self.visited_urls and url not in self.urls_to_visit:
            self.urls_to_visit.append(url)

    def crawl(self, url):
        html = self.download_url(url)
        for url in self.get_linked_urls(url, html):
            self.add_url_to_visit(url)

    def run(self):
        while self.urls_to_visit:
            url = self.urls_to_visit.pop(0)
            logging.info(f'Crawling: {url}')
            try:
                self.crawl(url)
            except Exception:
                logging.exception(f'Failed to crawl: {url}')
            finally:
                self.visited_urls.append(url)

if __name__ == '__main__':
    Crawler(urls=['https://www.imdb.com/']).run()

(使用 Requests 库、使用 Beautiful Soup 库和过滤 URL),然后继续使用我们的 IMDb 起始 URL 实例化该类并调用其方法。Crawlerdownload_urlget_linked_urlsadd_url_to_visitrun()

只要 中存在待处理的 URL,就会运行,会将每个 URL 传递给 ,提取任何链接,并将它们添加到 - 冲洗并重复。runurls_to_visitcrawl()urls_to_visit

要运行我们的爬虫,只需在命令行中输入此命令即可。

python crawler.py

爬网程序为每个访问过的 URL 记录一行。

INFO:Crawling: https://www.imdb.com/
INFO:Crawling: https://www.imdb.com/?ref_=nv_home
INFO:Crawling: https://www.imdb.com/calendar/?ref_=nv_mv_cal
INFO:Crawling: https://www.imdb.com/list/ls016522954/?ref_=nv_tvv_dvd
INFO:Crawling: https://www.imdb.com/chart/top/?ref_=nv_mv_250
INFO:Crawling: https://www.imdb.com/chart/moviemeter/?ref_=nv_mv_mpm
INFO:Crawling: https://www.imdb.com/feature/genre/?ref_=nv_ch_gr

代码非常简单,但在成功抓取完整网站之前,有许多性能和可用性问题需要解决。

  • 爬网程序速度较慢,不支持并行性。从时间戳可以看出,抓取每个 URL 大约需要一秒钟。每次爬网程序发出请求时,它都会等待响应,不会执行太多其他操作。
  • 下载 URL 逻辑没有重试机制,URL 队列不是真正的队列,并且对于大量 URL 效率不高。
  • 链接提取逻辑不支持通过删除 URL 查询字符串参数来标准化 URL,不处理相对锚点/片段 URL(即 ),也不支持按域过滤 URL 或过滤掉对静态文件的请求。href="#myanchor"
  • 抓取工具不会识别自己,并忽略漫游器 .txt 文件。


使用 Scrapy 进行网络爬虫

Scrapy 是最受欢迎的网络抓取和抓取 Python 框架,。

Scrapy 具有多组件架构。通常实现至少两个不同的类:Spider 和 Pipeline。Web 抓取可以被认为是一种 ETL,在其中从 Web 中提取数据并将其加载到存储中。蜘蛛提取数据,管道将其加载到存储中。转换可以发生在爬虫和管道中,

可以在组件之间添加爬虫和下载器中间件,如下图所示。



Scrapy 架构概述 [源代码]

from scrapy.spiders import Spider

class ImdbSpider(Spider):
    name = 'imdb'
    allowed_domains = ['www.imdb.com']
    start_urls = ['https://www.imdb.com/']

    def parse(self, response):
        pass

Scrapy 还提供了几个通用的爬虫类:CrawlSpider、XMLFeedSpider、CSVFeedSpider 和 SitemapSpider。CrawlSpider 类继承自 Spider 基类,并提供额外的 rules 属性来定义如何抓取网站。每个规则都使用 LinkExtractor 来指定从每个页面中提取哪些链接。接下来,我们将通过为 IMDb(互联网电影数据库)构建爬虫来了解如何使用它们中的每一个。

为 IMDb 构建一个示例 Scrapy 爬虫

在尝试抓取 IMDb 之前,检查了 IMDb 机器人 .txt 文件,看看允许哪些 URL 路径。robots 文件仅禁止所有用户代理的 26 个路径。Scrapy 会事先读取 robot.txt 文件,并在 ROBOTSTXT_OBEY 设置为 时遵守该文件。使用 Scrapy 命令生成的所有项目都是这种情况。truestartproject

scrapy startproject scrapy_crawler

此命令使用默认的 Scrapy 项目文件夹结构创建一个新项目。

scrapy_crawler/

├── scrapy.cfg
└── scrapy_crawler
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        ├── __init__.py

然后可以使用规则创建一个蜘蛛来提取所有链接。scrapy_crawler/spiders/imdb.py

from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class ImdbCrawler(CrawlSpider):
    name = 'imdb'
    allowed_domains = ['www.imdb.com']
    start_urls = ['https://www.imdb.com/']
    rules = (Rule(LinkExtractor()),)

现在,只需使用命令启动爬虫scrapy

scrapy crawl imdb --logfile imdb.log

将获得大量日志,包括每个请求的一个日志。在浏览日志时,我注意到,即使我们设置为仅抓取 https://www.imdb.com 下的网页,也会有对外部域(例如 amazon.com)的请求。allowed_domains


[scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.amazon.com/b/?&node=5160028011&ref_=ft_iba> from <GET [https://www.imdb.com/whitelist-offsite?url=https%3A%2F%2Fwww.amazon.com%2Fb%2F%3F%26node%3D5160028011%26ref_%3Dft_iba&page-action=ft-iba&ref=ft_iba](https://www.imdb.com/whitelist-offsite?url=https%3A%2F%2Fwww.amazon.com%2Fb%2F%3F%26node%3D5160028011%26ref_%3Dft_iba&page-action=ft-iba&ref=ft_iba)>

IMDb 将外部域下的路径重定向到外部域。有一个开放的 Scrapy Github 问题,该问题显示外部 URL 在应用之前不会被过滤掉。为了解决这个问题,可以将链接提取器配置为跳过以两个正则表达式开头的 URL。/whitelist-offsite/whitelistOffsiteMiddlewareRedirectMiddleware

class ImdbCrawler(CrawlSpider):
    name = 'imdb'
    allowed_domains = ['www.imdb.com']
    start_urls = ['https://www.imdb.com/']
    rules = (
        Rule(LinkExtractor(
            deny=[
                re.escape('https://www.imdb.com/offsite'),
                re.escape('https://www.imdb.com/whitelist-offsite'),
            ],
        )),
    )

Rule类支持多个参数来过滤 URL。例如可以忽略特定的文件扩展名,并通过对查询字符串进行排序或折叠来减少重复 URL 的数量。LinkExtractor

如果找不到用例的特定参数,则可以使用 LinkExtractor 或 Rule 的参数。例如两次获得相同的页面,一次是纯 URL,另一次是附加查询字符串参数。process_valueprocess_links

  • https://www.imdb.com/name/nm1156914/
  • https://www.imdb.com/name/nm1156914/?mode=desktop&ref_=m_ft_dsk

为了限制抓取的 URL 的数量,可以使用 w3lib 库中的 url_query_cleaner 函数从 URL 中删除所有查询字符串,并在 .process_links

from w3lib.url import url_query_cleaner

def process_links(links):
    for link in links:
        link.url = url_query_cleaner(link.url)
        yield link

class ImdbCrawler(CrawlSpider):

    name = 'imdb'
    allowed_domains = ['www.imdb.com']
    start_urls = ['https://www.imdb.com/']
    rules = (
        Rule(LinkExtractor(
            deny=[
                re.escape('https://www.imdb.com/offsite'),
                re.escape('https://www.imdb.com/whitelist-offsite'),
            ],
        ), process_links=process_links),
    )

现在已经限制了要处理的请求数,可以添加一种方法来从每个页面中提取数据并将其传递到管道进行存储。例如可以在不同的管道中对其进行处理,也可以选择 HTML 元数据。parse_itemresponse.text

要在标头标签中选择 HTML 元数据,可以指定自己的 XPath 表达式,最好使用库 extract 从 HTML 页面中提取所有元数据。

可以使用 .pip install extruct

import re
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from w3lib.url import url_query_cleaner
import extruct

def process_links(links):
    for link in links:
        link.url = url_query_cleaner(link.url)
        yield link

class ImdbCrawler(CrawlSpider):
    name = 'imdb'
    allowed_domains = ['www.imdb.com']
    start_urls = ['https://www.imdb.com/']
    rules = (
        Rule(
            LinkExtractor(
                deny=[
                    re.escape('https://www.imdb.com/offsite'),
                    re.escape('https://www.imdb.com/whitelist-offsite'),
                ],
            ),
            process_links=process_links,
            callback='parse_item',
            follow=True
        ),
    )

    def parse_item(self, response):
        return {
            'url': response.url,
            'metadata': extruct.extract(
                response.text,
                response.url,
                syntaxes=['opengraph', 'json-ld']
            ),
        }

该属性设置为,以便 Scrapy 仍然遵循每个响应的所有链接,配置了 extruct 以仅提取 Open Graph 元数据和 JSON-LD,这是一种在 Web 中使用 JSON 对链接数据进行编码的流行方法,由 IMDb 使用。可以运行爬网程序并将项目以 JSON 行格式存储到文件中。followTrue

scrapy crawl imdb --logfile imdb.log -o imdb.jl -t jsonlines

输出文件包含每个已爬网项的一行。例如,对于从 HTML 中的标记中获取的电影,提取的 Open Graph 元数据如下所示。imdb.jl<meta>


{
    "url": "http://www.imdb.com/title/tt2442560/",
    "metadata": {"opengraph": [{
         "namespace": {"og": "http://ogp.me/ns#"},
         "properties": [
             ["og:url", "http://www.imdb.com/title/tt2442560/"],
             ["og:image", "https://m.media-amazon.com/images/M/MV5BMTkzNjEzMDEzMF5BMl5BanBnXkFtZTgwMDI0MjE4MjE@._V1_UY1200_CR90,0,630,1200_AL_.jpg"],
             ["og:type", "video.tv_show"],
             ["og:title", "Peaky Blinders (TV Series 2013\u2013 ) - IMDb"],
             ["og:site_name", "IMDb"],
             ["og:description", "Created by Steven Knight.  With Cillian Murphy, Paul Anderson, Helen McCrory, Sophie Rundle. A gangster family epic set in 1900s England, centering on a gang who sew razor blades in the peaks of their caps, and their fierce boss Tommy Shelby."]
        ]
   }]}
}

单个项目的 JSON-LD 太长,无法包含在文章中,以下是 Scrapy 从标签中提取的内容的示例。<script type="application/ld+json">

"json-ld": [
    {
        "@context": "http://schema.org",
        "@type": "TVSeries",
        "url": "/title/tt2442560/",
        "name": "Peaky Blinders",
        "image": "https://m.media-amazon.com/images/M/MV5BMTkzNjEzMDEzMF5BMl5BanBnXkFtZTgwMDI0MjE4MjE@._V1_.jpg",
        "genre": ["Crime","Drama"],
        "contentRating": "TV-MA",
        "actor": [
            {
                "@type": "Person",
                "url": "/name/nm0614165/",
                "name": "Cillian Murphy"
            },
            ...
        ]
        ...
    }
]

通过按顺序点击过滤器,爬虫会生成内容相同的网址,只是过滤器的应用顺序不同。

  • https://www.imdb.com/name/nm2900465/videogallery/ content_type-拖车/related_titles-TT0479468
  • https://www.imdb.com/name/nm2900465/videogallery/ related_titles-TT0479468/content_type-拖车

长过滤器和搜索 URL 是一个难题,可以通过使用 Scrapy 设置限制 URL 的长度来部分解决,URLLENGTH_LIMIT。

以 IMDb 为例来展示在 Python 中构建网络爬虫的基础知识。如果需要来自 IMDb 的特定数据,可以查看提供每日 IMDb 数据导出的 IMDb 数据集项目或 Cinemagoer,

大规模 Web 爬虫

如果尝试抓取像 IMDb 这样拥有超过 1.3 亿个页面的大型网站(至少根据 Google 的数据),请务必通过调整您的爬虫并相应地调整其设置来负责任地抓取。

  1. USER_AGENT - 允许您指定用户代理并提供可能的联系方式
  2. DOWNLOAD_DELAY - 指定爬网程序在请求之间应等待的秒数
  3. CONCURRENT_REQUESTS_PER_DOMAIN - 指示爬网程序应向一个站点发送的最大并发请求数
  4. AUTOTHROTTLE_ENABLED - 启用自动和动态请求限制

请注意,默认情况下,Scrapy 爬网针对单个域进行了优化。如果要对多个网域进行爬网,请检查这些设置以针对广泛爬网进行优化,包括将默认爬网顺序从深度优先更改为呼吸优先。要限制抓取预算,可以使用关闭爬虫扩展程序的CLOSESPIDER_PAGECOUNT设置来限制请求数。

使用默认设置,Scrapy 每分钟为 IMDb 等网站抓取约 600 个页面。以这种速度,用一个机器人抓取 1.3 亿个页面大约需要半年时间。如果您需要抓取多个网站,最好为每个大型网站或网站组启动单独的爬虫。如果您对分布式 Web 爬网感兴趣,可以阅读开发人员如何使用 20 个 Amazon EC2 机器实例在不到两天的时间内使用 Python 抓取 250M 个页面。

在某些情况下,可能会遇到需要您执行 JavaScript 代码来呈现所有 HTML 的网站。否则,可能无法收集网站上的所有链接。因为现在网站在浏览器中动态渲染内容非常普遍,所以我编写了一个 Scrapy 中间件,用于使用 ScrapingBee 的 API 渲染 JavaScript 页面。

这一点主要是解决这种使用JavaScript动态生成HTML页面的情况。

结论

  • HTML是传统爬虫的关键
  • 新的WEB开发技术使用JavaScript来生成页面,实际爬虫任务比这个复杂
  • requests bs4 是基础的库,
  • scrapy是一个爬虫框架。

相关推荐

一个基于.Net Core遵循Clean Architecture原则开源架构

今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...

AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%

写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...

OneCode低代码平台的事件驱动设计:架构解析与实践

引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...

国内大厂AI插件评测:根据UI图生成Vue前端代码

在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...

AI+低代码技术揭秘(二):核心架构

本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...

GitDiagram用AI把代码库变成可视化架构图

这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...

30天自制操作系统:第六天:代码架构整理与中断处理

1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...

AI写代码越帮越忙?2025年研究揭露惊人真相

近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...

一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具

一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...

5分钟掌握 c# 网络通讯架构及代码示例

以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...

从复杂到优雅:用建造者和责任链重塑代码架构

引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...

低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈

专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...

框架设计并不是简单粗暴地写代码,而是要先弄清逻辑

3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...

大佬用 Avalonia 框架开发的 C# 代码 IDE

AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...

轻量级框架Lagent 仅需20行代码即可构建自己的智能代理

站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...

取消回复欢迎 发表评论: