第5章Scrapy 框架

视频学习

scrapy环境简介视频

http://home.hddly.cn:50090/media/chrome_IWlr5MZVSJ.mp4

创建scrapy项目视频

http://home.hddly.cn:50090/media/chrome_oOCFZ6Y8IT.mp4

在云课上完成scrapy用户采集

http://home.hddly.cn:50090/media/TiSsnUj7Xt.mp4

在本地上完成scrapy用户采集

http://home.hddly.cn:50090/media/yC4w8sHV4d.mp4

5.1 Scrapy 框架的介绍和安装

【练一练】Scrapy 爬虫框架简介与基本操作

下面我们使用 scrapy 写一个爬虫脚本,爬取蓝桥课程页面所有课程名称、简介、类型和学习人数信息,并保存为 JSON 文本。课程页面的地址是 https://www.lanqiao.cn/courses/

/home/shiyanlou/Code 下新建 shiyanlou_courses_spider.py 文件,写入 Scrapy 爬虫的基本结构:

分析蓝桥的课程页面可以看出,每页有 4 x 5 行共 20 个课程卡片,我们需要从中提取 20 条数据,爬取前 3 页,共计 60 条数据,点击页面底部的「下一页」按钮进入下一页,将浏览器地址栏中的地址复制出来:

这样就可以写出 start_requests 方法:

Scrapy 内部的下载器会下载每个 Request,然后将结果封装为 response 对象传入 parse 方法,这个对象和前面 scrapy shell 练习中的对象是一样的,也就是说你可以用 response.css() 或者 response.xpath() 来提取数据了。

通过分析蓝桥课程页面的文档结构,以《Linux 入门基础(新版)》课程为例,我们需要提取的数据主要包含在下面的 div 里面:

图片描述

根据这个 div 可以用提取器写出 parse 方法:

完整源码

 

运行

按照上一步中的格式写好 spider 后,就能使用 scrapy 的 runspider 命令来运行爬虫了。

注意这里输出得到的 data.json 文件中的中文显示成 unicode 编码的形式,所以看到感觉像是乱码,其实是正常的。

-o 参数表示打开一个文件,scrapy 默认会将结果序列化为 JSON 格式写入其中。爬虫运行完后,在当前目录打开 data.json 文件就能看到爬取到的数据了。

5.2 连接数据库的标准 Scrapy 项目

【练一练】连接数据库的标准 Scrapy 项目

介绍

上一节中,我们只是基于 Scrapy 写了一个爬虫脚本,并没有使用 Scrapy 项目标准的形式。这一节我们要将脚本变成标准 Scrapy 项目的形式,并将爬取到的数据存储到 MySQL 数据库中。数据库的连接和操作使用 SQLAlchemy。

知识点

连接数据库准备

本实验会将爬取的数据存入 MySQL,需要做一些准备工作。首先需要将 MySQL 的编码格式设置为 utf8,编辑配置文件:

检查以下几个配置是否存在,有的话则可以不修改:

保存后,就可以启动 mysql 了:

以 root 身份进入 mysql,实验环境默认是没有密码的:

创建 shiyanlou 库给本实验使用:

完成后输入 quit 退出。

本实验使用 SQLAlchemy 这个 ORM 在爬虫程序中连接和操作 MySQL ,先安装一下sqlalchemy,还需要安装 Python3 连接 MySQL 的驱动程序 mysqlclient

 

创建项目

进入到 /home/shiyanlou/Code 目录,使用 Scrapy 提供的 startproject 命令创建一个 Scrapy 项目,需要提供一个项目名称,我们要爬取蓝桥的数据,所以将 shiyanlou 作为项目名:

进入 /home/shiyanlou/Code/shiyanlou 目录,可以看到项目结构是这样的:

创建爬虫

Scrapy 的 genspider 命令可以快速初始化一个爬虫模版,使用方法如下:

其中 name 参数是这个爬虫的名称,domain 指定要爬取的网站。

进入第二个 shiyanlou 目录,运行下面的命令快速初始化一个爬虫模版:

Scrapy 会在 /home/shiyanlou/Code/shiyanlou/shiyanlou/spiders 目录下新建一个 courses.py 文件,并且在文件中初始化了代码结构:

