前面的关卡中,我们学习了如何用协程来提升爬虫的速度,并且通过项目实操,将协程运用于抓取HI运动的食物数据。
不知道你会不会有这样一种感觉:要写出一个完整的爬虫程序需要做很多琐碎的工作。比如,要针对不同的网站制定不同的解析方式;要导入不同功能的模块;还要编写各种爬取流程的代码。
我们在日常工作中会使用PPT模板来制作PPT。那么有没有一个现成的爬虫模板,让我们能够改之即用,也就是说对这个模板进行适当的修改,就能完成一个爬虫项目的开发呢?
这种模板在Python中还真的存在,只不过我们一般称之为框架。
就像PPT模板会帮你设置好背景、字体和颜色一样,一个爬虫框架里包含了能实现爬虫整个流程的各种模块,。
这一关,我们要学习的就是一个功能强大的爬虫框架——Scrapy。
在我们之前的课程中,我们需要不同模块实现不同的功能:使用requests模块进行请求、使用csv模块存储数据等。而在Scrapy里,你不需要这么做,因为很多爬虫需要涉及的功能,比如麻烦的异步,在Scrapy框架都自动实现了。
我们之前编写爬虫的方式,就相当于我们在自己装修一个毛坯房。需要自己去找施工队来「水电施工」「泥工砌墙贴砖」「木工吊顶打柜子」「油漆工刷乳胶漆」等等…而框架就相当于一个精装房,拎包入住!
上面的这张图是Scrapy的整个结构。你可以把整个Scrapy框架当成是你所在的部门。最中心位置的Scrapy Engine(引擎)就是你所在部门的大boss,负责统筹规划你这个部门的四大职能。
以爬虫流程的顺序来依次介绍Scrapy爬虫部门的4大职能:
Scheduler(调度器)主要负责处理引擎发送过来的requests对象(即网页请求的相关信息集合,包括params,data,cookies,request headers…等),会把请求的url以有序的方式排列成队,并等待引擎来提取(功能上类似于gevent库的queue模块)。
Downloader(下载器)则是负责处理引擎发送过来的requests,进行网页爬取,并将返回的response(爬取到的内容)交给引擎。它对应的是爬虫流程【获取数据】这一步。
Spiders(爬虫)的主要任务是创建requests对象和接受引擎发送过来的response(Downloader部门爬取到的内容),从中解析并提取出有用的数据。它对应的是爬虫流程【解析数据】和【提取数据】这两步。
Item Pipeline(数据管道)负责存储和处理Spiders提取到的有用数据。这个对应的是爬虫流程【存储数据】这一步。
Downloader Middlewares(下载中间件)的工作相当于下载器的助手,比如会提前对引擎发送的诸多requests做出处理。
Spider Middlewares(爬虫中间件)的工作则相当于爬虫的助手,比如会提前接收并处理引擎发送来的response,过滤掉一些重复无用的东西。
从这个图中可以看出 Scrapy 框架的工作原理,即是——引擎就是大Boss,统领其他成员协同工作。
在使用 Scrapy 的时候。我们不需要去关心爬虫的每个流程。并且Scrapy中的网络请求都是默认异步模式,请求和返回都会由引擎去自动分配处理。
如果某个请求出现异常,框架也会帮我们做异常处理,跳过这个异常的请求,继续去执行后面的程序。
所以说,我们使用 Scrapy 可以省掉很多的时间和工作!就像我们直接住进精装房一样!
现在,我们已经初步了解Scrapy的结构以及工作原理。接下来,为了熟悉Scrapy的用法,我们使用它来完成一个小项目——爬取豆瓣Top250图书。
我们还是来遵循「项目实现」的三步骤:、
我们在代码实现部分,重点讲解Scrapy的使用方法。
明确目标,让我们观察一下要爬取的网站:https://book.douban.com/top250。豆瓣Top250图书一共有10页,每页有25本书籍。
我们的初步目标是:先只爬取前三页书籍的信息,总共是 25 x 3 = 75 本,包含书名、出版信息和书籍评分。
那就是分析要爬取的网页结构。 既然我们要爬取书籍信息,我们就得先判断这些信息被存在了哪里。
右击打开“检查”工具,点开Network,刷新页面,然后点击第0个请求top250,看Response.
我们能在里面找到书名、出版信息,说明我们想要的书籍信息就藏在这个网址的HTML里。
https://book.douban.com/top250?start=25
我们可以看到,网址发生了变化,后面多了?start=25。现在的你,应该一眼就能猜到后面的数字应该是代表一页的25本书籍吧?
你可以翻到第3页,验证一下猜想是不是正确的。
事实证明,我们猜对了。每翻一页,网址后面的数字都会增加25,说明这个start的参数就是代表每页的25本书籍。
我们要爬取的网址的构造规律就出来了。只要改变?start=后面的数字(翻一页加25),我们就能得到每一页的网址。
找到了网址的构造规律之后,我们就可以重点来分析HTML的结构,看看等下怎么才能提取出我们想要的书籍信息。
还是是右击打开“检查”工具,点击Elements,再点击光标,把鼠标依次移到书名、出版信息、评分处,就能在HTML里找到这些书籍信息。如下图,《追风筝的人》的书籍信息就全部放在
标签里。我们得继续再找一个既方便我们提取,又能包含所有书籍信息的标签。
我们可以看看
标签下的元素,好像刚好都能满足我们的要求,既有class属性,又包含了书籍的信息。我们只要取出元素下元素的title属性的值、
元素、
页面分析完毕,接着进入代码实现的步骤,要开始写 Scrapy 的代码啦。
如果你想着自己本地的电脑使用Scrapy,需要提前安装好它。
- Windows:在终端输入命令:pip install scrapy;
- mac:在终端输入命令:pip3 install scrapy
首先,在本地电脑打开终端,然后跳转到你想要保存项目的目录下。
- windows:Win+R,输入cmd;
- mac:command+空格,搜索“终端”
假设你想跳转到D盘里名为Python文件夹中的scrapy_project子文件夹。
在mac系统中的终端里,也是使用cd命令来跳转到你要的文件夹位置
整个scrapy项目的结构,如下图所示:
Scrapy项目里每个文件都有它对应的具体功能。例如:
如前所述,spiders是放置爬虫的目录。我们可以在spiders这个文件夹里创建爬虫文件。
我们来把这个文件,命名为Book_douban_Top250。后面的大部分代码都需要在这个Book_douban_Top250.py文件里编写。
先在Book_douban_Top250.py文件里导入我们需要的模块。
import scrapy
import bs4
接着我们开始编写爬虫的核心代码。
在Scrapy中,每个爬虫的代码结构基本都如下所示:
import scrapy
import bs4
class DoubanSpider(scrapy.Spider):
name = 'book_douban'
allowed_domains = ['book.douban.com']
start_urls = ['https://book.douban.com/top250?start=0']
def parse(self, response):
print(response.text)
代码解释:
第1行代码:定义一个爬虫类DoubanSpider。就像我刚刚讲过的那样,DoubanSpider类继承自scrapy.Spider类。
第2行代码:name是定义爬虫的名字,这个名字是爬虫的唯一标识。name = 'book_douban’意思是定义爬虫的名字为book_douban。等会我们启动爬虫的时候,要用到这个名字。
第3行代码:allowed_domains是定义允许爬虫爬取的网址域名(不需要加https://)。如果网址的域名不在这个列表里,就会被过滤掉。
为什么会有这个设置呢?
当你在爬取大量数据时,经常是从一个URL开始爬取,然后关联爬取更多的网页。
假设我们在今天的爬虫目标是爬豆瓣top250每本书籍的书评信息,我们会先爬取书单,再找到每本书的URL,再进入每本书的详情页面去抓取评论。
allowed_domains就限制了我们这种关联爬取的URL,一定在book.douban.com这个域名之下,不会跳转到某个奇怪的广告页面。
第4行代码:start_urls是定义起始网址,就是爬虫从哪个网址开始抓取。在此,allowed_domains的设定对start_urls里的网址不会有影响。
第5行代码:parse是Scrapy里默认处理response的一个方法。
是不是觉得和之前自己手动写爬虫的完全不一样了呢?怎么连 requests.get() 都没有了呀?其实在框架里,我们并不需要写这一句。
scrapy框架会为我们代劳做这件事,写好你的请求,接下来你就可以直接写对 response 如何做处理了。
豆瓣Top250图书一共有10页,每一页的网址我们都知道。我们可以把10页网址都塞进start_urls的列表里。
我们可以利用豆瓣Top250图书的网址规律,用for循环构造出每个网址,再把网址添加进start_urls的列表里。
完善后的代码如下:
import