Scrapy vs. Requests:哪一个更适合进行网络爬虫?

对比 Scrapy 和 Requests,找出最适合您需求的网络爬虫工具。
5 min read
Scrapy vs Requests 博客图片

在本篇 Scrapy vs Requests 指南中,您将了解:

  • Scrapy 和 Requests 是什么
  • Scrapy 与 Requests 在网络爬虫方面的对比
  • Scrapy 与 Requests 在处理分页场景时的对比
  • Scrapy 和 Requests 在网络爬虫场景下常见的局限性

让我们开始吧!

什么是 Requests?

Requests 是一个用于发送 HTTP 请求的 Python 库。它在网络爬虫中被广泛使用,通常与类似 BeautifulSoup 这样的 HTML 解析库配合使用。

Requests 在网络爬虫中的主要特性包括:

  • 支持多种 HTTP 方法:您可以使用主要的 HTTP 方法,比如 GETPOSTPUTPATCHDELETE,以便与网页或 API 进行交互。
  • 自定义请求头:可以设置自定义请求头(例如 User-Agent)来模拟真实浏览器或处理简单的身份验证。
  • 会话管理:requests.Session() 对象可以在多次请求之间保留 cookies 和请求头。这对于需要登录或保持会话状态的网站爬虫非常有用。
  • 超时和错误处理:可以设置超时来避免请求挂起,并通过异常处理来使爬虫更加健壮。
  • 代理支持:可以通过代理发送请求,对于绕过 IP 封禁以及访问有地理限制的内容很有帮助。

什么是 Scrapy?

Scrapy 是一个使用 Python 编写的开源网络爬虫框架,能够快速、高效且可扩展地从网站获取数据。

Scrapy 提供了一个完整的框架,用于爬取网站、提取数据并将其存储为多种格式(例如 JSON、CSV 等)。如果您的需求是大规模网络爬虫项目,Scrapy 可以更方便地处理复杂的爬取任务和并发请求,同时还能够遵循爬取规则。

Scrapy 在网络爬虫中的主要特性包括:

  • 内置网页爬取功能:Scrapy 被设计成一个网页爬虫,它可以自动跟随网页中的链接,从而用极少的额外代码爬取多个页面或整个网站。
  • 异步请求:Scrapy 采用异步架构,可以同时处理多个请求。这比类似 Python HTTP 客户端 (如 requests) 更快。
  • 用于数据提取的选择器:Scrapy 提供了使用 XPath 和 CSS 选择器提取 HTML 中数据的能力。
  • 可定制化中间件:可使用中间件对请求和响应进行自定义处理。
  • 自动限速:可以自动根据服务器的响应时间和负载情况来调整爬虫请求速度。
  • 支持解析 robots.txt会遵守robots.txt 文件中的规则,确保您的爬虫行为符合目标网站的要求。
  • 代理与 User-Agent 轮换:Scrapy 支持通过中间件进行代理轮换User-Agent 轮换,避免出现 IP 封禁与被检测到的情况。

Scrapy vs Requests:网络爬虫特性对比

了解了什么是 Requests 和 Scrapy 后,接下来我们来深入比较两者在网络爬虫场景下的使用:

特性 Scrapy Requests
使用场景 大规模、复杂的爬虫项目 更简单的爬虫任务与原型
异步请求 原生支持异步请求 无内置支持
爬取功能 自动跟踪链接并爬取多个页面 需要手动实现爬取逻辑
数据提取 内置支持 XPath 和 CSS 选择器 需要外部库来实现数据提取
并发 可直接处理多并发请求 需要外部集成来管理并发请求
中间件 可针对代理、重试、请求头等进行定制化 无内置中间件
限速 内置自动限速功能,避免过度请求 无内置限速功能
代理轮换 可通过中间件轻松实现代理轮换 需要手动实现
错误处理 针对请求失败有内置重试机制 需要手动实现
文件下载 支持文件下载,但需要附加设置 文件下载简单直观

使用场景

Scrapy 更适合大规模、复杂的网络爬虫项目。它特别适合处理需要爬取多个页面、使用并发请求并且需要导出结构化数据的任务。

而 Requests 则是一个 HTTP 请求库,更适合处理一些简单事务,比如抓取单个网页、与 API 交互或下载文件。

异步请求与并发

Scrapy 基于 Twisted,这是一种事件驱动的 Python 网络框架。它可以处理异步和并发请求,使大规模爬虫运行速度更快。

