Scrapy库简介

Scrapy, Python开发的一个快速, 高层次的屏幕抓取和web抓取框架, 用于抓取web站点并从页面中提取结构化的数据. Scrapy用途广泛, 可以用于数据挖掘, 监测和自动化测试.
Scrapy吸引人的地方在于它是一个框架, 任何人都可以根据需求方便的修改. 它也提供了多种类型爬虫的基类, 如BaseSpider, sitemap爬虫等; 最新版本又提供了web2.0爬虫的支持.

Scrapy库的安装

安装Scrapy库的最快也是最简单的方法是在shell上使用以下命令:
pip install scrapy

Scrapy爬虫框架解析

01

数据流的3个路径

  • 一:
    • Engine从Spider处获得爬取请求(Request)
    • Engine将爬取请求转发给Scheduler, 用于调度
  • 二:
    • Engine从Scheduler处获得下一个要爬取的请求
    • Engine将爬取请求通过中间件发送给Downloader
    • 爬取网页后,Downloader形成响应(Response), 通过中间件发送给Engine
    • Engine将收到的响应通过中间件发送给Spider处理
  • 三:
    • Spider处理响应后产生爬取项(scraped Item)和新的爬取请求(Requests)给Engine
    • Engine将爬取项发送给Item PipeLine(框架出口)
    • Engine将爬取请求发送给Scheduler

数据流的出入口

Engine控制各模块数据流, 不间断从Scheduler处获得爬取请求, 直至请求为空.
框架入口:Spider的初试爬取请求(Request)
框架出口:Item Pipeline

各个模块(5+2结构)

Engine(不需要用户修改)

  1. 控制所有模块之间的数据流
  2. 根据条件触发事件

Downloader(不需要用户修改)

根据请求下载网页

Scheduler(不需要用户修改)

对所有爬取请求进行调度管理

Downloader Middleware(用户可以编写配置代码)

  • 目的: 实施Engine, Scheduler和Downloader之间进行用户可配置的控制
  • 功能: 修改, 丢弃, 新增请求或响应

Spider(需要用户编写配置代码)

  1. 解析Downloader返回的响应(Response)
  2. 产生爬取项(scraped item)
  3. 产生额外的爬取请求(Request)

Item Pipelines(需要用户编写配置代码)

  1. 以流水线方式处理Spider产生的爬取项
  2. 由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型
  3. 可能操作包括:清理、检验和查重爬取项中的HTML数据,将数据存入数据库

Spider Middleware(用户可以编写配置代码)

  • 目的:对请求和爬取项的再处理
  • 功能:修改、丢弃、新增请求或爬取项

Scrapy爬虫的常用命令

Scrapy命令行:
Scrapy是为持续运行设计的专业爬虫框架, 提供操作的Scrapy命令行.

Scrapy命令行格式:
>scrapy<command>[options][args]

Scrapy常用命令:

命令 描述 格式
startproject 创建一个新工程 scrapy startproject<name>[dir]
genspider 创建一个爬虫 scrapy genspider[options]<name><domain>
settings 获得爬虫配置信息 scrapy setting[options]
crawl 运行一个爬虫 scrapy crawl<spider>
list 列出工程中所有爬虫 scrapy list
shell 启动URL调试命令行 scrapy shell[url]

创建一个scrapy项目

在命令行界面可以使用 startproject 命令直接创建scrapy项目:

1
2
3
4
5
6
7
8
9
C:\Users\zzZ5>d:
D:\>cd d:\Study\Program Study\python study\scrapy
D:\Study\Program Study\python study\scrapy>scrapy startproject zzZ5
New Scrapy project 'zzZ5', using template directory 'd:\program files\python37\lib\site-packages\scrapy\templates\project', created in:
D:\Study\Program Study\python study\scrapy\zzZ5

You can start your first spider with:
cd zzZ5
scrapy genspider example example.com

此时scrapy自动在当前目录下创建一个新的爬虫工程项目.

生成的工程目录:

目录 描述
zzZ5/ 目录外层
scrapy.cfg 部署Scrapy爬虫的配置文件
zzZ5/zzZ5/ Scrapy框架的用户自定义Python代码
__init__.py 初始化脚本
items.py Items代码模板(继承类)
middlewares.py Middlewares代码模板(继承类)
pipelines.py Pipelines代码模板(继承类)
settings.py Scrapy爬虫配置文件
spiders/ Spiders代码模板目录(继承类)
spiders/__init__.py 初始文件, 无需修改
spiders/__pycache__/ 缓存目录, 无需修改

创建一个scrapy爬虫

在spiders目录下面, 我们可以使用命令行命令 scrapy genspider demo zzZ5.xyz 自动生成一个 demo.py 爬虫文件, 也可以在spiders目录下自己手动创建一个 demo.py 爬虫文件(爬虫名可以任意取). 爬虫文件的内容大致为以下这些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding: utf-8 -*-
import scrapy


class DemoSpider(scrapy.Spider): # 需要继承scrapy.Spider类
name = 'demo' # 定义爬虫名
# allowed_domains = ['zzZ5.xyz'] # 要爬取的主域名, 不必须

# start_urls = ['http://zzZ5.xyz/'] #开始爬取的页面, 不必须

