01-Scrapy简介和环境搭建

Scrapy是Python中一个用来抓取网站数据的开源网络爬虫(网络蜘蛛)框架,在数据挖掘、信息收集等领域应用广泛。Scrapy提供了编写网络爬虫的大致框架,基于Scrapy我们可以方便快捷的构建功能完善的爬虫程序。本篇笔记将对Scrapy框架的相关内容进行介绍。
Scrapy官方网站:https://www.scrapy.org/
Scrapy框架结构
网络爬虫乍一听并不是什么高难度的程序,实际上提到编写一个网络爬虫我们可能首先会想到使用requests等HTTP客户端库自己构建,然而当涉及信息抽取、页面跳转、并发、限流、数据持久化、断点续“爬”等一系列问题时我们这种自己架构的爬虫就会变得越来越复杂,当我们意识到需要抽取一个框架以便以后能够复用并专注于业务逻辑时,我们大概就会又重新“发明”了一次Scrapy,这也是Scrapy框架已经存在了的意义。
总而言之,Scrapy就是围绕爬虫程序可能涉及的方方面面来设计的,其内部结构大致如下。

爬虫(蜘蛛) Spiders:Spider是用户自定义的内容解析器,简而言之其中包含定义抓取哪些页面、基于页面响应抽取信息或生成新的请求的代码逻辑,也是Scrapy开发中我们最需要关心部分。
Engine 调度引擎:Scrapy内部的核心控制模块,负责管理和调度HTTP请求响应、执行爬虫、处理管道交互等。
Scheduler 调度器:接收来自调度引擎的请求,将其排队并返回下一个待爬取任务。
Downloader 下载器:实际执行数据请求的HTTP客户端,请求互联网的服务器并获取响应结果。
Item Pipeline 数据处理管线:负责清洗、验证及持久化处理由爬虫抽取的数据模型。
Middlewares 中间件:中间件实现了类似切面的处理逻辑,它位于Engine和Spider之间,能够在爬虫处理数据的前后插入特定逻辑。
Scrapy基于Twisted框架开发,它实现了事件驱动的异步IO机制,具有较高的IO并发性能。
安装Scrapy并创建工程
我们直接创建一个Python虚拟环境,并执行以下命令使用pip包管理器安装Scrapy框架。
pip install Scrapy
Scrapy框架本身包含了一个CLI命令行工具,我们可以直接用它来创建基础的工程文件,执行以下命令以当前文件夹为根目录创建一个叫tutorial的工程。
scrapy startproject tutorial .
注意:最后的.表示在当前目录下创建,不加默认会新建目录。
执行完成后,生成的目录结构如下。
工程根目录
|_ totorial
|_ spiders # 存放我们编写的蜘蛛的Python包
|_ items.py # 数据模型定义文件
|_ middlewares.py # 中间件定义文件
|_ pipelines.py # 数据处理管线定义文件
|_ settings.py # 模块配置文件,其中配置并发数、延时、启用组件等
|_ scrapy.cfg # 项目基础配置文件,CLI工具根据它识别爬虫功能模块
对于scrapy.cfg配置文件,其中配置的内容类似于程序的配置入口,它指定我们通过scrapy命令运行爬虫时需要加载哪个包的配置文件,例如下面配置指定使用tutorial包的settings.py作为配置入口。Scrapy工程其实也可以包含多个模块,此时[settings]中也要对应编写多个入口配置,scrapy命令会读取环境变量SCRAPY_PROJECT来确定究竟运行的是哪个模块。
[settings]
default = tutorial.settings
至于[deploy]配置项它主要用于scrapyd,一个用于在远程服务器上部署爬虫并提供REST API的模块。
Scrapy简单使用例子
下面我们直接编写一个简单的Spider例子来演示Scrapy框架的开发流程,这里我们以网页https://quotes.toscrape.com来练习爬虫的编写。
在spiders包中,我们直接创建一个“蜘蛛”。
quotes_spider.py
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'https://quotes.toscrape.com/page/1/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response, **kwargs):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
可以看到所谓的“蜘蛛”就是一个Python类,它继承了基类scrapy.Spider,我们简单看一下这个类:
name:爬虫的名字,这个名字是必须指定的且不能重复,Scrapy框架会根据这个名字实例化爬虫。
start_requests():这是覆盖父类的一个方法,它返回了一个生成器,生成器的返回值类型是scrapy.Request对象,参数传入了我们定义的URL和下面的定义的parse()方法作为回调。这很好理解,Scrapy的调度引擎收到Request对象后会将其交给下载器发起HTTP请求,请求URL由我们传入,返回数据用我们提供的回调函数解析。
parse():这也是覆盖父类的一个方法,这个函数专门用来处理Request的返回数据,参数response是TextResponse对象,里面包含了HTTP请求返回的信息。这里我们将返回的HTML直接保存在一个文件中(实际上Spider内部不应包含阻塞式IO代码,数据应交由Item Pipeline处理,但我们这里仅仅是为了演示功能,实际开发中不要直接在Spider中保存数据)。
现在我们可以直接用Scrapy的CLI工具执行这个爬虫。
scrapy crawl quotes
执行完成后,我们会看到控制台上输出一系列的日志信息,最终在项目的根目录下保存了一个HTML文件,文件中的内容就是我们要抓取的网页,此时我们便成功开发了一个最简单的爬虫程序。
不过实际开发中,爬虫程序通常复杂的多,我们通常不是简单的保存服务器返回的HTML,而是对数据进行一系列的抽取并提取为数据模型对象,然后保存在数据库等位置供后续分析和使用,此外爬虫不可能仅仅抓取一个页面,它还需要按照一定的策略在多个页面之间来回穿梭,有关这些高级内容我们将在后续章节继续介绍。