跳到主要内容
返回博客

Scrapy Python 终极指南:网络数据抓取与代理集成

-
Blog The Ultimate Scrapy Python Guide cover
目录
-

大规模网络数据抓取是一项具有挑战性的任务,而借助 Scrapy 这类专用工具可以大大简化这一过程。Scrapy 是一种开源 Python 框架,目前是采集在线数据较为常用的方案之一。它的异步架构保证了速度,还可以通过中间件进行自定义配置。

采集大量信息往往需要一层在线隐私保护。在本篇 Scrapy 教程中,我们将演示它如何与代理及其他反检测工具(如用户代理轮换)集成。如果您刚开始学习网络数据抓取,欢迎访问我们的Python 网络数据抓取指南,因为 Scrapy 框架正是用这门编程语言构建的。

接下来,让我们看看如何启动您的 Scrapy 项目,从环境搭建到排查最常见的问题。

搭建 Scrapy Python 环境

首先,您需要搭建开发环境,第一步是安装 Python,可以从官方网站下载。本教程以 Windows 操作系统为例,使用命令提示符执行命令。目前 Windows 版 Python 的最新版本是 3.13.3。

安装时,别忘了勾选“Add python.exe to PATH”复选框,以便在命令提示符中执行其命令,如下图所示。

IMG1.png

然后,打开命令提示符并输入

pip install scrapy

稍等片刻,直到命令提示符显示所有软件包均已成功安装。

现在,您可以使用以下命令启动 Scrapy 项目:

startproject project_name

其中 project_name 是您为项目取的名称,我们将其命名为 ScrapyTutorial。如果执行成功,命令提示符会显示启动第一个 spider(不要与网络爬取混淆,后者有时也称为 spidering)的命令,spider 是一个定义数据抓取规则的 Python 类。

此时,Scrapy 已创建了如下目录结构。

IMG2.png

scrapy.cfg 文件保存项目设置,采集数据的结构在 items.py 中定义,而 pipelines.py 控制采集到的信息如何处理。spiders 目录存放所有与 spider 相关的文件。这种有条理的 Scrapy 项目结构使得浏览和修改项目变得尤为简单。

现在,您可以生成第一个 spider。首先,在命令提示符中输入以下命令进入项目目录:

cd project_name

使用您之前取的项目名称,然后执行

scrapy genspider SpiderName Example.com

命令,其中 SpiderName 是您为 spider 取的名称,后面跟上要抓取的网站。请确保 spider 名称与项目名称不同。

在本例中,我们创建了一个名为 Mars 的 Scrapy 项目 spider,用于抓取我们的网站。

IMG3.png

目前命令提示符的操作暂告一段落,但先不要关闭它,稍后还会用到。此时,它已在 spiders 目录下创建了一个名为“Mars”的新文件,在本例中该目录位于

'C:\Users\Username\ScrapyTutorial\ScrapyTutorial\spiders'
。

进入项目目录并打开该文件,但请注意,用 Python 启动器打开是行不通的,因为您需要在 Scrapy 框架下工作。您可以用简单的 Windows 记事本打开该文件,但使用专用 IDE(集成开发环境)效果更好。我们将使用微软的 Visual Studio Code。

右键点击您的 spider 文件,选择“打开方式”,然后选择您的 IDE 或简单记事本。效果如下:

IMG4.png

这是一个非常简单的示例。它定义了我们将使用 Scrapy spider 抓取“marsproxies.com”网站,并且这是唯一允许抓取的域名。定义“allowed_domains”十分重要,可以防止爬虫误抓取非预期数据。

最后,parse(self, response) 是一个回调函数,Scrapy 会在完成前面步骤后执行它。由于我们尚未定义任何具体规则,它将采集整个网站的完整 HTML 文档。

使用 Scrapy 提取数据

