项目四 基于框架实现动态网页数据采集与存储

Scrapy 的工程结构、爬虫编写、items 定义、Pipeline 落库——从“脚本”升级到“工程”。

框架第7-8周Scrapy · Item · Pipeline10 段代码

本项目学习目标

  • 能创建 Scrapy 项目并解释各目录作用
  • 能写 Spider 并用 parse 提取数据
  • 能定义 Item、写 Pipeline 存入 CSV/数据库
  • 能在 settings 里配置并发、延时、请求头

本项目任务导览

01

基于框架实现动态网页数据采集与存储

使用 Scrapy 框架完成新闻公告页面的数据采集与存储,掌握框架化的爬虫开发流程。

  • 项目导言与学习目标
  • 任务 4-1 使用 Scrapy 框架完成新闻公告页面数据采集与存储
  • 知识拓展与实例案例(3 例)
项目导言与学习目标

从"手写爬虫"进阶到"框架爬虫"。

Scrapy 是 Python 的一个应用 Twisted 异步处理的第三方应用程序框架,用户只需要定制开发几个模块即可实现一个爬虫,用来快速爬取网站并从页面中抓取网页内容以及各种图片。

了解 Scrapy 相关概念; 熟悉 Scrapy 框架的设置; 掌握文本解析与内容存储; 精通内存存储逻辑。

具备项目创建的能力; 具备使用 Scrapy 操作命令创建项目的能力; 具备解析网页中文本的能力; 具备网页数据采集能力。

具有较好的团队精神; 具有高度的责任感; 具备很强的学习和沟通能力; 具有清晰思路、较强的逻辑思维能力。

03

任务 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 文件中代码进行字段的自定义操作即可明确爬取的目标。

PYTHON
# 语法格式一:使用 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,之后直接使用即可。

PYTHON
# 语法格式二:引入 Item 和 Field
# 导入scrapy的Item参数和Field方法
from scrapy import Item, Field
# 定义包含scrapy.Item参数的类
class ScrapynameItem(Item):
    # 自定义字段
    name = Field()
    # 通过提示
    pass

items.py 文件中包含的内容仅为字段的定义,还需在 MySpider.py 文件中通过类名称引入 items.py 文件中 class 定义的类并实例化字段对象,之后通过"对象[字段名称]=值"的方式进行赋值操作。

PYTHON
# 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() 方法应用:

PYTHON
# 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() 方法应用:

PYTHON
# 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

十、数据存储

SHELL
scrapy crawl 爬虫文件名称 path

在 Scrapy 中,可以使用 crawl 命令的"-o"参数,在运行 Scrapy 项目时将数据保存到本地 JSON、CSV 等文件中。

Scrapy 中管道的使用需要先在 settings.py 文件添加 ITEM_PIPELINES 参数,进行管道的启用。

PYTHON
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 中的数据,对数据存储是否成功进行验证。
04

知识拓展与实例案例

课堂讲解用:3 个可运行实例 + 知识拓展卡片。

知识拓展

实例 1:定义 Item 与 Spider 提取数据

目标:演示 Scrapy 项目中 Item 字段定义与 Spider 解析方法的完整结构。

PYTHON
# 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 itemyield 是"产出":把当前这条数据交给 Scrapy 框架(去管道/存储),然后继续处理下一条。可以理解成"分批交货"。

实例 2:命令行输出 JSON 存储

目标:不写存储代码,直接用 scrapy crawl 命令把数据导出为 JSON / CSV 文件。

PYTHON
# 运行爬虫并输出 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.jsonoutput 的缩写:把爬到的数据写入 news.json 文件。Scrapy 会根据扩展名自动选择格式。
[{"title": ..., "link": ..., "time": ...}]json 文件里是一个数组,每个元素是一条新闻数据,字段与 Item 里定义的一一对应。
-o news.csv换成 csv 扩展名,数据就保存成表格文件,Excel 可直接打开。
-o news.jljl 是 JSON Lines:每行一条 JSON,方便逐条读取,适合大数据量。

实例 3:Pipeline 去重与入库

目标:编写管道类完成"去重 → 清洗 → 写入数据库",并在 settings.py 中启用。

PYTHON
# 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。数值越小越先执行;写了这行,管道才会真正生效。