这里面有一个新的属性 allowed_domains 是在前一节中没有介绍到的。它是干嘛的呢?allow_domains 的值可以是一个列表或字符串,包含这个爬虫可以爬取的域名。假设我们要爬的页面是 https://www.example.com/1.html ,那么就把 example.com 添加到 allowed_domains。这个属性是可选的,在我们的项目中并不需要使用它,可以删除。

除此之外 start_urls 的代码和上一节相同:

 

Item

爬虫的主要目标是从网页中提取结构化的信息,Scrapy 爬虫可以将爬取到的数据作为一个 Python dict 返回,但由于 dict 的无序性,所以它不太适合存放结构性数据。Scrapy 推荐使用 Item 容器来存放爬取到的数据。

所有的 items 写在 items.py/home/shiyanlou/Code/shiyanlou/shiyanlou/items.py) 中,下面为要爬取的课程定义一个 Item

有了 CourseItem,就可以将 parse 方法的返回包装成它:

 

Item Pipeline

如果把 scrapy 想象成一个产品线,spider 负责从网页上爬取数据,Item 相当于一个包装盒,对爬取的数据进行标准化包装,然后把它们扔到 Pipeline 流水线中。

主要在 PipelineItem 进行这几项处理:

当创建项目时,scrapy 已经在 /home/shiyanlou/Code/shiyanlou/shiyanlou/pipelines.py 中为项目生成了一个 pipline 模版:

除了 process_item 还有两个常用的 hooks 方法,open_spiderclose_spider

定义 Model,创建表

 

items.py 所在目录下创建 models.py/home/shiyanlou/Code/shiyanlou/shiyanlou/models.py),在里面使用 SQLAlchemy 语法定义 courses 表结构:

创建文件

源码

使用 SQLAlchemy 创建映射类 Course 这步操作在前面的实验《关系数据库 MySQL 和 ORM》中讲到了,大家可以去查阅相关文档,也可以通过《SQLAlchemy 基础教程》 了解更多相关知识。

运行程序:

如果运行正确的话,程序什么都不会输出,执行完后,进入 MySQL 客户端中检查是否已经创建了表:

如果出现类似上面的东西说明表已经创建成功了!

