在本篇 Scrapy vs Requests 指南中,您将了解:
- Scrapy 和 Requests 是什么
- Scrapy 与 Requests 在网络爬虫方面的对比
- Scrapy 与 Requests 在处理分页场景时的对比
- Scrapy 和 Requests 在网络爬虫场景下常见的局限性
让我们开始吧!
什么是 Requests?
Requests 是一个用于发送 HTTP 请求的 Python 库。它在网络爬虫中被广泛使用,通常与类似 BeautifulSoup 这样的 HTML 解析库配合使用。
Requests 在网络爬虫中的主要特性包括:
- 支持多种 HTTP 方法:您可以使用主要的 HTTP 方法,比如
GET
、POST
、PUT
、PATCH
和DELETE
,以便与网页或 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 这个网站为目标,它在不同页面上展示了名人名言:
我们的目标是使用 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 提供的解析方法提取名言内容、作者和标签。由于每条名言可能存在多个标签,因此需要对标签列表进行特殊处理。
- 页面爬取完毕后,脚本会检测是否存在
next
按钮。如果有,提取指向下一页的链接并赋值给url = base_url + next_page
。当最后一页时,url
会变为None
,循环结束。
第 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
的文件:
如何使用 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"
其中:
FEED_FORMAT
指定了输出文件格式(Scrapy 支持多种格式)FEED_URI
指定了输出文件名
第 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 位客户的信赖。其中包括:
- 住宅代理:超过 7200 万住宅 IP,覆盖 195+ 个国家/地区。
- 数据中心代理:超过 770,000 个数据中心 IP。
- ISP 代理:超过 700,000 个 ISP IP。
- 移动代理:超过 700 万移动 IP。
结论
在本篇 Scrapy vs Requests 的博文中,您了解了这两种库在网络爬虫中的角色,探讨了它们在页面获取、数据提取方面的功能,并在真实的分页场景中进行了对比。
Requests 虽然需要更多的手动逻辑,但在某些定制化场景下可能有更大的灵活性;而 Scrapy 则提供了更完整的爬虫功能,非常适用于结构化的大规模爬虫项目。
您还了解到它们都存在潜在的局限性,例如 IP 封禁 或地理限制。不过,可以通过 使用代理 或者利用 Bright Data 的 Web Scrapers 解决方案来绕过这些限制。
Bright Data 的 Web Scrapers 能与 Scrapy 和 Requests 无缝集成,让您能够从各大网站获取公开数据,不受限制。
现在就注册一个免费的 Bright Data 账号,查看我们的代理和爬虫 API,并开始您的免费试用吧!