def start_requests(self): # 由此方法通过下面链接爬取页面, 不必须
# 定义爬取的链接
urls = [
'https://zzz5.xyz/tags/%E7%A7%91%E5%AD%A6%E8%AE%A1%E7%AE%97/',
'https://zzz5.xyz/tags/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) # 将爬取到的页面提交给parse方法处理

def parse(self, response):

'''
start_requests已经爬取到页面,那如何提取我们想要的内容呢?那就可以在这个方法里面定义。
这里的话,并没有定义,只是简单的把页面做了一个保存,并没有涉及提取我们想要的数据,后面会慢慢说到
也就是用xpath、正则、或是css进行相应提取,这个例子就是让你看看scrapy运行的流程:
1、定义链接;
2、通过链接爬取(下载)页面;
3、定义规则,然后提取数据;
'''

filename = response.url.split("/")[-2] + '.html' # 获取文件名, 这个需要自己微调
with open(filename, 'wb') as f: # python文件操作,不多说了;
f.write(response.body) # 写入刚刚下载的文件, response.body就代表了刚才下载的页面!
self.log('保存文件: %s' %filename) # 打印日志

接下来在工程目录下面, 使用命令行命令 scrapy crawl demo 即可运行该爬虫.

scrapy爬虫类型

Request类

类型为class scrapy.http.Request(), 表示一个HTTP请求, 由Spider生成, 由Downloader执行.

Request类的属性和方法:

属性/方法 描述
.url Request对应的请求URL地址
.method 对应的请求方法, ‘GET’, ‘POST’等
.headers 字典类型风格的请求头
.body 请求内容的主体, 字符串类型
.meta 用户添加的扩展信息, 在Scrapynei’bu模块间传递信息使用
.copy() 复制该请求

Response类

类型为class scrapy.http.Response(), 表示一个HTTP响应, 由Downloader生成, 由Spider处理.

Request类的属性和方法:

属性/方法 描述
.url Request对应的URL地址
.status HTTP状态码, 默认是200
.headers Response对应的头部信息
.body Response对应的内容信息, 字符串类型
.flags 一组标记
.request 产生Response类型的Request对象
.copy() 复制该响应

Item类

类型为class scrapy.item.Item(), 表示一个从HTML页面提取的信息内容, 由Spider生成, 由Item Pipeline处理.

Item类似字典类型, 可以按照字典类型操作.

Scrapy爬虫提取信息的方法

Scrapy爬虫支持多种HTML信息提取方法, 如: Beautiful Soup, lxml, re, XPath Selector, CSS selector等.

CSS selector的用法

我们要提取的就是zzZ5.xyz中的<title>zzZ5的个人博客</title>这个标签里面的数据, 我们最终要得到的是:”zzZ5的个人博客” 这么一段字符串.
那我们就循序渐进的看看我们会怎么操作, 会使用哪些函数.
首先我们需要在命令行输入:
scrapy shell https://zzZ5.xyz
就这样一个格式, 其实这段代码就是一个下载的过程, 一执行这么一段代码scrapy就立马把我们相应链接的相应页面给拿到了,那接下来就可以任你处置了(差不多就是调试提取数据用的命令).

然后我们继续在命令行输入如下命令:response.css('title')
response.css(‘标签名’), 标签名的话可以是html标签比如: title, body, div, 也可以是你自定义的class标签.
那当我们输入以上命令之后, 你会发现已经提取了一些数据:

1
2
>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>zzZ5的个人博客</title>'>]

那你会发现,我们使用这个命令提取的一个Selector的列表, 并不是我们想要的数据; 那我们再使用scrapy给我们准备的一些函数来进一步提取, 改变一下上面的写法, 输入:

1
2
>>> response.css('title').extract()
['<title>zzZ5的个人博客</title>']

我们只是在后面加入了: .extract() 这么一个函数你就提取到了我们标签的一个列表, 更近一步了, 那如果我们不要列表, 只要title这个标签, 要怎么处理呢? 看我们的输入:

1
2
>>> response.css('title').extract()[0]
'<title>zzZ5的个人博客</title>'

这里的话, 我们只需要在后面添加: [0], 那代表提取这个列表中的第一个元素, 那就得到了我们的title字符串; 这里的话scrapy也给我提供了另外一个函数, 可以这样来写, 一样的效果:

1
2
>>> response.css('title').extract_first()
'<title>zzZ5的个人博客</title>'

extract_first()就代表提取第一个元素, 和我们的: [0] 一样的效果, 只是更简洁些, 至此我们已经成功提取到了我们的title, 但是你会发现多了一个title标签, 这并不是你需要的, 那要怎么办呢, 我们可以继续改变一下以上的输入:

1
2
>>> response.css('title::text').extract_first()
'zzZ5的个人博客'

我们在title后面加上了 ::text , 这代表提取标签里面的数据, 至此, 我们已经成功提取到了我们需要的数据:
‘zzZ5的个人博客’
总结一下, 其实就这么一段代码:
response.css('title::text').extract_first()

配置pipelines.py

可以通过配置pipelines.py, 对获得的数据进行进一步的处理.
原文件为:

1
2
3
4
5
6
7
8
9
10
11
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html


class Zzz5Pipeline(object):
def process_item(self, item, spider):
return item

其中每个 item pipeline 都共会调用四个方法, 这里给出三个常用的方法:

  • process_item(self, item, spider):
    每个pipeline都调用此方法. process_item()返回一个带有数据的dict或一个Item(或任何子类)对象.

  • open_spider(self, spider)
    当爬虫open的时候调用.

  • close_spider(self, spider)
    当爬虫close的时候调用

在 item pipeline 中编写完处理数据程序后, 在settings.py中的ITEM_PIPELINES中将该类添加进去.

配置settings.py

下面给出一些常用的设置:

选项 描述
CONCURRENT_REQUESTS Downloader最大并发请求下载数量, 默认32
CONCURRENT_ITEMS Item Pipeline最大并发ITEM处理数量, 默认100
CONCURRENT_REQUESTS_PER_DOMAIN 每个目标域名最大的并发请求数量, 默认8
CONCURRENT_REQUESTS_PER_IP 每个目标IP最大的并发请求数量, 默认为0, 非零有效