59·模块十二:网络编程高级

进阶爬虫:Scrapy 框架入门

web-scrapingscrapyframework

第59课:进阶爬虫 - Scrapy 框架入门

学习目标

完成本课学习后,你将能够:

  1. 理解 Scrapy 框架的核心组件与工作流程。
  2. 熟练使用 scrapy startproject 命令创建并组织 Scrapy 项目。
  3. 编写一个完整的 Spider 来爬取网页数据。
  4. 使用 Scrapy Shell 调试和测试选择器(Selectors)。
  5. 利用 Item Pipeline 对爬取的数据进行清洗和存储。

核心概念

在上一课中,我们使用 requestsBeautifulSoup 手动编写了爬虫。这种方式对于简单的、一次性的任务很方便,但当项目变得复杂(需要处理分页、登录、数据持久化、反爬策略)时,代码会变得难以维护。

Scrapy 是一个为了爬取网站数据、提取结构性数据而编写的应用框架。它就像一个功能完备的"爬虫工厂",内置了请求调度、下载、数据提取、数据处理流水线等一系列工具,让我们可以专注于定义"要爬取什么数据"和"如何提取数据",而无需重复造轮子。

项目结构: 一个标准的 Scrapy 项目目录如下:

myproject/
    scrapy.cfg        # 项目部署配置文件
    myproject/        # 项目的 Python 模块
        __init__.py
        items.py      # 定义要抓取的数据结构(Item)
        middlewares.py # 下载器和Spider的中间件
        pipelines.py  # 数据处理管道(如清洗、存储)
        settings.py   # 项目设置(如并发数、下载延迟、管道开启)
        spiders/      # 存放所有爬虫(Spider)的目录
            __init__.py

核心组件

  1. Spider:我们定义的爬虫类。它负责解析响应(Response),提取数据(Item)和新的请求(Request)。这是最需要我们编写代码的部分。
  2. Selector:Scrapy 内置的选择器,基于 lxml 库,支持 XPath 和 CSS 选择器,用于从 HTML/XML 中提取数据。
  3. Item:定义抓取数据的容器,类似于 Python 的字典,但更规范、有明确的字段。
  4. Item Pipeline:处理被 Spider 提取出来的 Item 的组件。典型应用包括:清洗数据、验证数据、存储到数据库或文件。
  5. Engine:框架的"大脑",负责控制数据流在系统中所有组件间的流动,并在发生动作时触发事件。

工作流程

  1. Engine 从 Spider 获取初始请求(Request)。
  2. Engine 将请求放入调度器(Scheduler)并请求下一个待爬取的请求。
  3. 调度器将下一个请求返回给 Engine。
  4. Engine 通过下载器中间件(Downloader Middlewares)将请求发送给下载器(Downloader)。
  5. 下载器完成下载,生成响应(Response),并将其发送回 Engine。
  6. Engine 通过 Spider 中间件(Spider Middlewares)将响应发送给 Spider。
  7. Spider 处理响应,提取出 Item 和新的 Request,并发送回 Engine。
  8. Engine 将提取的 Item 发送给 Item Pipeline,将新的 Request 发送给调度器。
  9. 重复步骤2-8,直到没有更多请求。

代码示例

我们将编写一个爬虫,从"名言网站" (quotes.toscrape.com) 抓取名言及其作者。

第一步:创建项目 在终端中运行:

scrapy startproject quotes_demo
cd quotes_demo

第二步:定义 Item 编辑 quotes_demo/items.py 文件,定义我们要抓取的数据字段。

# quotes_demo/items.py
import scrapy

class QuoteItem(scrapy.Item):
    # 定义字段,相当于数据表的列
    text = scrapy.Field()       # 名言内容
    author = scrapy.Field()     # 作者
    tags = scrapy.Field()       # 标签,通常是一个列表

第三步:编写 Spiderquotes_demo/spiders/ 目录下创建一个新文件 quotes_spider.py

# quotes_demo/spiders/quotes_spider.py
import scrapy
from quotes_demo.items import QuoteItem # 导入我们定义的Item

class QuotesSpider(scrapy.Spider):
    name = 'quotes' # Spider的唯一标识符,运行爬虫时使用
    allowed_domains = ['quotes.toscrape.com'] # 允许爬取的域名
    start_urls = ['http://quotes.toscrape.com/'] # 爬虫启动时的URL列表

    def parse(self, response):
        """
        默认的回调方法,用于处理下载器返回的每个页面响应。
        :param response: 网页的响应对象,包含页面内容
        """
        # 使用CSS选择器提取所有名言块
        quotes = response.css('div.quote')

        for quote in quotes:
            item = QuoteItem() # 实例化一个Item对象
            # 提取数据,并通过Item的字段赋值
            item['text'] = quote.css('span.text::text').get().strip()
            item['author'] = quote.css('small.author::text').get().strip()
            item['tags'] = quote.css('div.tags a.tag::text').getall()
            yield item # 使用yield将Item发送给Item Pipeline

        # 处理下一页:找到"下一页"按钮的链接
        next_page = response.css('li.next a::attr(href)').get()
        if next_page is not None:
            # 构建完整的下一页URL,并生成一个新的请求,回调函数依然是parse
            yield response.follow(next_page, callback=self.parse)