Requests 本身并不支持异步或并发请求。如果需要并发请求,可以将其与 GRequests 集成。

爬取功能

ROBOTSTXT_OBEY 设置为 True 时,Scrapy 会自动读取 robots.txt 文件,自动跟随被允许的链接并爬取被允许的页面。

Requests 并没有内置的爬取功能,所以需要您手动指定想要抓取的链接并进行一次次请求。

数据提取

Scrapy 内置了 XPath 和 CSS 选择器,可以直接对 HTML 和 XML 进行数据解析。

Requests 并未包含解析功能,需要结合 BeautifulSoup 等外部库来进行数据提取。

中间件

Scrapy 提供了可定制的中间件机制,用于处理 代理重试请求头等高级功能,实现对爬虫逻辑的自定义扩展。

Requests 没有中间件机制,需要手动写逻辑来实现 代理轮换 或重试等功能。

限速

Scrapy 具有内置自动限速功能,可根据服务器加载情况自动调整抓取速度,避免对目标服务器造成压力。

Requests 没有内置的限速功能,如果要实现限速,需要在请求间手动添加延时,例如使用 time.sleep()

代理轮换

Scrapy 可通过中间件让代理轮换变得轻而易举,从而避免 IP 被封禁,并实现匿名爬取。

Requests 并不原生支持代理轮换,如果要使用,您需要自行配置代理并实现逻辑,可参照我们博客的相关指南

错误处理

Scrapy 内置了用于处理 失败请求的重试机制,可在网络或服务器出现问题时自动进行重试。

Requests 则需要用户手动处理错误和异常,例如通过 try-except 块,并可考虑使用 retry-requests 等库。

文件下载

Scrapy 可以通过 FilesPipeline 来下载文件,但需要额外配置来处理大型文件或流式下载。

Requests 可通过在 requests.get() 方法中设置 stream=True 参数来实现简单直接的文件下载。

Scrapy vs Requests:在分页场景下的对比

了解了 Requests 和 Scrapy 之后,接下来我们用具体的网络爬虫分页场景来进行对比。

在处理 网络爬虫分页时,需要定制逻辑来跟随页面链接并从多个页面提取数据。

本教程将以 Quotes to Scrape 这个网站为目标,它在不同页面上展示了名人名言:

“Quotes to Scrape”目标网站

我们的目标是使用 Scrapy 和 Requests 分别对该网站进行爬取并抓取所有页面的名言。我们先从 Requests 开始,也因为它在这一点上往往需要更多的代码实现。

环境需求

要复现下面使用 Scrapy 和 Requests 的教程,需要您在机器上安装 Python 3.7 或更高版本

如何使用 Requests 进行网络爬虫

本章节将演示如何使用 Requests 抓取目标站点(quotes.toscrape.com)上的所有名言。

请注意,单独使用 Requests 并不能直接解析网页内容。您还需要一个 HTML 解析器,比如 BeautifulSoup

第 1 步:设置环境和安装依赖

假设您的项目主文件夹命名为 requests_scraper/。完成此步骤后,文件夹结构如下:

requests_scraper/
    ├── requests_scraper.py
    └── venv/

其中:

  • requests_scraper.py 是存放所有脚本代码的 Python 文件
  • venv/ 为虚拟环境目录

您可以使用以下命令创建 venv/ 虚拟环境目录:

python -m venv venv

然后在 Windows 系统下通过以下命令激活该环境:

venv\Scripts\activate

在 macOS 和 Linux 系统下:

source venv/bin/activate

现在,您可以通过以下命令安装所需的库:

pip install requests beautifulsoup4

第 2 步:设置变量

现在,您可以开始在 requests_scraper.py 文件中编写代码。首先,定义如下变量:

base_url = "https://quotes.toscrape.com"
all_quotes = []

这里:

  • base_url 是要爬取的网站起始地址
  • all_quotes 用于存储所有抓取到的名言

第 3 步:编写爬取逻辑

接下来,您可以使用下面的代码来实现爬取与抓取功能:

url = base_url
while url:
    # Send a GET request to the current page
    response = requests.get(url)

    # Parse the HTML code of the page
    soup = BeautifulSoup(response.text, "html.parser")

    # Find all quote blocks
    quotes = soup.select(".quote")
    for quote in quotes:
        text = quote.select_one(".text").get_text(strip=True)
        author = quote.select_one(".author").get_text(strip=True)
        tags = [tag.get_text(strip=True) for tag in quote.select(".tag")]
        all_quotes.append({
            "text": text, 
            "author": author, 
            "tags": ",".join(tags)
        })

    # Check for the "Next" button
    next_button = soup.select_one("li.next")
    if next_button:
        # Extract the URL from the "Next" button and
        # set it as the next page to scrape
        next_page = next_button.select_one("a")["href"]
        url = base_url + next_page
    else:
        url = None

