逆向爬虫15 Scrapy基本介绍与使用
一、什么是Scrapy?
Scrapy到目前为止依然是这个星球上最流行的 爬虫框架
。摘一下官方给出对scrapy的介绍
An open source and collaborative framework for extracting the data you need from websites.
In a fast, simple, yet extensible way.
Scrapy的特点:速度快,简单,可扩展性强
Scrapy的官方文档:https://docs.scrapy.org/en/latest/
爬虫框架: 在前面的笔记中,对于 爬虫
我们已经有概念了,即从指定网站URL中抓取指定的数据,并持久化存储到数据库中。那什么是框架
呢?回顾之前写的爬虫代码,从页面URL获取,到发起request请求,再到接受response响应,以及最后从response中解析出所要的data,整个流程都是我们自己来完成的。
这是我们最原始的写代码方式,一个人要考虑所有的事情,对这个人的要求就很高。这看似是一件很棒的事情,但对爬虫(或者说软件工程)这件事本身来说,存在着很多问题。
- 代码的逻辑都耦合在一起,当需求改变时,很可能会抽一发而动全身,可维护性差,可扩展性差。
- 逻辑耦合会导致一些重复性的工作无法抽离出来,导致每次都需要重复编写相同功能的代码。
- 上述两个问题最终导致类似功能的代码无法批量生产,这也是软件工程所需要解决的普遍问题。
补充: 这里突然让我想起了工程的意义(不仅仅是软件工程),工程要解决的第一件事就是把一件看似无法完成的事情划分成若干个可行的部分,然后逐个攻破,最后实现预定的功能。而要解决的第二件事就是,把类似功能的问题规范化,标准化,流程化,当之后遇到类似问题的时候,可以用这套流程来快速实现类似的功能,而不需要从头再来,这适用与所有与工程概念相关的东西。
因此,什么是 框架
呢?框架
是基于软件工程思想,将业务逻辑解耦成不同模块,再将繁重而重复的模块抽离出来,事先写好,留出需要用户自定义功能部分的接口,用户只需要学习 框架
的工作流程,知道自己需要在什么地方填写自己的业务代码,即可快速实现类似的功能,这就是 框架
存在的意义。
二、Scrapy工作流程(重点)
Scrapy的工作流程:
先规范一下途中英文的中文意思:① engine (引擎) ② spider (爬虫) ③ scheduler (调度器) ④ downloader (下载器) ⑤ pipline (管道)
整个工作流程:
-
爬虫
中起始的URL构造成request对象,并传递给调度器
。 -
引擎
从调度器
中获取到request对象,然后交给下载器
。 - 由
下载器
来获取到页面源代码,并封装成response对象,反馈给引擎
。 -
引擎
将获取到的response对象传递给爬虫
,由爬虫
对数据进行解析,再反馈给引擎
。 -
引擎
将数据传递给管道
进行数据持久化保存或进一步的数据处理。 - 在此期间如果
爬虫
中提取到的不是数据,而是子页面URL,可以进一步提交给调度器
,进而重复步骤2
的过程。
上述过程一直在重复着几个东西:
- 引擎 (engine)
Scrapy的核心,所有模块的衔接,数据流程梳理。 - 调度器 (scheduler)
本质上这东西可以看成是一个队列,里面存放着一堆我们即将要发送的请求,可以看成是一个URL的容器。它决定了下一步要去爬取哪一个URL,通常我们在这里可以对URL进行去重操作。 - 下载器 (downloader)
它的本质就是用来发动请求的一个模块,完全可以把它理解成实现 get_page_source() 功能的模块,只不过它返回的是一个response对象 - 爬虫 (spider)
这是我们要写的第一个部分的内容,负责解析下载器返回的response对象,从中提取我们要的数据。 - 管道 (pipline)
这是我们要写的第二个部分的内容,主要负责数据的存储和各种持久化操作。
经过上述的介绍来看,Scrapy其实就是把平时写的爬虫进行了四分五裂的改造,对每个功能进行了单独的封装,并且各个模块之间互相不做依赖,一切都由引擎进行调配,这种思想就叫 解耦
,让模块与模块之间的关联性更加的松散,这样如果希望替换某一模块的时候会非常的容易,对其它模块也不会产生任何影响。
三、 Scrapy实例
接下来,使用Scrapy完成一个超级简单的爬虫,目标是先把Scrapy用起来,理解哥哥模块之间是如何搭配工作的。
1. 创建项目
scrapy startproject 项目名称
scrapy startproject game4399
创建好项目后,我们可以在目录下看到Scrapy帮我们创建了一个文件夹,里面的目录结构如下:
game4399 # 项目所在文件夹, 建议用pycharm打开该文件夹
├── game4399 # 项目跟目录
│ ├── __init__.py
│ ├── items.py # 封装数据的格式
│ ├── middlewares.py # 所有中间件
│ ├── pipelines.py # 所有的管道
│ ├── settings.py # 爬虫配置信息
│ └── spiders # 爬虫文件夹, 稍后里面会写入爬虫代码
│ └── __init__.py
└── scrapy.cfg # scrapy项目配置信息,不要删它,别动它,善待它.
2. 创建爬虫
cd 文件夹 # 进入项目所在文件夹
scrapy genspider 爬虫名称 允许抓取的域名范围
cd game4399
scrapy genspider game 4399.com
至此,爬虫创建完毕,打开目录中多了一个文件
├── game4399
│ ├── __init__.py
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ └── game.py # 多了一个这个.
└── scrapy.cfg
3. 编写数据解析过程
完善game.py中的内容
import scrapy
class GameSpider(scrapy.Spider):
name = 'game'
allowed_domains = ['4399.com']
start_urls = ['http://www.4399.com/flash/gamehw.htm']
def parse(self, response):
# print(response) # response对象
# print(response.text) # 页面源代码
# print(response.json()) # 如果页面返回的是json
# print(response.xpath()) # 使用xpath进行数据解析
# print(response.css()) # 用CSS选择器进行解析
ul = response.xpath('//ul[@class="tm_list"]')[0]
li_lst = ul.xpath('./li')
# 分块提取数据
for li in li_lst:
name = li.xpath('./a/b/text()').extract_first()
category = li.xpath('./em/a/text()').extract_first()
date = li.xpath('./em/text()').extract_first()
print(name, category, date)
dic = {
"name": name,
"category": category,
"date": date
}
# 需要用yield将数据传递给管道
yield dic # 如果返回的是数据,直接可以认为是给了管道pipline
注意: spider返回的内容只能是字典,request对象,item数据或者None,其他内容一律报错
运行爬虫:
scrapy crawl 爬虫名字
scrapy crawl game
如果觉得Scrapy日志输出的内容太多,可以到settings.py文件中添加日志等级。
# 日志的级别: DEBUG, INFO, WARNING, ERROR, CRITICAL
LOG_LEVEL = "WARNING"
4. 编写pipline,对数据进行简单的保存
数据传递到pipline,先看一下在pipline中的样子。
首先修改settings.py文件中的pipline信息
ITEM_PIPELINES = {
# 前面是pipeline的类名地址
# 后面是优先级, 优先级月低越先执行
'game4399.pipelines.Game4399Pipeline': 300,
'game4399.pipelines.NewPipeline': 299
}
然后修改pipline中的代码:
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
# 记住,管道默认是不生效的,需要去settings里面去开启管道
class Game4399Pipeline:
def process_item(self, item, spider):
print(f"item: {item}")
print(f"spider: {spider.name}")
return item
class NewPipeline:
def process_item(self, item, spider):
item['love'] = '我爱周杰伦'
return item
由于NewPipline的优先级比Game4399Pipeline高,因此NewPipeline会先拿到item数据,并在里面加上了’love’字段的’我爱周杰伦’数据,因此可以在Game4399Pipeline中的item中看到新加进去的’love’字段数据。
四、Scrapy使用小结
至此,对Scrapy有了一个非常初步的了解和使用,快速总结一下,Scrapy框架的使用流程:
- 创建爬虫项目:
scrapy start xxx
- 进入项目目录:
cd xxx
- 创建爬虫:
scrapy genspider 名称 抓取域
- 编写
item.py
文件,定义好数据item
- 修改spider中的parse方法,对返回的响应response对象进行解析,返回item
- 在pipline中对数据进行保存工作
- 修改
settings.py
文件,将pipline设置未生效,并设置好优先级。