第四步:配置 Item Pipeline 编辑 quotes_demo/pipelines.py,实现一个简单的管道,将数据保存到 JSON 文件。

# quotes_demo/pipelines.py
import json

class SaveToPipeline:
    def open_spider(self, spider):
        """爬虫启动时调用,初始化文件"""
        self.file = open('quotes.json', 'w', encoding='utf-8')
        self.file.write('[\n') # 写入JSON数组开头
        self.first_item = True

    def process_item(self, item, spider):
        """每个Item都会经过这个方法,进行处理(如清洗、存储)"""
        # 将Item转换为字典,并写入JSON文件
        line = json.dumps(dict(item), ensure_ascii=False) + ',\n'
        if self.first_item:
            self.file.write(line)
            self.first_item = False
        else:
            # 在写入前,先写入一个逗号(因为JSON对象之间需要逗号分隔)
            self.file.write(',' + line)
        return item # 必须返回item,供后续管道使用

    def close_spider(self, spider):
        """爬虫关闭时调用,收尾工作"""
        self.file.write('\n]')
        self.file.close()

第五步:启用 Pipeline 并运行 编辑 quotes_demo/settings.py 文件,找到 ITEM_PIPELINES 配置项并取消注释,或添加如下内容:

# quotes_demo/settings.py (部分)
# 开启我们的Pipeline,并设置优先级(数值越小,优先级越高)
ITEM_PIPELINES = {
    'quotes_demo.pipelines.SaveToPipeline': 300,
}

# 设置下载延迟,体现友好性,避免给对方服务器造成过大压力
DOWNLOAD_DELAY = 2

运行爬虫: 在终端(项目根目录 quotes_demo 下)运行:

scrapy crawl quotes

运行结束后,你会在当前目录发现一个 quotes.json 文件,里面包含了所有抓取到的名言数据。

实践练习

练习一:基础数据提取

  1. 修改 quotes_spider.py,在提取 text 时,尝试使用 XPath 选择器代替 CSS 选择器。
    • 提示:XPath 表达式可以是 '//span[@class="text"]/text()'
  2. 运行爬虫,确保数据仍然能被正确提取。

练习二:增加数据字段

  1. 修改 QuoteItem 类,增加一个 birth_date 字段(作者出生日期)。
  2. 修改 quotes_spider.pyparse 方法,让它不仅爬取首页,还要进入每个作者的详情页,抓取其出生日期。
    • 提示:作者链接的选择器是 quote.css('small.author ~ a::attr(href)')'。你需要生成一个新的请求,使用新的回调函数来处理作者详情页。
  3. 修改 Pipeline,将新字段也保存到 quotes.json 中。

练习三:数据存储到 CSV

  1. pipelines.py 中新建一个 SaveToCsvPipeline 类。
  2. 实现将 Item 数据保存到 quotes.csv 文件的功能。CSV 文件第一行为列名(text, author, tags, birth_date)。
  3. settings.pyITEM_PIPELINES 中启用这个新的管道。
    • 提示:可以使用 Python 的 csv 模块。

常见错误

  1. 忘记在 settings.py 中启用 Pipeline:即使写好了 Pipeline 类,如果不在 ITEM_PIPELINES 字典中注册,它也不会生效。
  2. 选择器(Selector)错误:这是最常见的问题。CSS 或 XPath 选择器可能写错,或者页面结构发生了变化。一定要善用 Scrapy Shell 来调试! 在终端中运行 scrapy shell 'http://quotes.toscrape.com/',然后交互式地测试你的选择器:response.css('your.selector')
  3. 在 Pipeline 中未返回 itemprocess_item 方法的最后必须返回 item 对象。如果返回 None,那么这个 Item 将不会被后续的 Pipeline 处理,甚至可能丢失。
  4. 处理 JavaScript 动态加载的内容:Scrapy 默认无法执行 JavaScript。如果目标网站的内容是 JS 动态加载的,你需要使用 scrapy-splashscrapy-playwright 等扩展。
  5. 不遵守 robots.txt:Scrapy 默认遵守 robots.txt 规则。如果你在爬取一个禁止爬取的网站,可能会得到 Forbidden 错误。可以在 settings.py 中设置 ROBOTSTXT_OBEY = False(但请谨慎使用,确保你的行为合法合规)。

小结

本节课我们踏入了 Scrapy 框架的大门:

  • Scrapy 是一个强大、高效、可扩展的 Python 爬虫框架,适合中大型爬虫项目。
  • 核心组件 包括 Spider(定义爬取逻辑)、Item(数据容器)、Pipeline(数据处理管道)、Selector(数据提取器)。
  • 工作流程 清晰:Engine 协调 Spider、Scheduler、Downloader 和 Pipeline,形成数据流动的闭环。
  • 开发步骤startproject -> 定义 Item -> 编写 Spider -> 配置 Pipeline -> 在 settings 中启用相关功能。
  • 调试利器scrapy shell 是验证选择器和调试爬虫的必备工具。

掌握 Scrapy 意味着你拥有了一个强大的网络数据采集工具箱。下一课,我们将从数据采集转向 Web 应用开发,学习如何使用 Flask 框架来创建属于自己的网站。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「Web开发入门:Flask 框架简介」 以巩固所学知识。