接下来,我们将展示如何通过 CSS 选择器指定解析规则来抓取数据,这正是网络爬取与网络数据抓取真正发挥作用的地方。在您的 IDE 中,您将编写更多代码,定义 Scrapy 项目采集信息的内容、方式与位置。请记住,这里并不是用 Python 直接执行代码,而是通过命令提示符使用 Scrapy 框架的命令行界面(CLI)。

让我们用 Scrapy 的 crawl 函数执行这个基础 spider。回到命令提示符,确保目录仍处于 Scrapy 项目下(几步前已通过“cd project_name”命令设置)。然后输入

scrapy crawl SpiderName

并输入您几步前创建的 spider 名称。

执行该命令后,您会看到 Scrapy 执行的一长串操作记录,最后以“INFO: Spider closed (finished)”结束,这表示已成功抓取目标数据。不过请注意,命令提示符并未显示任何实际内容,这是因为 parse 回调函数末尾有一条“pass”语句。在您的 IDE 中,在“def parse(self, response)”后添加以下代码行:

  print(response.body.decode('utf-8'))

最后一行指示 Scrapy 显示 HTML 内容,并使用 UTF-8 标准将数据从字节(即二进制 0 和 1 的值)解码为人类可读的文本。结果如下图所示。

IMG5.png

完成初始流程后,可以看到命令提示符显示了 <!DOCTYPE html> 以及网站的实际内容。由于这些数据未经解析,会显示 HTML 文档的全部复杂结构。接下来,我们来看看如何通过 CSS 选择器进行精确筛选。

使用 Python 进行网络数据抓取时,您经常需要借助开发者工具来定位目标数据。在浏览器中打开网站后,按下 ctrl + shift + I,或右键点击网站页面并选择“检查”,即可打开开发者工具并查看网站的 HTML 代码。

此时,具备一定的 JavaScript 编程知识会很有帮助,因为它是目前最常用的网站开发编程语言之一。您也可以先熟悉基本的 HTML 和 CSS 结构,但很可能很快就会遇到更复杂的 JavaScript 函数。

接下来,我们将展示如何使用 CSS(层叠样式表)选择器从我们的首页提取价格信息。

IMG6.png

我们右键点击并检查了价格元素,右侧随即显示出 HTML 代码。可以看到,价格信息存放在 HTML 的段落 <p> 元素中,带有“tp-headline-m”和“text-neutral-0”这两个类名。

<p class="tp-headline-m text-neutral-0">$0.22</p>

在 HTML 代码中查找这些类名后,我们发现这些类名被所有价格元素共用,因此可以用 CSS 选择器来提取它们。接下来,我们在 IDE 中的“def parse(self, response):”这一行下方添加以下代码:

pricing = response.css('[class="tp-headline-m text-neutral-0"]::text').getall()
 if pricing:
print("Price details:")
for price in pricing:
print(f"- {price.strip()}")

首先,我们将价格详情定义为 Scrapy 的 response 对象,这是您的 spider 要获取的内容。我们指定使用 CSS 选择器来抓取带有“tp-headline-m”和“text-neutral-0”类名的元素。

由于我们的价格元素仅由这两个类名定义,我们还指示 Scrapy 精确匹配类名,也就是说,目标元素必须恰好以字符串形式带有这两个类名。:text 指示直接抓取文本内容,也就是简单地复制 <p> 和 </p> 标签之间的内容。

然后我们告诉 Scrapy 获取所有符合上述类名的结果,并以一个 if 语句收尾,用于查找所有匹配指定条件的元素。最后,我们指示它使用格式化字符串打印这些值,并在价格前加上“-”符号。结果如下:

IMG7.png

现在,Scrapy 不再显示难以阅读的 HTML 代码,而是展示出一份整理好的价格元素列表,成功抓取了全部六个价格。不过请注意,末尾出现了“Join Our Discord Community”这句话。虽然我们欢迎您加入我们的 Discord 服务器,但它本不应出现在我们的 Scrapy 项目结果中。

