查看全课大纲(第 25 / 101 节)
- 1.数据分析基本概念
- 2.学习数据分析的一般路线
- 3.数据分析的流程
- 4.数据类型
- 5.环境部署(1)
- 6.环境部署(2)
- 7.课程介绍
- 8.TXT文件操作
- 9.JSON文件操作
- 10.CSV文件操作
- 11.Excel文件操作
- 12.数据库及SQL常用语法
- 13.数据库基本操作
- 14.数据库多表连接
- 15.实战:欧洲职业足球数据库分析
- 16.爬虫简介
- 17.URL管理模块
- 18.网页下载模块
- 19.网页解析模块(1)
- 20.网页解析模块(2)
- 21.Scrapy简介
- 22.Scrapy使用步骤(1)
- 23.Scrapy使用步骤(2)
- 24.Scrapy使用步骤(3)
- 25.Scrapy使用步骤(4)
- 26.实战:获取国内城市空气质量指数数据
- 27.NumPy和SciPy介绍
- 28.多维数组
- 29.多维数组操作
- 30.NumPy的常用方法
- 31.向量化介绍
- 32.向量化及通用函数
- 33.实战:2016美国大选分析
- 34.数据结构-Series
- 35.数据结构-DataFrame
- 36.数据结构-Index
- 37.Series的索引操作
- 38.DataFrame的索引操作
- 39.索引操作总结
- 40.运算与对齐
- 41.函数应用操作(1) -- map
- 42.函数应用操作 (2) -- apply applymap
- 43.文件读写操作
- 44.排序操作
- 45.数据清洗--处理缺失数据
- 46.数据清洗--处理重复数据
- 47.数据清洗--替换数据
- 48.常用统计方法(1) -- describe quantile
- 49.常用统计方法(2) -- sum mean median count
- 50.常用统计方法(3) -- max min idxmax idxmin
- 51.常用统计方法(4) -- mad var std cumsum
- 52.实战:全球食品数据分析
- 53.层级索引
- 54.分组与聚合介绍
- 55.分组操作(1) -- GroupBy对象及常用聚合操作
- 56.分组操作(2) -- 自定义分组及聚合操作
- 57.透视表介绍
- 58.透视表操作
- 59.数据规整(1) -- 数据合并concat
- 60.数据规整(2) -- 数据连接merge
- 61.数据重构(3) -- 数据重构stack unstack
- 62.实战:互联网电影资料库分析
- 63.探索性数据分析EDA介绍
- 64.EDA的目的
- 65.EDA常用工具
- 66.Matplotlib绘图基本介绍
- 67.Matplotlib画布
- 68.散点图和柱状图的绘制
- 69.直方图的绘制
- 70.矩阵绘图
- 71.子图的使用
- 72.Matplotlib颜色、标记、线型
- 73.Matplotlib坐标刻度、标签、图例、标题
- 74.Seaborn介绍
- 75.数据集分布可视化(1) -- 单变量分布、双变量分布
- 76.数据集分布可视化(2) -- 变量关系可视化
- 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
- 78.交互式数据可视化工具Bokeh介绍
- 79.Bokeh绘制散点图、柱状图、盒子图、弦图
- 80.Bokeh绘制常用图形元素
- 81.3D绘图 -- mplot3d
- 82.3D曲线可视化
- 83.3D散点图可视化
- 84.3D柱状图可视化
- 85.Pandas绘图
- 86.实战:Lending Club借贷数据探索性分析及可视化
- 87.机器学习介绍及应用场景
- 88.机器学习建模介绍 (1) -- 分类
- 89.机器学习建模介绍 (2) -- 回归
- 90.机器学习建模介绍 (3) -- 聚类
- 91.机器学习分类
- 92.机器学习工具scikit-learn
- 93.使用scikit-learn的流程
- 94.数据集准备及划分
- 95.模型选择
- 96.数据预处理及特征工程
- 97.过拟合与欠拟合
- 98.模型调参介绍
- 99.模型调参方法
- 100.模型测试及评价
- 101.实战:通过移动设备行为数据预测性别和年龄
Scrapy使用步骤(4)
约 10 分钟
Scrapy Pipeline:数据清洗与持久化
小象实战讲义 · Python数据分析实战
在上一节中,我们学习了如何使用 Scrapy 的 Spider 来解析网页并提取出结构化的数据(Item)。但解析出的数据往往不是最终形态,我们通常需要对其进行清洗(如去重、填充缺失值),并最终保存到本地文件或数据库中。本节将讲解 Scrapy 框架中的 Pipeline 组件,它就像一个管道,负责对 Spider 爬取到的 Item 进行一系列的后处理,并最终完成数据的持久化。
💡 核心导读
- Pipeline 的作用:理解 Pipeline 作为“数据处理管道”的核心思想,它负责接收、处理和输出 Spider 产生的 Item。
- Pipeline 的配置:掌握如何在
settings.py中启用和配置自定义的 Pipeline。 - Pipeline 的编写:学习编写一个自定义的 Pipeline 类,实现数据的清洗、转换和保存(以 CSV 文件为例)。
- 实战演练:完成一个完整的 Scrapy 项目,将爬取的城市名称和链接数据,经过 Pipeline 处理后,保存为 CSV 文件。
什么是 Pipeline?
在 Scrapy 框架中,Pipeline 是一个专门用于处理 Spider 爬取到的 Item 的组件。你可以把它想象成一个工厂的流水线:
- Spider 是原料采集车间,负责从网络上“采集”原始数据(Item)。
- Pipeline 是加工车间,负责对采集到的原料(Item)进行清洗、筛选、组装等处理。
- 最终,处理好的成品(数据)被包装、入库(保存到文件或数据库)。
这种设计将数据爬取(Spider)与数据处理(Pipeline)解耦,使得代码结构更清晰,也便于维护和扩展。例如,你可以在 Pipeline 中实现:
- 数据清洗:去除重复项、过滤无效数据、填充缺失值。
- 数据验证:检查数据格式是否符合预期。
- 数据持久化:将数据保存为 CSV、JSON、XML 文件,或写入 MySQL、MongoDB 等数据库。
配置 Pipeline
Pipeline 的启用和配置主要在项目根目录下的 settings.py 文件中进行。
- 找到配置项:打开
settings.py文件,找到名为ITEM_PIPELINES的配置字典。它默认是被注释掉的。 - 启用 Pipeline:取消该字典的注释。字典的键(Key)是你将要编写的 Pipeline 类的完整导入路径,值(Value)是一个代表该 Pipeline 执行优先级的整数(范围 0-1000,数值越小优先级越高)。当有多个 Pipeline 时,Scrapy 会根据优先级决定执行顺序。
# settings.py 中的配置示例
ITEM_PIPELINES = {
# ‘你的项目名.pipelines.你的Pipeline类名’ : 优先级,
'myproject.pipelines.CityItemPipeline': 300,
}优先级说明:优先级数字越小,越早执行。通常使用默认值 300 即可。如果你有多个 Pipeline,例如一个用于数据清洗(优先级 200),一个用于数据保存(优先级 400),那么清洗 Pipeline 会先执行。
编写自定义 Pipeline
Pipeline 本质上是一个 Python 类。我们将在 pipelines.py 文件中创建它。这个类通常需要实现三个核心方法:
open_spider(self, spider): 当 Spider 被打开时调用。通常在这里进行初始化工作,如打开文件、连接数据库。process_item(self, item, spider): 这是 Pipeline 的核心。每个被 Spider 爬取到的 Item 都会经过这个方法。你可以在这里对 Item 进行处理,并必须返回一个 Item 对象(或抛出DropItem异常丢弃该 Item)。close_spider(self, spider): 当 Spider 被关闭时调用。通常在这里进行收尾工作,如关闭文件、断开数据库连接。
下面,我们以实现一个将 Item 保存为 CSV 文件的 CityItemPipeline 为例。请注意:由于本课程环境限制(禁用了 Scrapy 库),以下代码为原理性伪代码,用于说明 Pipeline 的编写逻辑。在实际的 Scrapy 项目中,你可以直接使用 scrapy.exporters 模块中的 CsvItemExporter 来简化导出工作。
# 以下为 Scrapy Pipeline 原理性伪代码,展示核心结构
# 实际 Scrapy 项目请使用 scrapy.exporters.CsvItemExporter
class CityItemPipeline:
def open_spider(self, spider):
"""Spider 启动时执行,初始化文件等资源"""
# 伪代码:打开一个 CSV 文件准备写入
# self.file = open('cities.csv', 'w', newline='', encoding='utf-8')
# self.writer = csv.DictWriter(self.file, fieldnames=['city_name', 'city_link'])
# self.writer.writeheader()
print("Pipeline: 开始处理数据,准备写入文件...")
def process_item(self, item, spider):
"""处理每一个 Item"""
# 伪代码:这里可以进行数据清洗,例如去除空值
# if item.get('city_name') and item.get('city_link'):
# self.writer.writerow({
# 'city_name': item['city_name'],
# 'city_link': item['city_link']
# })
# else:
# print(f"丢弃无效数据: {item}")
print(f"Pipeline: 正在处理 Item -> {dict(item)}")
# 必须返回 item,以供后续可能的 Pipeline 处理
return item
def close_spider(self, spider):
"""Spider 关闭时执行,释放资源"""
# 伪代码:关闭文件
# self.file.close()
print("Pipeline: 数据处理完成,文件已保存。")实战:模拟 Pipeline 数据处理流程
虽然我们无法在讲义中运行真实的 Scrapy 项目,但我们可以用纯 Python 模拟 Pipeline 的核心思想:接收数据 -> 处理数据 -> 输出数据。假设我们已经通过某种方式(比如上一节的解析逻辑)获得了一个城市数据列表,现在需要对其进行清洗并保存。
下面的代码块完全使用 Python 标准库,模拟了 Pipeline 的流程:
import csv
from io import StringIO
# 模拟从 Spider 解析得到的原始数据列表 (Item 列表)
raw_city_items = [
{'city_name': '北京', 'city_link': 'https://example.com/beijing'},
{'city_name': '上海', 'city_link': 'https://example.com/shanghai'},
{'city_name': '', 'city_link': 'https://example.com/empty'}, # 无效数据:城市名为空
{'city_name': '广州', 'city_link': 'https://example.com/guangzhou'},
{'city_name': '深圳', 'city_link': 'https://example.com/shenzhen'},
{'city_name': '广州', 'city_link': 'https://example.com/guangzhou'}, # 重复数据
]
# 1. 数据清洗函数 (模拟 Pipeline 的 process_item 逻辑)
def clean_city_item(item):
"""清洗单个城市数据项"""
# 规则1: 城市名不能为空
if not item.get('city_name'):
print(f"清洗: 丢弃城市名为空的数据 -> {item}")
return None
# 规则2: 链接不能为空(这里假设链接都有值)
if not item.get('city_link'):
print(f"清洗: 丢弃链接为空的数据 -> {item}")
return None
# 其他清洗规则可以在此添加...
return item
# 2. 主处理流程 (模拟 Spider -> Pipeline 的数据流)
cleaned_items = []
seen = set() # 用于去重的集合
for raw_item in raw_city_items:
cleaned_item = clean_city_item(raw_item)
if cleaned_item is None:
continue # 丢弃无效数据
# 规则3: 基于城市名和链接进行去重
item_key = (cleaned_item['city_name'], cleaned_item['city_link'])
if item_key in seen:
print(f"去重: 丢弃重复数据 -> {cleaned_item}")
continue
seen.add(item_key)
cleaned_items.append(cleaned_item)
print(f"清洗和去重后,有效数据项数量: {len(cleaned_items)}")
# 3. 数据持久化 (模拟保存为 CSV)
# 使用 StringIO 模拟文件写入,避免实际生成文件
output = StringIO()
fieldnames = ['city_name', 'city_link']
writer = csv.DictWriter(output, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(cleaned_items)
# 获取模拟的 CSV 文件内容
csv_content = output.getvalue()
output.close()
print("\n--- 生成的 CSV 文件内容 ---")
print(csv_content)清洗: 丢弃城市名为空的数据 -> {'city_name': '', 'city_link': 'https://example.com/empty'}
去重: 丢弃重复数据 -> {'city_name': '广州', 'city_link': 'https://example.com/guangzhou'}
清洗和去重后,有效数据项数量: 4
--- 生成的 CSV 文件内容 ---
city_name,city_link
北京,https://example.com/beijing
上海,https://example.com/shanghai
广州,https://example.com/guangzhou
深圳,https://example.com/shenzhen通过上面的模拟,我们清晰地演示了 Pipeline 的核心价值:在数据被最终保存前,提供了一个集中、可配置的环节来进行各种数据质量治理工作。
关于 Scrapy 内置导出器的补充说明
在真实的 Scrapy 项目中,为了简化将 Item 导出为各种格式文件的过程,Scrapy 提供了 scrapy.exporters 模块。例如,使用 CsvItemExporter 可以非常方便地将 Item 导出为 CSV 文件。
需要注意的一个常见问题:某些早期版本或配置下的 CsvItemExporter 在导出 CSV 时,可能会在每行数据之间插入一个空行。如果遇到这个问题,你有两种解决思路:
- 后续处理:在拿到 CSV 文件后,使用我们后面将学到的 Pandas 进行读取和清洗,Pandas 可以轻松处理多余的空行。
- 自定义导出逻辑:你可以完全自己编写文件写入逻辑,不使用内置的 Exporter,从而精确控制输出格式。
📝 动手练一练
- 概念辨析:Scrapy 框架中,Spider 和 Pipeline 的主要分工是什么?如果我想在数据保存到数据库前,先过滤掉所有“价格”字段低于 10 元的商品记录,这个逻辑应该放在哪个组件中实现?
👉 点击查看参考答案
- 分工:Spider 主要负责发现和爬取目标网页,并从中解析和提取结构化的数据(Item)。Pipeline 主要负责接收 Spider 产生的 Item,并对其进行清洗、验证、转换和持久化等后续处理。
- 过滤逻辑位置:过滤“价格低于10元”的记录属于数据清洗和筛选的范畴,这个逻辑应该放在 Pipeline 的
process_item方法中实现。在该方法中检查 Item 的“价格”字段,如果低于阈值,应抛出DropItem异常来丢弃该 Item,使其不会被后续的 Pipeline 处理或保存。(注意:在process_item中返回None并不能丢弃 Item——Scrapy 会把返回的None原样传给下一个 Pipeline;丢弃 Item 的唯一正规方式就是raise DropItem,这与本节正文 L48 的口径一致。)
- 代码实践:参考上面的模拟 Pipeline 代码,假设原始数据中增加了“空气质量指数(AQI)”字段。请修改清洗函数
clean_city_item,增加一条规则:如果 AQI 值大于 150(表示污染),则将该条数据也丢弃。原始数据如下:raw_city_items = [ {'city_name': '北京', 'city_link': '...', 'aqi': 120}, {'city_name': '石家庄', 'city_link': '...', 'aqi': 180}, {'city_name': '厦门', 'city_link': '...', 'aqi': 45}, ]
👉 点击查看参考答案
def clean_city_item_v2(item):
"""清洗单个城市数据项,增加 AQI 过滤"""
# 规则1: 城市名不能为空
if not item.get('city_name'):
print(f"清洗: 丢弃城市名为空的数据 -> {item}")
return None
# 规则2: 链接不能为空
if not item.get('city_link'):
print(f"清洗: 丢弃链接为空的数据 -> {item}")
return None
# 新增规则3: AQI 不能大于150
if item.get('aqi', 0) > 150: # 使用 get 防止 aqi 字段缺失,默认值为0
print(f"清洗: 丢弃 AQI > 150 的数据 -> {item}")
return None
return item
# 测试新函数
test_items = [
{'city_name': '北京', 'city_link': '...', 'aqi': 120},
{'city_name': '石家庄', 'city_link': '...', 'aqi': 180},
{'city_name': '厦门', 'city_link': '...', 'aqi': 45},
]
for it in test_items:
result = clean_city_item_v2(it)
print(f"输入: {it} -> 输出: {result}")输入: {'city_name': '北京', 'city_link': '...', 'aqi': 120} -> 输出: {'city_name': '北京', 'city_link': '...', 'aqi': 120}
清洗: 丢弃 AQI > 150 的数据 -> {'city_name': '石家庄', 'city_link': '...', 'aqi': 180}
输入: {'city_name': '石家庄', 'city_link': '...', 'aqi': 180} -> 输出: None
输入: {'city_name': '厦门', 'city_link': '...', 'aqi': 45} -> 输出: {'city_name': '厦门', 'city_link': '...', 'aqi': 45}本章小结
本节我们完成了 Scrapy 框架核心流程的最后一块拼图——Pipeline。我们了解到,Pipeline 是 Scrapy 项目中负责数据后处理的“加工车间”,它将 Spider 的爬取逻辑与数据的清洗、持久化逻辑优雅地分离。
📋 行动清单
- 理解 Pipeline 机制:在脑海中建立起“Spider 生产 Item -> Pipeline 加工 Item -> 输出最终结果”的数据流图。
- 掌握配置与编写:记住在
settings.py中配置ITEM_PIPELINES的方法,以及自定义 Pipeline 类需要实现的三个核心方法(open_spider,process_item,close_spider)。 - 模拟数据流:尝试用纯 Python 代码模拟一个简单的数据处理管道,对一组字典列表进行清洗(去空、去重)并输出,巩固“数据流”处理的思想。这种思想在后续学习 Pandas 等数据处理库时同样适用。
至此,关于 Scrapy 框架的核心使用步骤我们就介绍完毕了。从创建项目、定义数据结构、编写爬虫主体到配置处理管道,你已经掌握了一个现代爬虫框架的基本工作流程。虽然在实际项目中可能会遇到更复杂的页面结构和反爬策略,但这套基础方法论是解决问题的根本。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载本节课件:Scrapy使用步骤(4)PDF · 218KB
- 下载全套课件打包(第1-5章)ZIP · 12.8MB
- 下载全套课件打包(第6-8章)ZIP · 15MB
- 下载实战数据集:AppleStore 应用商城分析ZIP · 329KB
- 下载实战数据集:女性服装电商分析ZIP · 2.8MB
- 下载Python 数据分析环境搭建指南PDF · 2MB
- 下载Scrapy 安装教程PDF · 12.7MB
- 下载附加实战项目:AppleStore 应用商城数据分析ZIP · 0.3MB · ipynb + CSV 数据
- 下载附加实战项目:银行电话营销数据分析ZIP · 0.4MB · ipynb + CSV 数据
- 下载附加实战项目:女性服装电商评论数据分析ZIP · 2.7MB · ipynb + CSV 数据
- 下载附加实战项目:美国化学学会杂志数据分析ZIP · 34.2MB · ipynb + SQLite 数据库