scrapy介绍
上面的这张图是Scrapy的整个结构。你可以把整个Scrapy框架看成是一家爬虫公司。最中心位置的Scrapy Engine(引擎)就是这家爬虫公司的大boss,负责统筹公司的4大部门,每个部门都只听从它的命令,并只向它汇报工作。
我会以爬虫流程的顺序来依次跟你介绍Scrapy爬虫公司的4大部门。
Scheduler(调度器)部门主要负责处理引擎发送过来的requests对象(即网页请求的相关信息集合,包括params,data,cookies,request headers…等),会把请求的url以有序的方式排列成队,并等待引擎来提取(功能上类似于gevent库的queue模块)。
Downloader(下载器)部门则是负责处理引擎发送过来的requests,进行网页爬取,并将返回的response(爬取到的内容)交给引擎。它对应的是爬虫流程【获取数据】这一步。
Spiders(爬虫)部门是公司的核心业务部门,主要任务是创建requests对象和接受引擎发送过来的response(Downloader部门爬取到的内容),从中解析并提取出有用的数据。它对应的是爬虫流程【解析数据】和【提取数据】这两步。
Item Pipeline(数据管道)部门则是公司的数据部门,只负责存储和处理Spiders部门提取到的有用数据。这个对应的是爬虫流程【存储数据】这一步。
Downloader Middlewares(下载中间件)的工作相当于下载器部门的秘书,比如会提前对引擎大boss发送的诸多requests做出处理。
Spider Middlewares(爬虫中间件)的工作则相当于爬虫部门的秘书,比如会提前接收并处理引擎大boss发送来的response,过滤掉一些重复无用的东西。
运行逻辑
上图展现出的也是Scrapy框架的工作原理——引擎是中心,其他组成部分由引擎调度。
在Scrapy里,整个爬虫程序的流程都不需要我们去操心,且Scrapy中的程序全部都是异步模式,所有的请求或返回的响应都由引擎自动分配去处理。
哪怕有某个请求出现异常,程序也会做异常处理,跳过报错的请求,继续往下运行程序。
在一定程度上,Scrapy可以说是非常让人省心的一套爬虫框架。
实操部分
创建
在电脑端打开cmd,输入cd +目标文件,例如本地电脑,想把资料存在C:\Users\MI\Desktop\python学习\python爬虫学习内容\13_scrapy1,
就输入cd C:\Users\MI\Desktop\python学习\python爬虫学习内容\13_scrapy1
cmd会到达目标地址
然后,再输入一行能帮我们创建Scrapy项目的命令:scrapy startproject douban,douban就是Scrapy项目的名字。按下enter键,一个Scrapy项目就创建成功了。
整个scrapy项目的结构,如下图所示:
Scrapy项目里每个文件都有特定的功能,比如settings.py 是scrapy里的各种设置。items.py是用来定义数据的,pipelines.py是用来处理数据的,它们对应的就是Scrapy的结构中的Item Pipeline(数据管道)。
如前所述,spiders是放置爬虫的目录。我们可以在spiders这个文件夹里创建爬虫文件。我们来把这个文件,命名为top250。后面的大部分代码都需要在这个top250.py文件里编写。
既然top250是爬虫内的,那么自然要导入scrapy bs4这种类型的库.
导入scrapy是待会我们要用创建类的方式写这个爬虫,我们所创建的类将直接继承scrapy中的scrapy.Spider类。这样,有许多好用属性和方法,就能够直接使用。
开始编写爬虫核心代码
在Scrapy中,每个爬虫的代码结构基本都如下所示:
class DoubanSpider(scrapy.Spider): #继承scrapy.spider的类DoubanSpider
name = 'douban' #name是定义爬虫的名字,这个名字是爬虫的唯一标识。意思是定义爬虫的名字为douban等会我们启动爬虫的时候,要用到这个名字。
allowed_domains = ['https://book.douban.com'] #定义允许爬虫爬取的网址域名。如果网址的域名不在这个列表里,就会被过滤掉。
start_urls = ['https://book.douban.com/top250?start=0'] #定义起始网址,就是爬虫从哪个网址开始抓取。在此,allowed_domains的设定对start_urls里的网址不会有影响。
def parse(self, response): #parse是Scrapy里默认处理response的一个方法,中文是解析。具体说就是类似平时使用的bs4处理数据,然后从网页提取数据,存数据进列表的形式,只不过这些都被定义为了parse这个函数。所以叫做处理response的方法。
print(response.text)
为什么会有这个设置呢?当你在爬取大量数据时,经常是从一个URL开始爬取,然后关联爬取更多的网页。比如,假设我们今天的爬虫目标不是爬书籍信息,而是要爬豆瓣top250,每本书的书评。我们会先爬取书单,再找到每本书的URL,再进入每本书的详情页面去抓取评论。
allowed_domains就限制了,我们这种关联爬取的URL,一定是https://book.douban.com,不会跳转到某个奇怪的广告页面。
你或许会好奇,这里是不是少了一句类似requests.get()这样的代码?的确是,在这里,我们并不需要写这一句。scrapy框架会为我们代劳做这件事,写好你的请求,接下来你就可以直接写对响应如何做处理,我会在后面为你做示例。
当爬取多个网址时,可以用for循环和append来输入
start_urls = []
for x in range(3):
url = 'https://book.douban.com/top250?start=' + str(x * 25)
start_urls.append(url)
我们只先爬取豆瓣Top250前3页的书籍信息。
接下来,只要再借助parse方法处理response,借助BeautifulSoup来取出我们想要的书籍信息的数据,代码即可完成。
按照过去,我们会把书名、出版信息、评分,分别赋值,然后统一做处理——或是打印,或是存储。但在scrapy这里,事情却有所不同。
spiders(如top250.py)只干spiders应该做的事。对数据的后续处理,另有人负责。
代码实现1——定义数据
在scrapy中,我们会专门定义一个用于记录数据的类。
定义这个类的py文件,正是items.py。(即 下面是需要在这个文件内输入的代码)
我们已经知道,我们要爬取的数据是书名、出版信息和评分,我们来看看如何在items.py里定义这些数据。代码如下:
import scrapy
#导入scrapy
class DoubanItem(scrapy.Item):
#定义一个类DoubanItem,它继承自scrapy.Item
title = scrapy.Field()
#定义书名的数据属性
publish = scrapy.Field()
#定义出版信息的数据属性
score = scrapy.Field()
#定义评分的数据属性
第5、7、9行代码:我们定义了书名、出版信息和评分三种数据。scrapy.Field()这行代码实现的是,让数据能以类似字典的形式记录。但记录的方式却不是字典,是是我们定义的DoubanItem,属于“自定义的Python字典”。
可以看到,其实parse的作用是处理response的数据,然后存在定义的item的对象DoubanItem()内。
那么优化代码如下
import scrapy
import bs4
from ..items import DoubanItem
# 需要引用DoubanItem,它在items里面。因为是items在top250.py的上一级目录,所以要用..items,这是一个固定用法。
class DoubanSpider(scrapy.Spider):
#定义一个爬虫类DoubanSpider。
name = 'douban'
#定义爬虫的名字为douban。
allowed_domains = ['https://book.douban.com']
#定义爬虫爬取网址的域名。
start_urls = []
#定义起始网址。
for x in range(3):
url = 'https://book.douban.com/top250?start=' + str(x * 25)
start_urls.append(url)
#把豆瓣Top250图书的前3页网址添加进start_urls。
def parse(self, response):
#parse是默认处理response的方法。
bs = bs4.BeautifulSoup(response.text,'html.parser')
#用BeautifulSoup解析response。
datas = bs.find_all('tr',class_="item")
#用find_all提取<tr class="item">元素,这个元素里含有书籍信息。
for data in datas:
#遍历data。
item = DoubanItem()
#实例化DoubanItem这个类。
item['title'] = data.find_all('a')[1]['title']
#提取出书名,并把这个数据放回DoubanItem类的title属性里。
item['publish'] = data.find('p',class_='pl').text
#提取出出版信息,并把这个数据放回DoubanItem类的publish里。
item['score'] = data.find('span',class_='rating_nums').text
#提取出评分,并把这个数据放回DoubanItem类的score属性里。
print(item['title'])
#打印书名。
yield item
#yield item是把获得的item传递给引擎
每一次,当数据完成记录,它会离开spiders,来到Scrapy Engine(引擎),引擎将它送入Item Pipeline(数据管道)处理。这里,要用到yield语句。
yield语句你可能还不太了解,这里你可以简单理解为:它有点类似return,不过它和return不同的点在于,它不会结束函数,且能多次返回信息。
截止至目前,代码应该是可以实现了,但是还需要一些设置。
原因在于Scrapy里的默认设置没被修改。比如我们需要修改请求头。点击settings.py文件,你能在里面找到如下的默认设置代码:
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'douban (+http://www.yourdomain.com)'
# Obey robots.txt rules
ROBOTSTXT_OBEY = True
修改 USER AGENT。删掉注释#,然后替换掉user agent的内容,即修改请求头。请求头如下
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
又因为Scrapy是遵守robots协议的,如果是robots协议禁止爬取的内容,Scrapy也会默认不去爬取,所以我们还得修改Scrapy中的默认设置。
把ROBOTSTXT_OBEY=True改成ROBOTSTXT_OBEY=False,就是把遵守robots协议换成无需遵从robots协议,这样Scrapy就能不受限制地运行。
到这里,我们已经做好了scrapy的设置,但还差一布,如何运行。
如果你直接运行代码,仍然会存在报错, 原因是scrapy的运行方法和平时我们运行代码的方式不同,有两个方法。
1.在本地电脑的终端跳转到scrapy项目的文件夹(跳转方法:cd+文件夹的路径名),然后输入命令行:scrapy crawl douban(douban 就是我们爬虫的名字)。
2.另一种运行方式需要我们在最外层的大文件夹里新建一个main.py文件(与scrapy.cfg同级)。
我们只需要在这个main.py文件里,输入以下代码,点击运行,Scrapy的程序就会启动。
from scrapy import cmdline
#导入cmdline模块,可以实现控制终端命令行。
cmdline.execute(['scrapy','crawl','douban'])
#用execute()方法,输入运行scrapy的命令。
第1行代码:在Scrapy中有一个可以控制终端命令的模块cmdline。导入了这个模块,我们就能操控终端。
第3行代码:在cmdline模块中,有一个execute方法能执行终端的命令行,不过这个方法需要传入列表的参数。我们想输入运行Scrapy的代码scrapy crawl douban,就需要写成[‘scrapy’,‘crawl’,‘douban’]这样。
至此结束scrapy的学习。
目前还没有学到存储内容的方法。往后接着学习。
本文的顺序与实际操作顺序有所出入。实际应该是