该代码:

  • 使用 while 循环持续抓取,直到所有页面都爬取完毕。
  • 在循环中:
    • soup.select() 找到页面上所有含有 quote 类的元素。
    • for 循环遍历这些元素,使用 BeautifulSoup 提供的解析方法提取名言内容、作者和标签。由于每条名言可能存在多个标签,因此需要对标签列表进行特殊处理。
      目标网页 HTML 代码中的 “quote” 类
  • 页面爬取完毕后,脚本会检测是否存在 next 按钮。如果有,提取指向下一页的链接并赋值给 url = base_url + next_page。当最后一页时,url 会变为 None,循环结束。
目标网页 HTML 代码中定义 “next” 按钮的 “next” 类
目标网页 HTML 代码中定义 “next” 按钮的 “next” 类

第 4 步:将数据写入 CSV 文件

爬取完成后,可以将数据写入 CSV 文件:

with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
writer.writeheader()
writer.writerows(all_quotes)

这里使用 Python 的 csv 标准库:

  • 指定输出文件名 quotes.csv
  • 将名言写入 CSV 文件(先写表头,再写入全部数据)

第 5 步:整合全部代码

以下是本章节中使用 Requests 和 BeautifulSoup 的完整示例:

import requests
from bs4 import BeautifulSoup
import csv

# URL of the website
base_url = "https://quotes.toscrape.com"
# List to store all quotes
all_quotes = []

# Start scraping from the first page
url = base_url
while url:
    # Send a GET request to the current page
    response = requests.get(url)

    # Parse the HTML code of the page
    soup = BeautifulSoup(response.text, "html.parser")

    # Find all quote blocks
    quotes = soup.select(".quote")
    for quote in quotes:
        text = quote.select_one(".text").get_text(strip=True)
        author = quote.select_one(".author").get_text(strip=True)
        tags = [tag.get_text(strip=True) for tag in quote.select(".tag")]
        all_quotes.append({
            "text": text, 
            "author": author, 
            "tags": ",".join(tags)
        })

    # Check for the "Next" button
    next_button = soup.select_one("li.next")
    if next_button:
        # Extract the URL from the "Next" button and
        # set it as the next page to scrape
        next_page = next_button.select_one("a")["href"]
        url = base_url + next_page
    else:
        url = None

# Save the quotes to a CSV file
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
    writer.writeheader()
    writer.writerows(all_quotes)

运行以上脚本:

python requests_scraper.py

您将会在项目目录下看到一个名为 quotes.csv 的文件:

使用 Requests 和 BeautifulSoup 提取数据后获得的预期 CSV 文件

如何使用 Scrapy 进行网络爬虫

了解了如何使用 Requests 进行网络爬虫后,现在我们来看看如何使用 Scrapy 达到相同的效果,爬取相同的网站并实现相同目标。

第 1 步:设置环境并安装依赖

假设您的项目主文件夹名为 scrapy_scraper/

首先,创建并激活一个虚拟环境(方法同上),然后安装 Scrapy:

pip install scrapy

接着,在 scrapy_scraper/ 目录下运行:

scrapy startproject quotes_scraper

这会在主文件夹内生成一些预定义好的文件,并将其放在 quotes_scraper/ 文件夹下:

scrapy_scraper/ 
├── quotes_scraper/ # Main Scrapy project folder
│   ├── __init__.py  
│   ├── items.py # Defines the data structure for scraped items 
│   ├── middlewares.py # Custom middlewares
│   ├── pipelines.py # Handles post-processing of scraped data 
│   ├── settings.py # Project settings 
│   └── spiders/ # Folder for all spiders  
├── venv/ 
└── scrapy.cfg # Scrapy configuration file

第 2 步:定义 Items

items.py 文件用来定义需要抓取的数据结构。由于我们需要获得名言、作者以及标签,因此可将其定义为:

import scrapy

class QuotesScraperItem(scrapy.Item):
    quote = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

第 3 步:定义主爬虫(Spider)

spiders/ 目录下创建以下 Python 文件:

  • __init__.py(将该目录标记为 Python 包)
  • quotes_spider.py