Scrapy 之所以也抓取了这个元素,是因为它在字符串中同样带有完全相同的两个类名。

<p class="tp-headline-m text-neutral-0" data-astro-cid-m3jmx4ij=""> Join Our Discord Community </p>

我们想借此说明精确使用 CSS 选择器的重要性。如果您定位的类名同时被数十个不同元素使用,就可能抓取到不必要的大量数据,之后还得手动筛选。因此,建议花时间学习 HTML 和 CSS 结构,以便按需精确定位目标元素。

使用 XPath 选择器

CSS 选择器之外还有许多替代方案,XPath 便是较为常用的一种。这种查询语言可以让用户根据内容位置、元素层级、属性以及特定条件来获取数据。

例如,XPath 提供了名为 Axes 的关键字,可以根据数据相对于其他元素的位置来定位内容。您可以把它想象成在 HTML 文档中移动,通过“axis-name::node-test”这种格式来表达。

Axis 表示从起点出发的方向,比如子元素或父元素(HTML 正是以此来构建层级关系)。而 node(通常是一个 HTML 元素)则是您要查找的目标,比如 <p> 和 <li> 元素。XPath 语法使用 / 符号,因此一行简单的 XPath 代码大致如下:

//*/parent::p

// 表示从根节点开始,选取文档中所有层级的元素。* 通配符表示选取文档中任意位置的所有节点。第三个 / 表示我们定位的是所有父元素,末尾的 p 则表示我们要查找的是段落元素。

换句话说,该表达式用于查找选定文档中所有属于其中任意元素直接父级的段落。

处理 JavaScript 与动态内容

JavaScript 元素让网站变得具有交互性和动态性,但这也给数据抓取带来了 Scrapy 难以克服的挑战。Scrapy 主要用于抓取静态 HTML 文档,并不会渲染其中嵌入的 JavaScript 元素。

JavaScript 可以从 API 加载数据,例如价格信息或天气预报。由于 Scrapy 不会执行这些代码,因此无法获取仅在 JavaScript 执行后才会出现的数据。同时,部分内容可能需要用户进行滚动或点击按钮等交互后才会显示。

这就需要一种能够模拟人类操作并执行 JavaScript 代码的 Python 脚本方案。下面我们来介绍两种能有效弥补 Scrapy 这类网络爬取短板的工具:Selenium 和 Playwright。

Selenium

Selenium 是一种成熟稳健的浏览器自动化框架,是数据抓取中极具价值的工具,可用于获取需要登录或点击按钮等交互才能得到的数据,并兼容多种浏览器和编程语言。

Scrapy 与 Selenium 是两种不同但能很好配合使用的工具——毕竟,如果没有强大的扩展插件为其增添更多功能,Scrapy 又能算什么呢?当您需要抓取大量使用 JavaScript 的网站时,可以通过 Scrapy 中间件集成(通常是 Scrapy Downloader Middleware)将任务交给 Selenium 处理。完整内容请阅读这篇 Scrapy 与 Selenium 对比指南。

Playwright

Playwright 由 Microsoft 开发,最初用于 Web 应用测试,但如今也被广泛用于数据抓取。与 Selenium 类似,它同样用于实现浏览器自动化,并会在抓取前自动等待网站完全加载完毕,因此您无需为此额外编写代码。

它与 Scrapy 的集成方式与 Selenium 相同。Playwright 支持的浏览器种类相对较少,但运行速度通常更快,上手也相对容易,只是支持的编程语言范围较窄。

在 Scrapy 中使用代理

许多网站都部署了反爬虫防护机制:有的是为了防止收集用户数据,有的是担心爬虫拖慢网站速度。您还会经常遇到 Cloudflare 的数据抓取防护措施,例如 CAPTCHA 验证或 IP 地址限制,这时就需要用到代理了。

