项目四 基于框架实现动态网页数据采集与存储
Scrapy 的工程结构、爬虫编写、items 定义、Pipeline 落库——从“脚本”升级到“工程”。
本项目学习目标
- 能创建 Scrapy 项目并解释各目录作用
- 能写 Spider 并用 parse 提取数据
- 能定义 Item、写 Pipeline 存入 CSV/数据库
- 能在 settings 里配置并发、延时、请求头
本项目任务导览
基于框架实现动态网页数据采集与存储
使用 Scrapy 框架完成新闻公告页面的数据采集与存储,掌握框架化的爬虫开发流程。
- 项目导言与学习目标
- 任务 4-1 使用 Scrapy 框架完成新闻公告页面数据采集与存储
- 知识拓展与实例案例(3 例)
从"手写爬虫"进阶到"框架爬虫"。
Scrapy 是 Python 的一个应用 Twisted 异步处理的第三方应用程序框架,用户只需要定制开发几个模块即可实现一个爬虫,用来快速爬取网站并从页面中抓取网页内容以及各种图片。
了解 Scrapy 相关概念; 熟悉 Scrapy 框架的设置; 掌握文本解析与内容存储; 精通内存存储逻辑。
具备项目创建的能力; 具备使用 Scrapy 操作命令创建项目的能力; 具备解析网页中文本的能力; 具备网页数据采集能力。
具有较好的团队精神; 具有高度的责任感; 具备很强的学习和沟通能力; 具有清晰思路、较强的逻辑思维能力。
任务 4-1 使用 Scrapy 框架完成新闻公告页面数据采集与存储
任务技能:Scrapy 简介及安装 → 项目结构 → 项目构建流程 → 字段定义 → 项目设置 → 文本解析 → 数据存储 → 任务实施。
一、Scrapy 简介
Scrapy 是 Python 的一个第三方应用程序框架,主要用于爬取网站并从页面中提取结构数据。它基于 Twisted 异步处理,核心组件包括:
二、Scrapy 安装
Scrapy 属于 Python 的第三方框架,可以使用 pip 安装、wheel 安装和源码安装等安装方式,并且在通过 pip 方式安装 Scrapy 时,会自动安装 lxml、pyOpenSSL、Twisted、PyWin32 等相关的依赖库。
- 打开 cmd 命令窗口,输入 pip install scrapy;
- 进入 Python 的交互式命令行,通过 import 引入 Scrapy 进行安装验证。
三、Scrapy 项目结构
| 文件 / 目录 | 说明 |
|---|---|
| items.py | 字段定义文件 |
| scrapy.cfg | 项目配置文件 |
| middlewares.py | 中间件设置文件 |
| spiders | 爬虫文件存储目录 |
| settings.py | 项目设置文件 |
| pipelines.py | 项目管道文件 |
| MySpider.py | 爬虫文件 |
四、Scrapy 项目构建流程
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 项目创建 | 通过 startproject 命令创建爬虫项目 |
| 2 | 字段定义 | 修改 items.py 文件代码,明确抓取的目标字段 |
| 3 | 项目设置 | 修改 settings.py 文件对项目名称、并发请求、爬取策略、项目管道等进行设置 |
| 4 | 通用参数设置 | 在爬虫文件中,根据需求选择通用参数,爬取页面数据 |
| 5 | 爬虫文件创建 | 通过 genspider 命令在 spiders 目录中创建爬虫文件 |
| 6 | 文本解析 | 通过 Xpath 或 CSS 选择器解析 XML 文档 |
| 7 | 数据存储 | 修改 pipelines.py 代码,通过管道的设置实现爬取内容的存储 |
五、Scrapy 操作命令
| 命令 | 描述 |
|---|---|
| -h | 查看可用命令的列表 |
| fetch | 使用 Scrapy downloader 提取的 URL |
| runspider | 未创建项目的情况下,运行一个编写好的 spider 模块 |
| settings | 规定项目的设定值 |
| shell | 给定 URL 的一个交互式模块 |
| startproject | 用于创建项目 |
| version | 显示 Scrapy 版本 |
| view | 使用 Scrapy downloader 提取 URL 并显示在浏览器中的内容 |
| genspider | 使用内置模板在 spiders 文件下创建一个爬虫文件 |
| bench | 测试 scrapy 在硬件上运行的效率 |
| 命令 | 描述 |
|---|---|
| crawl | 用来使用爬虫抓取数据,运行项目 |
| check | 检查项目并由 crawl 命令返回 |
| list | 显示本项目中可用爬虫(spider)的列表 |
| edit | 可以通过编辑器编辑爬虫 |
| parse | 通过爬虫分析给定的 URL |
六、自定义字段(items.py)
在进行网页内容的爬取时,明确目标是不可或缺的一个步骤。在 Scrapy 中,通过修改 items.py 文件中代码进行字段的自定义操作即可明确爬取的目标。
# 语法格式一:使用 scrapy.Field()
# 导入scrapy模块
import scrapy
# 定义包含scrapy.Item参数的类
class ScrapynameItem(scrapy.Item):
# define the fields for your item here like:
# 自定义字段
name = scrapy.Field()
# 通过提示
pass除了通过"scrapy.Field()"方法外,还可以通过 import 命令从 Scrapy 中先引入 Item 和 Field,之后直接使用即可。
# 语法格式二:引入 Item 和 Field
# 导入scrapy的Item参数和Field方法
from scrapy import Item, Field
# 定义包含scrapy.Item参数的类
class ScrapynameItem(Item):
# 自定义字段
name = Field()
# 通过提示
passitems.py 文件中包含的内容仅为字段的定义,还需在 MySpider.py 文件中通过类名称引入 items.py 文件中 class 定义的类并实例化字段对象,之后通过"对象[字段名称]=值"的方式进行赋值操作。
# MySpider.py 中使用字段
import scrapy
# 导入 ScrapynameItem 类
from ScrapyName.items import ScrapynameItem
class MyspiderSpider(scrapy.Spider):
def parse(self, response):
item = ScrapynameItem()
item["name"] = "值"七、项目设置(settings.py)
Scrapy 中项目的设置通过 settings.py 文件,通过指定项目属性和属性值定义设置项目,包括项目名称设置、并发请求设置、爬虫中间件设置等。
八、通用参数设置
| 通用 Spider 参数 | 描述 |
|---|---|
| scrapy.Spider | 通用 Spider |
| CrawlSpider | 指定规则爬取网站 |
| XMLFeedSpider | 通过迭代节点分析 XML 内容 |
九、选择器
| 符号和方法 | 描述 |
|---|---|
| nodeName | 选取此节点的所有节点 |
| / | 根节点 |
| // | 选择文档中的所有匹配节点 |
| . | 当前节点 |
| .. | 当前节点的父节点 |
| @ | 选取属性 |
| * | 匹配任何元素节点 |
| @* | 匹配任何属性节点 |
| Node() | 匹配任何类型的节点 |
| text() | 获取文本内容 |
在 Scrapy 中,路径表达式通过 xpath() 方法应用:
# xpath() 方法应用
import scrapy
# 从 scrapy.selector 中导入 Selector
from scrapy.selector import Selector
class MyspiderSpider(scrapy.Spider):
name = 'MySpider'
allowed_domains = ['域名列表']
start_urls = ['网址列表']
def parse(self, response):
# 构造 Selector 实例
sel = Selector(response)
# 解析 HTML
xpath = sel.xpath('表达式')
pass在 Scrapy 中,CSS 路径表达式通过 css() 方法应用:
# css() 方法应用
import scrapy
# 从 scrapy.selector 中导入 Selector
from scrapy.selector import Selector
class MyspiderSpider(scrapy.Spider):
name = 'MySpider'
allowed_domains = ['域名列表']
start_urls = ['网址列表']
def parse(self, response):
# 构造 Selector 实例
sel = Selector(response)
# 解析 HTML
css = sel.css('表达式')
pass十、数据存储
scrapy crawl 爬虫文件名称 path在 Scrapy 中,可以使用 crawl 命令的"-o"参数,在运行 Scrapy 项目时将数据保存到本地 JSON、CSV 等文件中。
Scrapy 中管道的使用需要先在 settings.py 文件添加 ITEM_PIPELINES 参数,进行管道的启用。
ITEM_PIPELINES = {
'ScrapyProject.pipelines.ScrapyprojectPipeline': 300,
}| 参数 | 描述 |
|---|---|
| ScrapyProject | 项目名称 |
| pipelines | 管道文件名称 |
| ScrapyprojectPipeline | 管道中包含的类名称 |
| 300 | 执行管道的优先级,值为 0~1000,数字越小,管道的优先级越高,优先调用 |
十一、任务实施:使用 Scrapy 框架实现新闻公告页面内容的采集与存储
- 打开命令窗口,使用 startproject 命令创建一个名为 PressRelease 的 Scrapy 项目;
- 切换到命令行,进入 PressRelease 的项目,使用 genspider 命令在 spiders 目录中创建一个名为 job 的爬虫文件;
- 输入地址 https://www.inspur.com/lcjtww/445068/445237/index.html;
- 修改配置文件,进入 settings.py 文件,将 robots.txt 策略设置为不采用,并添加 ITEM_PIPELINES 参数开启管道;
- 在名为"mysql"的 MySQL 数据库中,创建一个用于存储数据的表 press_release;
- 查看数据表 press_release 中的数据,对数据存储是否成功进行验证。
知识拓展与实例案例
课堂讲解用:3 个可运行实例 + 知识拓展卡片。
知识拓展
实例 1:定义 Item 与 Spider 提取数据
目标:演示 Scrapy 项目中 Item 字段定义与 Spider 解析方法的完整结构。
# items.py —— 定义要采集的字段
import scrapy
class NewsItem(scrapy.Item):
title = scrapy.Field() # 标题
link = scrapy.Field() # 链接
time = scrapy.Field() # 发布时间
# spiders/news.py —— 解析规则
import scrapy
from demo.items import NewsItem
class NewsSpider(scrapy.Spider):
name = "news"
start_urls = ["https://www.example.com/news/index.html"]
def parse(self, response):
# response.xpath() 直接使用,无需额外导入
for li in response.xpath('//ul[@class="news"]/li'):
item = NewsItem()
item["title"] = li.xpath("a/text()").get()
item["link"] = li.xpath("a/@href").get()
item["time"] = li.xpath("span/text()").get()
yield item| 代码 | 白话解释 |
|---|---|
| import scrapy | 导入 Scrapy 框架,使用它提供的 Item、Field 等功能。 |
| class NewsItem(scrapy.Item): | 定义一个"数据模板"类 NewsItem,它继承 scrapy.Item——相当于声明"我要采集的数据表长什么样"。 |
| title = scrapy.Field() | 给模板添加一个字段 title。Field() 就是"一个字段位",可以理解成表格里的一列,后面按名字存取数据。 |
| name = "news" | 爬虫的名字,运行命令 scrapy crawl news 时用的就是它,必须唯一。 |
| start_urls = ["..."] | 爬虫启动后第一个要访问的网址列表,从这个页面开始抓。 |
| def parse(self, response): | 每个请求的响应都会自动交给 parse 方法处理,response 里装着网页内容,是"默认入口"。 |
| for li in response.xpath('//ul[@class="news"]/li'): | 用 XPath 找到所有新闻条目 li,逐个处理。 |
| item["title"] = li.xpath("a/text()").get() | 用 .get() 取第一条匹配的文本,存入 item 的 title 字段;没取到时返回 None。 |
| yield item | yield 是"产出":把当前这条数据交给 Scrapy 框架(去管道/存储),然后继续处理下一条。可以理解成"分批交货"。 |
实例 2:命令行输出 JSON 存储
目标:不写存储代码,直接用 scrapy crawl 命令把数据导出为 JSON / CSV 文件。
# 运行爬虫并输出 JSON 文件
[root@master demo]# scrapy crawl news -o news.json
[root@master demo]# cat news.json
[
{"title": "学校召开教学改革会议", "link": "/news/1.html", "time": "2023-02-10"},
{"title": "运动会圆满落幕", "link": "/news/2.html", "time": "2023-02-11"},
{"title": "新图书馆正式开放", "link": "/news/3.html", "time": "2023-02-12"}
]
# 也可以输出 CSV / JSON Lines 格式
[root@master demo]# scrapy crawl news -o news.csv
[root@master demo]# scrapy crawl news -o news.jl| 命令 | 白话解释 |
|---|---|
| scrapy crawl news | 启动爬虫。crawl 是"爬取"命令,news 是爬虫名字(对应 Spider 里 name = "news")。 |
| -o news.json | output 的缩写:把爬到的数据写入 news.json 文件。Scrapy 会根据扩展名自动选择格式。 |
| [{"title": ..., "link": ..., "time": ...}] | json 文件里是一个数组,每个元素是一条新闻数据,字段与 Item 里定义的一一对应。 |
| -o news.csv | 换成 csv 扩展名,数据就保存成表格文件,Excel 可直接打开。 |
| -o news.jl | jl 是 JSON Lines:每行一条 JSON,方便逐条读取,适合大数据量。 |
实例 3:Pipeline 去重与入库
目标:编写管道类完成"去重 → 清洗 → 写入数据库",并在 settings.py 中启用。
# pipelines.py
import sqlite3
class NewsPipeline:
def open_spider(self, spider):
# 爬虫启动时建立数据库连接
self.conn = sqlite3.connect("news.db")
self.conn.execute("CREATE TABLE IF NOT EXISTS news("
"title TEXT, link TEXT PRIMARY KEY, time TEXT)")
def process_item(self, item, spider):
# 1. 去重:link 为主键,重复时数据库会拒绝写入
# 2. 清洗:空标题替换为"未知"
if not item.get("title"):
item["title"] = "未知"
self.conn.execute(
"INSERT OR IGNORE INTO news(title, link, time) VALUES(?,?,?)",
(item["title"], item["link"], item["time"]))
self.conn.commit()
return item
def close_spider(self, spider):
self.conn.close()
# settings.py —— 开启管道
ITEM_PIPELINES = {
"demo.pipelines.NewsPipeline": 300,
}| 代码 | 白话解释 |
|---|---|
| class NewsPipeline: | 定义管道类。管道是 Scrapy 的"数据加工车间",负责清洗、去重、存储等收尾工作。 |
| def open_spider(self, spider): | 爬虫启动时自动调用一次,通常在这里"开店前准备":连接数据库、建表等。 |
| self.conn = sqlite3.connect("news.db") | 连接(或创建)SQLite 数据库文件 news.db,self.conn 是连接句柄,类内其他方法都能用。 |
| CREATE TABLE IF NOT EXISTS news(...) | 建表语句:如果表不存在就创建,字段有 title、link(主键)、time。主键 PRIMARY KEY 保证不重复。 |
| def process_item(self, item, spider): | 每来一条数据都会调用这个方法,item 就是当前这条数据,处理完要 return 给下一个管道。 |
| if not item.get("title"): item["title"] = "未知" | 数据清洗:如果标题为空,补成"未知",保证入库的数据规整。 |
| INSERT OR IGNORE INTO news ... | 插入数据;如果 link 主键已存在(重复数据),IGNORE 让数据库忽略这次插入——天然的"去重"。 |
| def close_spider(self, spider): | 爬虫结束时自动调用,通常在这里"关店收尾":关闭数据库连接,释放资源。 |
| ITEM_PIPELINES = {"demo.pipelines.NewsPipeline": 300} | 在 settings.py 中"注册"管道并设置优先级 300。数值越小越先执行;写了这行,管道才会真正生效。 |