注意,如果遇到 MySQLdb 包没有找到的错误,有以下几种可能,依次排查下就可以了:

  1. mysqlclient 没有安装
  2. mysqlclient 被 sudo pip3 install 安装到了系统路径,但在 virtualenv 里执行的 scrapy 没有找到这个包
  3. mysqlclient 被 pip3 install 安装到了 virtualenv,但没有激活 virtualenv 执行的 scrapy 没有找到这个包
  4. mysqlclient 被 pip3 install 安装到了 virtualenv,但没有在 virtualenv 安装 scrapy,执行 scrapy 的时候用的是系统的 scrapy(which scrapy 可以查看执行的路径)
  5. mysqlclient 和 scrapy 被 pip3 install 安装到了 virtualenv,但安装完成后没有 deactivate 再次重新激活 virtualenv,执行 scrapy 的时候用的是系统的 scrapy(which scrapy 可以查看执行的路径

保存 item 到数据库

创建好数据表后,就可以在 pipelines.py 编写代码将爬取到的每个 item 存入数据库中。

我们编写的这个 ShiyanlouPipeline 默认是关闭的状态,要开启它,需要在 /home/shiyanlou/Code/shiyanlou/shiyanlou/settings.py 将下面的代码取消注释:

ITEM_PIPELINES 里面配置需要开启的 pipeline,它是一个字典,key 表示 pipeline 的位置,值是一个数字,表示的是当开启多个 pipeline 时它的执行顺序,值小的先执行,这个值通常设在 100~1000 之间

运行

前面使用的 runspider 命令用于启动一个独立的 scrapy 爬虫脚本,在 scrapy 项目中启动爬虫使用 crawl 命令,需要指定爬虫的 name

爬虫运行完后,进入 MySQL,输入下面的命令查看爬取数据的前 3 个:

此处输入图片的描述

因为 Scrapy 爬虫是异步执行的,所以爬取到的 course 顺序和蓝桥网站上的会不一样

随堂练习截图

在云课中完成《连接数据库的标准 Scrapy 项目》,然后截图。截图中包含cources.py脚本,items.py脚本,和item pipline脚本,运行脚本和mysql查询数据脚本

5.3 Scrapy 爬取蓝桥用户数据

【练一练】Scrapy 爬取蓝桥用户数据

简介

本节内容运用前两节学到的知识,爬取蓝桥的用户数据,主要是为了练习、巩固前面学习到的知识。

知识点

要爬取的内容

下面是一个用户主页的截图,箭头指的是我们要爬取的内容:

此处输入图片的描述

要爬取的内容和字段名称定义:

 

定义数据模型

意:本节实验的操作需要使用上一节 scrapy 创建的 shiyanlou 项目的代码,代码目录为 /home/shiyanlou/Code/shiyanlou

环境准备

决定好了要爬取的内容,就可以使用 SQLAlchemy 定义数据模型了,在上一节实验中创建的 /home/shiyanlou/Code/shiyanlou/shiyanlou/models.py 中的 Course 后面定义 User 模型:

使用桌面文件管理工具,找到/home/shiyanlou/Code/shiyanlou/shiyanlou/models.py ,右击,使用vscode打开文件,使用右侧剪切板工具,粘贴如下内容到models.py文件中:

使用终端工具,运行脚本在数据库中创建 users 表了:

创建数据库

以 root 身份进入 mysql,实验环境默认是没有密码的:

创建 shiyanlou 库给本实验使用:

安装依赖包

SQLAlchemy 默认不会重新创建已经存在的表,所以不用担心 create_all 会重新创建 course 表造成数据丢失;

运行完成后验证表是否创建

结果如下,如果有courses和users表,说明创建表的脚本执行成功:

创建 Item

/home/shiyanlou/Code/shiyanlou/shiyanlou/items.py 中添加 UserItem,为每个要爬取的字段声明一个 Field:

使用桌面浏览器,使用vscode打开items.py进行修改

 

在文件末尾添加UserItem类,文件结果如下:

如图示:

创建爬虫

使用 genspider 命令创建 users 爬虫:

如图示:

使用vscode打开/home/shiyanlou/Code/shiyanlou/shiyanlou/目录下的:users.py

Scrapy 为我们在 spiders 下面创建 users.py 爬虫,将它修改如下:

解析数据

解析数据主要是编写 parse 函数。在实际编写前,最好是用 scrapy shell 对某一个用户进行测试,将正确的提取代码复制到 parse 函数中。

下面的几个例子是 “需要提取的某用户数据在页面源码中的结构” 和对应的提取器(提取器有很多种写法,可以用自己的方式去写)

如下图所示,会员用户头像右下角有一个会员标志,它是一张图片。在 div 标签下,非会员用户只有一个 img 标签在 a 标签内部,会员用户有两个 img 标签,我们可以根据 img 的数量来判断用户类型:

图片描述

依次在 scrapy shell 中测试每个要爬取的数据,最后将代码整合进 users.py 中如下:

pipeline

因为 pipeline 会作用在每个 item 上,当和课程爬虫共存时,需要根据 item 类型使用不同的处理函数。

最终代码文件 /home/shiyanlou/Code/shiyanlou/shiyanlou/pipelines.py

运行

使用 crawl 命令启动爬虫:

总结

实验设计了一个新的实例,爬取蓝桥的用户页面,在这个页面中首先需要分析页面中的各种元素,从而设计爬虫中数据提取的方式。然后把需要的数据内容通过 Scrapy 项目中的代码获取得到并解析出来,存储到数据库中。

本节实验包含以下的知识点:

 

【本地实验】Scrapy 爬取蓝桥用户数据

简介

本节实验的操作在笔记本上,数据库使用远程数据库,暂使用内网地址:10.0.10.158,端口:3306

环境准备

使用pycharm打开目录d:\myname\shiyanlou\

image-20240514111647792

修改D:\myname\shiyanlou\shiyanlou\models.py 文件,内容如下:

安装依赖包

 

修改 Items.py

items.py中添加UserItem,每个Item需要包含stucode,collector,coll_time信息,文件结果如下:

创建爬虫users.py

使用 genspider 命令创建 users 爬虫:

修改 users.py 中如下:

修改pipelines.py

因为 pipeline 会作用在每个 item 上,当和课程爬虫共存时,需要根据 item 类型使用不同的处理函数。

最终代码文件 pipelines.py:

需要修改open_spider方法中的stucode和collector值,请换成本人学号和姓名

修改settings.py

修改settings文件开启ITEM_PIPELINES,在文件末尾添加如下:

运行爬虫

使用 crawl 命令启动爬虫:

在IDE上运行

修改D:\myname\shiyanlou\shiyanlou__init__.py,内容如下,然后可右击该文件运行或调试

随堂练习

练习内容:

在本地完成Scrapy 爬取蓝桥用户数据抓取 ,步骤包括环境准备、创建scrapy项目、修改items.py、创建爬虫users.py、创建爬虫users.py、修改settings.py、运行爬虫等,完成以上内容并截图

截图要求:

  1. 截图在云课环境上的运行截图,只需要scrapy crawl users运行截图
  2. 截图pycharm的源码,包括users.py,items.py,pipelines.py,settings.py新增的内容
  3. 截图在本机环境上运行scrapy crawl users的截图,截图中包含有已采集的数据和本人姓名
  4. 截图已采集数据量的截图,进入学生登陆-》统计积分-》采集统计,在mysql统计中选择会员信息统计

 

 

5.4 Scrapy 高级应用

【练一练】Scrapy 高级应用

实验简介

本节内容主要介绍使用 Scrapy 进阶的知识和技巧,包括页面追随、图片下载、组成 item 的数据在多个页面和模拟登录。

知识点

页面跟随

在前面实现课程爬虫和用户爬虫中,因为蓝桥的课程和用户 URL 都是通过 id 来构造的,所以可以轻松构造一批顺序的 URLS 给爬虫。但是在很多网站中,URL 并不是轻松可以构造的,更常用的方法是从一个或者多个链接(start_urls)爬取页面后,再从页面中解析需要的链接继续爬取,不断循环。

下面是一个简单的例子,在蓝桥课程编号为 63 的课程主页,从页面底部相关课程推荐来获取下一批要爬取的课程的 URL ,我们的任务是爬取该课程和所有推荐课程的名字和作者。

图片描述

结合前面所学的知识,你可能会写出类似这样的代码:

完成页面跟随的核心就是最后 for 循环的代码。使用 response.follow 方法可以对 for 循环代码做进一步简化:

图片下载

scrapy 内部内置了下载图片的 pipeline。下面以下载蓝桥课程首页每个课程的封面图片为例展示怎么使用它。注意项目的路径需要放置在 /home/shiyanlou/Code/ 下,命名为 shiyanlou。

可以新创建一个项目,也可以继续使用上一节实验的 scrapy 项目代码。

首先需要在 /home/shiyanlou/Code/shiyanlou/shiyanlou/items.py 中定义一个 item ,它包含两个必要的字段:

运行 scrapy genspider courses_image lanqiao.cn/courses 生成一个爬虫,爬虫的核心工作就是解析所有图片的链接到 CourseImageItem 的 image_urls 中。 将以下代码写入 /home/shiyanlou/Code/shiyanlou/shiyanlou/spiders/courses_image.py 文件中:

代码完成后需要在 settings.py 中启动 scrapy 内置的图片下载 pipeline,因为 ITEM_PIPELINES 里的 pipelines 会按顺序作用在每个 item 上,而我们不需要 ShiyanlouPipeline 作用在图片 item 上,所以要把它注释掉:

还需要配置图片存储的目录:

运行程序:

scrapy 会将图片下载到 images/full 下面,保存的文件名是对原文件进行的 hash。为什么会有一个 full 目录呢?full 目录用于存储原尺寸的图片,因为 scrapy 可以配置改变下载图片的尺寸,比如在 settings 中给你添加下面的配置生成小图片:

组成 item 的数据在多个页面

在前面几节实现的爬虫中,组成 item 的数据全部都是在一个页面中获取的。但是在实际的爬虫项目中,经常需要从不同的页面抓取数据组成一个 item。下面通过一个例子展示如何处理这种情况。

有一个需求,爬取蓝桥课程首页所有课程的名称、封面图片链接和课程作者。课程名称和封面图片链接在课程主页 https://www.shiyanlou.cn/courses/ 就能爬到,课程作者只有点击课程,进入课程详情页面才能看到,怎么办呢?

scrapy 的解决方案是多级 request 与 parse 。简单地说就是先请求课程首页,在回调函数 parse 中解析出课程名称和课程图片链接,然后在 parse 函数中再构造一个请求到课程详情页面,在处理课程详情页的回调函数中解析出课程作者。

首先在 items.py 中创建相应的 Item 类:

终端执行如下命令生成一个爬虫脚本:

操作截图:

图片描述

如上图所示,打开 multipage.py 文件并修改代码如下:

关闭所有的 pipeline,运行爬虫,保存结果到文件中:

这部分的知识点不容易理解,可以参考下先前同学的一个提问来理解 https://www.lanqiao.cn/questions/50921/

模拟登录

有些网页需要登录后才能访问,例如任何网站的用户个人主页。有些网页中的部分内容需要登录后才能看到,例如 GitHub 中的私有仓库。

图片描述

如果想要爬取登录后才能看到的内容就需要 Scrapy 模拟出登录的状态再去抓取页面,解析数据。这个实验就是要模拟登录自己的主页。

通常网站都会有一个 login 页面,GitHub 的 login 页面网址是:https://github.com/login。在浏览器的地址栏输入这个地址敲回车:

图片描述

鼠标右键选择“检查”,打开开发者工具栏,选择 Network

图片描述

在页面的登录表单中输入错误的用户名和密码,点击“登录”按钮。如下图所示,右侧出现 session 请求,点击此文件,出现请求和响应信息:

图片描述

点击登录按钮后,会有一个 POST 请求发送给服务器。在 Form Data 中可以看到本次提交的内容。

其中红色框中的 token 字段是关键信息,为了防止跨域伪造攻击,网站通常会在表单中添加一个隐藏域来放置这个 token 字段。当浏览器发送携带表单的 POST 请求时,服务器收到请求后会比对表单中的 token 字段与 Cookies 中的信息以判断请求来源的可靠性。

所以我们在发送 POST 登录请求时,就要携带这个 token 字段。

如何获取这个字段呢?很简单,在登录页的源码上就会有。在开发者工具栏中打开页面源码,搜索 "token" 关键字即可:

图片描述

也就是 form 标签下的第一个 input 标签的 value 属性值。

要获取 token 值,就要发送一次请求。将如下代码写入 /home/shiyanlou/github_login.py 文件中:

以上代码会向 GitHub 发送一次登录请求,我们可以通过响应对象获取隐藏在表单中的 token 字段。

针对本次模拟登录的学习,我们要安装 Scrapy 2.1 版本:

在执行 scrapy runspider 命令时,可以使用 -L 选项设置打印信息的级别。为了避免多余的普通信息出现在屏幕上,我们设置打印级别为 ERROR ,也就是只打印错误信息。

现在在终端执行如下命令:

此命令执行顺利的话,会打印 token 字段到屏幕上。操作截图如下:

图片描述

有了 token 字段,就可以构造 POST 登录请求了。这次请求与前一次的地址相同,请求方法不同。

这种情况,我们可以使用 scrapy.FormRequest.from_response 方法构造 POST 请求。

修改 github_login.py 文件如下:

再次执行程序:

图片描述

这样就登录成功了。现在可以爬取你自己的私有仓库和个人主页。

*注:由于原网页随时会发生变化,所以爬虫代码往往是即时代码,有效期由每个网站决定,主要看思路和框架

总结

本节内容主要通过蓝桥用户爬虫的代码实例介绍如何使用 Scrapy 进阶的知识和技巧,包括页面追随,图片下载, 组成 item 的数据在多个页面,模拟登录等。

本节实验中涉及到的知识点:

完成本周的学习之后,我们再次根据脑图的知识点进行回顾,让动手实践过程中学习到的知识点建立更加清晰的体系。

请点击以下链接回顾本周的 Scrapy 爬虫框架的学习:

请注意实验只会包含常用的知识点,对于未涉及到的知识点,如果在脑图中看到可以查看 Scrapy 官方文档获得详细说明,也非常欢迎在讨论组里与同学和助教进行讨论,技术交流也是学习成长的必经之路