按照上述步骤创建并运行 Scrapy 爬虫后,它会一直使用同一个 IP 地址采集数据,如果不定期轮换 IP,就可能面临被检测和封禁的风险。为此,我们提供超过 100 万个 IP 地址供您购买住宅代理 IP,并支持自定义自动 IP 轮换。

我们提供5种适合数据抓取的代理方案,具体使用场景和优势请参见下表。如果您的代理需求不仅限于数据抓取,还包括抢购限量硬件,我们的AMD 代理专为 AMD 官方商店的高竞争抢购场景打造。

Mars_Blog 2_1920x1745.png

每种代理类型都适合不同的数据抓取需求,其中住宅代理因出色的匿名性而格外受欢迎。接下来我们看看如何在 Scrapy 环境中配置使用住宅代理。

Scrapy 代理配置

在 Scrapy 中配置代理有两种方法。本文将采用更简单、更易管理的方式,手动且可自定义程度更高的配置方法留待日后介绍。首先打开命令提示符,输入:

pip install scrapy-rotating-proxies

接下来,您需要将代理信息添加到 Scrapy 项目目录下的 settings.py 文件中。进入该目录,在 IDE 中打开 settings.py,插入以下代码:

ROTATING_PROXY_LIST = []

本例将使用一个带黏性 IP 的丹麦住宅代理,您需要根据自己的代理信息做相同的配置,示例如下:

ROTATING_PROXY_LIST = [
'http://mr10478lWc5:MfW4bYnUPd_country-dk_session-4seo4wht_lifetime-12h@ultra.marsproxies.com:44443',
]

然后,您需要启用轮换代理与封禁检测中间件,添加以下代码:

DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
}

最后,保存设置,并像之前一样在命令提示符中执行 scrapy crawl 命令。在我们的示例中,命令提示符会显示以下内容:

IMG8.png

我们已将关键部分高亮标出,命令提示符在此确认已成功连接到代理服务器。

您随时可以在 settings.py 文件中添加更多代理,以实现 IP 轮换并规避检测和封禁。完成此配置后,您的 Scrapy 项目便能在不暴露真实 IP 地址的情况下进行数据抓取。

至此,Scrapy Python 实战教程部分就结束了。不过为求完整,下面我们再简要介绍其他反检测技巧,以及常见的 Scrapy 报错修复方法。

为您的 Scrapy 爬虫配置轮换住宅代理,避免被封禁

获取住宅代理

反检测技巧

仅靠隐藏 IP 并不足以完全掩盖您的抓取请求,还需要轮换 User-Agent 字符串、管理会话并设置延迟。

User-Agent 轮换

每次发起请求时,您的爬虫都会发送一个 User-Agent 字符串,用于标识浏览器(例如Browser Vision)和操作系统,以下是一个随机 User-Agent 字符串示例:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36

这表示该请求来自一台使用 115.0.0.0 版 Google Chrome 浏览器的 64 位 Windows 10 系统设备。

即使切换 IP,网站仍可通过检查 User-Agent 字符串识别出多次抓取请求。好在您可以像前面 IP 轮换示例那样,通过修改 settings.py 文件来轮换 User-Agent。受篇幅所限,具体操作流程我们会在专门的博文中详细介绍。

会话管理

会话跟踪是识别用户身份的另一种方式。您一定注意到过网站会请求您接受Cookie,用于存储用户数据。例如,当您登录 Facebook 时,如果您允许,系统便会将登录凭证保存在 Cookie 中;下次访问时无需再次输入凭证,Facebook会读取该 Cookie 并直接打开您的账户。

为每个目标网站使用不同的 Cookie 和 IP 地址,能够显著降低被检测和封禁的风险。Scrapy 默认启用 CookiesMiddleware 来高效管理会话 Cookie,其底层使用所谓的 CookieJar,开发者可以对其进行调整以增强反检测能力。

使用延迟

检测数据抓取行为最简单的方法之一就是监控数据请求频率。爬虫发起请求的速度远快于真人,这很容易暴露出自动化 Python 脚本的痕迹。要解决这个问题,只需添加