下面是 quotes_spider.py 文件示例:

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

class QuotesSpider(CrawlSpider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ["https://quotes.toscrape.com/"]

    # Define rules for following pagination links
    rules = (
        Rule(LinkExtractor(restrict_css="li.next a"), callback="parse_item", follow=True),
    )

    def parse_item(self, response):
        # Extract quotes, authors, and tags
        for quote in response.css("div.quote"):
            item = QuotesScraperItem()
            item["quote"] = quote.css("span.text::text").get()
            item["author"] = quote.css("small.author::text").get()
            item["tags"] = quote.css("div.tags a.tag::text").getall()
            yield item

上述代码中:

  • 定义了起始 URL。
  • 通过 Rule() 类定义了抓取分页链接的规则,使得爬虫自动跟随下一页。
  • 通过 parse_item() 方法提取名言、作者以及标签。

第 4 步:配置 Settings

Scrapy 要导出为 CSV,需要一些额外配置。打开 settings.py 文件,添加如下变量:

FEED_FORMAT = "csv"  
FEED_URI = "quotes.csv" 

其中:

第 5 步:运行爬虫

本教程未涉及到的其他文件可以使用 Scrapy 的默认内容。进入 quotes_scraper/ 文件夹:

cd quotes_scraper

随后,运行:

scrapy crawl quotes

这条命令会实例化 quotes_spider.py 文件中定义的 QuotesSpider() 类并启动爬虫。最终得出的 quotes.csv 文件与我们使用 Requests 和 BeautifulSoup 时获得的结果相同。

从这个示例可以看出:

  • Scrapy 更适合大规模项目,因为它天生就适合此类场景。
  • 针对分页,Scrapy 仅需要定义一个规则来处理,而 Requests 则需要手动写逻辑。
  • 将数据输出到 CSV 在 Scrapy 中更加简单,因为只需要简单的配置,而无需手动编写写 CSV 的逻辑。

Scrapy 和 Requests 的常见局限性

尽管 Scrapy 和 Requests 在网络爬虫中被广泛使用,但两者都存在一些局限性。

其中一个主要局限性是任何爬虫框架都可能会遇到的 IP 封禁 问题。虽然 Scrapy 附带了限速功能,能够根据服务器负载自动调整请求速率,但在很多情况下这并不足以避免 IP 被封禁。

要想避免 IP 封禁,最有效的方法之一是结合代理使用。让我们看看怎么做。

在 Requests 中使用代理

如果只想为 Requests 设置单个代理,可以使用下面的示例:

proxy = {
     "http": "<HTTP_PROXY_URL>",
     "https": "<HTTPs_PROXY_URL>"
 }
 response = requests.get(url, proxies=proxy)

如果想了解如何在 requests 中使用代理和 IP 轮换,可以参考我们的博客:

在 Scrapy 中使用代理

如果想设置单个代理,可以在 settings.py 文件中添加以下配置:

# Configure a single proxy
HTTP_PROXY = "<PROXY_URL>"

# Enable the HttpProxyMiddleware and disable the default UserAgentMiddleware
DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

这些配置会让所有请求都通过所指定的代理。更多详情可查看 我们关于 Scrapy 使用代理的集成指南

如果您想使用可自动轮换的代理,可以利用 scrapy-rotating-proxies 或者直接使用自动轮换住宅代理服务。

如果您在寻找可靠的代理,请注意 Bright Data 的代理网络深受财富 500 强企业和超过 20,000 位客户的信赖。其中包括:

结论

在本篇 Scrapy vs Requests 的博文中,您了解了这两种库在网络爬虫中的角色,探讨了它们在页面获取、数据提取方面的功能,并在真实的分页场景中进行了对比。

Requests 虽然需要更多的手动逻辑,但在某些定制化场景下可能有更大的灵活性;而 Scrapy 则提供了更完整的爬虫功能,非常适用于结构化的大规模爬虫项目。

您还了解到它们都存在潜在的局限性,例如 IP 封禁 或地理限制。不过,可以通过 使用代理 或者利用 Bright Data 的 Web Scrapers 解决方案来绕过这些限制。

Bright Data 的 Web Scrapers 能与 Scrapy 和 Requests 无缝集成,让您能够从各大网站获取公开数据,不受限制。

现在就注册一个免费的 Bright Data 账号,查看我们的代理和爬虫 API,并开始您的免费试用吧!