1 概述
在具体的学习scrapy之前,我们先对scrapy的架构做一个简单的了解,之后所有的内容都是基于此架构实现的,在初学阶段只需要简单的了解即可,之后的学习中,你会对此架构有更深的理解。 下面是scrapy官网给出的最新的架构图示。
1.1 基本组件
- 引擎(Engine) 引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。 详细内容查看下面的数据流(Data Flow)部分。
- 调度器(Scheduler) 调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。
- 下载器(Downloader) 下载器负责获取页面数据并提供给引擎,而后提供给spider。
- 爬虫(Spiders) Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。
- 管道(Item Pipeline) Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、验证及持久化(例如存取到数据库中)。
- 下载器中间件(Downloader middlewares) 下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
- Spider中间件(Spider middlewares) Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
1.2 数据流向
Scrapy的数据流由执行引擎(Engine)控制,其基本过程如下:
- 引擎从Spider中获取到初始Requests。
- 引擎将该Requests放入调度器,并请求下一个要爬取的Requests。
- 调度器返回下一个要爬取的Requests给引擎
- 引擎将Requests通过下载器中间件转发给下载器(Downloader)。
- 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
- 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
- Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
- 引擎将(Spider返回的)爬取到的Item交给ItemPipeline处理,将(Spider返回的)Request交给调度器,并请求下一个Requests(如果存在的话)。
- (从第一步)重复直到调度器中没有更多地Request。
2 安装
pip install scrapy
3 实战:爬取博客园文章信息
3.1 创建项目
创建项目是爬取内容的第一步,Scrapy通过
scrapy startproject
命令来在当前目录下创建一个新的项目。 下面我们创建一个爬取博客园(https://www.cnblogs.com/)文章信息的项目
scrapy startproject cnblog
其中cnblog是你的项目的名字,可以自己定义。 其目录结构如下
cnblog/
scrapy.cfg
cnblog/
__init__.py
items.py
pipelines.py
middlewares.py
settings.py
spiders/
__init__.py
下面简单的讲解一下各目录/文件的作用:
- scrapy.cfg 项目的配置文件,带有这个文件的那个目录作为scrapy项目的根目录
- items.py 定义你所要抓取的字段
- pipelines.py 管道文件,当spider抓取到内容(item)以后,会被送到这里,这些信息(item)在这里会被清洗,去重,保存到文件或者数据库。
- middlewares.py 中间件,主要是对功能的拓展,你可以添加一些自定义的功能,比如添加随机user-agent, 添加proxy。
- settings.py 设置文件,用来设置爬虫的默认信息,相关功能开启与否,比如是否遵循robots协议,设置默认的headers,设置文件的路径,中间件的执行顺序等等。
- spiders/ 在这个文件夹下面,编写你自定义的spider。
3.2 编写爬虫
编写spider文件 在项目中的spiders文件夹下面创建一个文件,命名为cnblog_spider.py我们将在这个文件里面编写我们的爬虫。先上代码再解释。
import scrapy # 导入scrapy模块
class Cnblog_Spider(scrapy.Spider): # 定义一个spider类,继承自scrapy.Spider父类
name = "cnblog" # name: 用于区别Spider。 该名字必须是唯一的,不可以为不同的Spider设定相同的名字。这一点很重要。
allowed_domains = ["cnblogs.com"]
start_urls = [
'https://www.cnblogs.com/', # start_urls: 包含了Spider在启动时进行爬取的url列表。第一个被获取到的页面将是其中之一。即这是爬虫链接的起点,爬虫项目启动,便开始从这个链接爬取,后续的URL则从初始的URL获取到的数据中提取。
]
def parse(self, response): # parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。
title = response.xpath('//a[@class="titlelnk"]/text()').extract()
link = response.xpath('//a[@class="titlelnk"]/@href').extract()
print(title)
print(link)
- 导入scrapy模块
- 定义一个spider类,继承自scrapy.Spider父类。
- name: 用于区别Spider。 该名字必须是唯一的,不可以为不同的Spider设定相同的名字。这一点很重要。
- start_urls: 包含了Spider在启动时进行爬取的url列表。第一个被获取到的页面将是其中之一。即这是爬虫链接的起点,爬虫项目启动,便开始从这个链接爬取,后续的URL则从初始的URL获取到的数据中提取。
- parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。
修改settings.py文件 将settings.py文件里面的下列内容修改如下,其余的内容不动。
ROBOTSTXT_OBEY = False #不遵循robots协议
#去掉下面的这个注释,以设置请求头信息,伪造浏览器headers,并手动添加一个user-agent
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
#user-agent新添加
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
运行我们的爬虫项目 至此,项目必要的信息已经全部完成了,下面就是运行我们的爬虫项目 进入带有scrapy.cfg文件的那个目录,前面已经说过,这是项目的根目录,执行下面的命令
scrapy crawl cnblog
cnblog是spiders/cnblog_spider.py文件里面我们定义的那个具有唯一性的name 你会发现打印出了博客园首页的文章列表和文章的url信息,如下所示。
3.3 数据存储
我们简单的实现了一个博客首页信息的爬取,并在控制台输出,但是,爬下来的信息自然是需要保存下来的。这一节主要是实现信息的存储,我们以将信息保存到文件为例,学习数据的存储,依然是以博客首页信息为例。
3.4 编写爬虫
修改items.py文件来定义我们的item Item 是保存爬取到的数据的容器;其使用方法和python字典类似。虽然你也可以在Scrapy中直接使用dict,但是Item提供了额外保护机制来避免拼写错误导致的未定义字段错误。简单的来说,你所要保存的任何的内容,都需要使用item来定义,比如我们现在抓取的页面,我们希望保存title,link, 那么你就要在items.py文件中定义他们,以后你会发现,items.py文件里面你所要填写的信息是最简单的了。
import scrapy
class CnblogItem(scrapy.Item):
title = scrapy.Field()
link = scrapy.Field()
这样就已经定义好了。
编写spider文件 在项目中的spiders文件夹下面创建一个文件,命名为cnblog_spider.py我们将在这个文件里面编写我们的爬虫。先上代码再解释
import scrapy
from cnblog.items import CnblogItem #新添加
class Cnblog_Spider(scrapy.Spider):
name = "cnblog"
allowed_domains = ["cnblogs.com"]
start_urls = [
'https://www.cnblogs.com/',
]
def parse(self, response):
item = CnblogItem() #新添加
item['title'] = response.xpath('//a[@class="titlelnk"]/text()').extract() #修改
item['link'] = response.xpath('//a[@class="titlelnk"]/@href').extract() #修改
yield item #新添加
下面主要对新添加或者修改的地方讲解
- 导入CnblogItem自定义类,注意:新建项目中带有scrapy.cfg文件的那个目录默认作为项目的根目录,因此from cnblog.items import CnblogItem就是从cnblog项目里面的items.py文件里面导入我们自定义的那个类,名称是CnblogItem,就是上面我们定义的那个CnblogItem ,只有导入了这个类,我们才可以保存我们的字段。
- item = CnblogItem() 实例化,不多说。
- item['title']和item['link'],前面已经说过,item其实就是可以简单的理解为字典,这个地方就是相当于给字典里面的键赋值。 最后yield item生成器,scrapy会将item传递给pipeline进行后续的处理,当然,前提是你打开了settings设置里面的设置项,相关的设置马上就会说到。
修改pipelines.py文件,实现保存。
class FilePipeline(object):
'''
实现保存到txt文件的类,类名这个地方为了区分,做了修改,
当然这个类名是什么并不重要,你只要能区分就可以,
请注意,这个类名待会是要写到settings.py文件里面的。
'''
def process_item(self, item, spider):
with open('cnblog.txt', 'w', encoding='utf-8') as f:
titles = item['title']
links = item['link']
for i,j in zip(titles, links):
f.write(i + ':' + j + '\n')
return item
修改settings.py文件
之前,我们修改了两个内容,ROBOTSTXT_OBEY和DEFAULT_REQUEST_HEADERS,这里我们在之前的基础上,在添加如下内容。
OBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
#新修改
ITEM_PIPELINES = {
'cnblog.pipelines.FilePipeline': 300, #实现保存到txt文件
}
对于上面的含义,
- cnblog.pipelines.FilePipeline其实就是应用cnblog/pipelines模块里面的FilePipeline类,就是我们之前写的那个,300的含义是执行顺序,如果还有其他管道保存到数据库,定义管道执行顺序。
运行爬虫 进入到项目文件,执行 scrapy crawl cnblog 可以看到根目录下生成了cnblog.txt的文件