DOWNLOAD_DELAY = x

这行代码到 settings.py 文件中,其中 x 表示延迟秒数,这样您的爬虫在发起数据请求之间就会有更长的间隔,使行为更接近真人。

常见 Scrapy 报错及解决方法

开发工作很大程度上就是在不断发现并修复错误,数据抓取也不例外。以下是一些常见的 Scrapy代理报错及其解决方法。

407 代理身份验证错误

如果您使用的代理服务器需要身份验证,就可能遇到此错误。我们的代理采用 hostname:port:username:password 的格式,以下是本文所使用的代理服务器示例:

ultra.marsproxies.com:44443:mr10478lWc5:MfW4bYnUPd_country-dk_session-lafwx1xh_lifetime-12h

然而,Scrapy 要求使用

http://username:password@proxy_host:proxy_port
该格式,因此我们已在 settings.py 文件中将其修改为如下形式:

http://mr10478lWc5:MfW4bYnUPd_country-dk_session-4seo4wht_lifetime-12h@ultra.marsproxies.com:44443

如果格式不正确,Scrapy 将无法完成代理身份验证,抓取网站时命令提示符就会提示 407 Proxy Authentication Required 错误。

代理离线

住宅代理使用真实用户的设备作为代理节点,如果该设备离开网络连接范围,就可能出现离线情况,此时您会遇到“Failed to connect to proxy server”或“Connection refused”错误。

首先,请检查您输入的代理字符串是否完全正确、没有任何拼写错误。确认无误后,可前往我们的免费在线代理检测工具,粘贴您的代理字符串进行检测——如果代理服务器正常运行,应返回状态码200;否则,请更换其他代理服务器重新连接。

403 Forbidden

这是一种常见错误,表示尽管已成功连接到服务器,但您没有访问特定数据的权限。可能是您的 IP 地址已被屏蔽,也可能是网站通过 User-Agent 识别出了抓取行为。

这种情况下,可以考虑采用其他在线隐私保护方法。前文提到的 IP 与 User-Agent 字符串轮换,以及设置 HTTP 请求延迟,都有助于解决这一问题。

总结与后续步骤

Scrapy 与代理搭配使用,能够有效支撑大规模数据抓取:Scrapy 的异步架构支持多个并发请求,可同时抓取多个网站;而代理则提供 IP 地址,帮助规避检测和封禁。

掌握 Scrapy 之后,建议您进一步学习 Selenium 或 Playwright 等工具,它们能够帮助您抓取大量使用 JavaScript 的网站,提升数据采集的覆盖范围和准确性。

同时,也要持续关注最新的抓取防护技术:Cloudflare 等专业 IT 安全公司会定期更新网站防护算法,这有时会对合规的数据抓取请求造成干扰。最后,如果您想了解更多工具和技巧,欢迎查看我们的数据抓取指南。

还在纠结用哪种编程语言?我们的Go 与 Python 对比详细分析了二者在大规模数据抓取项目中的适用情况。

Scrapy 比 BeautifulSoup 更好吗?

Scrapy 与 BeautifulSoup 并非简单的优劣对比,二者用途不同:Scrapy 是用于构建爬虫的框架,而 BeautifulSoup 则是用于解析 XML 和 HTML 内容的 Python 库,两者能够很好地配合使用。

Scrapy 比 Selenium 更好吗?

Selenium 是一种出色的浏览器自动化工具,将其与 Scrapy 做优劣对比意义不大。对于包含大量 JavaScript 元素的动态网站,建议使用 Selenium 进行抓取;而在大规模抓取任务方面,Scrapy 则更具优势。

Scrapy 有哪些局限性?

Scrapy 最明显的局限在于不支持 JavaScript:该框架不会执行 JavaScript 代码,因此无法获取网站完全加载后才显示的数据,也无法获取通过 API 返回的信息。

返回博客
-

相关文章