Scrapy01--基本介绍与初步使用
一、爬虫工程化
何为工程化,就是让你的程序更加的有体系,有逻辑,更加的模块化.
到目前为止,我们所编写的爬虫我们都是从头到尾的每一步都要亲力亲为. 这样做固然有其优点(可控性更好),但是各位请认真思考.
这样的代码逻辑是不能形成批量生产的效果的(写100个爬虫). 很多具有共通性的代码逻辑都没有进行重复利用.
那我们就可以考虑看看,能不能把一些共性的问题(获取页面源代码,数据存储),单独搞成一个功能.
如果我们把这些功能单独进行编写. 并且产生类似单独的功能模块,将大大的提高我们爬虫的效率. 已达到我们爬虫工程化开发的效果.
爬虫工程化: 对爬虫的功能进行模块化的开发. 并达到可以批量生产的效果(不论是开发还是数据产出)
二、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/
神马叫框架:按照框架本身设计的逻辑. 往里面填写内容就可以了
学习其他框架的时候. 切忌:不要去直接上来去抠它的源码
先学会如何使用(怎么往里填窟窿),再反着去看他的源代码. 理解起来就容易了
三、Scrapy工作流程(重点)
- 之前爬虫的逻辑:
# 伪代码,只为说明
def get_page_srouce():
resp = requests.get(xxxxx)
return resp.text | resp.json()
def parse_source():
xpath,bs4,re
return data
def save_data(data):
txt,csv,mysql,mongodb
def main(): # 负责掌控全局
# 首页的页面源代码
ret = get_page_source() # 获取页面源代码,发送网络请求
data = parse_source(ret) # 去解析出你要的数据
# 需要继续请求新的url
while:
# 详情页
ret = get_page_source() # 获取页面源代码,发送网络请求
data = parse_source(ret) # 去解析出你要的数据
save_data(data) # 负责数据存储
# 详情页如果还有分页.
# ...继续上述操作.
if __name__ == '__main__':
main()
- scrapy的工作流程:
scrapy整个工作流程
- 爬虫中起始的url构造成request对象,并传递给调度器
引擎
从调度器
中获取到request对象,然后交给下载器
- 由
下载器
来获取到页面源代码,并封装成response对象。并回馈给引擎
引擎
将获取到的response对象传递给spider
。由spider
对数据进行解析(parse)。并回馈给引擎
引擎
将数据传递给pipeline进行数据持久化保存或进一步的数据处理- 在此期间,如果spider中提取到的并不是数据, 而是子页面url,可以进一步提交给调度器,进而重复
步骤2
的过程
# 伪代码,只为说明
def get_page_srouce(url,method):
if method == get:
resp = requests.get(xxxxx)
return resp.text | resp.json()
def parse_source():
xpath,bs4,re
def save_data(data):
txt,csv,mysql,mongodb
def main(): # 负责掌控全局->为了你理解
# 主页
req = spider.get_first_req()
while 1:
scheduler.send(req)
next = scheduler.next_req()
sth = downloader.get_page_source(next)
data = spider.parse(sth)
if data is 数据:
pipeline.process_item(data)
if __name__ == '__main__':
main()
上述过程中一直在重复着几个东西:5大组件
-
引擎(engine)
scrapy的核心,所有模块的衔接,数据流程梳理.
-
调度器(scheduler)
本质上这东西可以看成是一个集合和队列. 里面存放着一堆即将要发送的请求. 可以看成是一个url的容器. 它决定了下一步要去爬取哪一个url. 通常在这里可以对url进行去重操作.
-
下载器(downloader)
它的本质就是用来发动请求的一个模块. 小白们完全可以把它理解成是一个requests.get()的功能. 只不过这货返回的是一个response对象.
-
爬虫(spider)
这是我们要写的第一个部分的内容,负责解析下载器返回的response对象.从中提取到我们需要的数据.
-
管道(pipeline)
这是我们要写的第二个部分的内容,主要负责数据的存储和各种持久化操作.
经过上述的介绍来看,scrapy其实就是把平时写的爬虫进行了四分五裂式的改造. 对每个功能进行了单独的封装,并且,各个模块之间互相的不做依赖. 一切都由引擎进行调配。
这种思想希望你能知道:解耦. 让模块与模块之间的关联性更加的松散.
这样我们如果希望替换某一模块的时候会非常的容易. 对其他模块也不会产生任何的影响.
到目前为止,我们对scrapy暂时了解这么多就够了. 后面会继续在这个图上进一步展开.
四、Scrapy安装
在windows上安装scrapy,可能会出现各种各样的异常BUG.
4.1 正常安装
### scrapy 2.5.1 ---> scrapy-redis 0.7.2
注意:由于scrapy的升级. 导致scrapy-redis无法正常使用
所以选择2.5.1这个版本作为学习. 后期各位可以根据scrapy-redis的升级,而跟着升级scrapy
# 先使用pip直接安装,看看报错不
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy==2.5.1
pip install scrapy==2.5.1
如果安装成功,直接去创建项目即可
如果安装失败,请先升级一下pip,然后重新安装scrapy即可.
最新版本的pip升级完成后. 安装依然失败,可以根据报错信息进行一点点的调整,多试几次pip. 直至success
4.2 安装包安装
如果上述过程还是无法正常安装scrapy,可以考虑用下面的方案来安装:
# 1.安装wheel
pip install wheel
# 2.下载twisted安装包,https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
# 3.用wheel安装twisted
pip install Twisted‑21.7.0‑py3‑none‑any.whl
# 4.安装pywin32
pip install pywin32
# 5.安装scrapy
pip install scrapy
# 最终控制台输入 scrapy version 能显示版本号. 就算成功了
4.3 调整OpenSSL版本
安装完成后. 请调整OpenSSL的版本
输入命令: scrapy version --verbose
### 错误版本:
Scrapy : 2.5.1
lxml : 4.9.0.0
libxml2 : 2.9.12
cssselect : 1.1.0
parsel : 1.6.0
w3lib : 1.22.0
Twisted : 22.4.0
Python : 3.9.12 (tags/v3.9.12:b28265d,Mar 23 2022,23:52:46) [MSC v.1929 64 bit (AMD64)]
pyOpenSSL : 22.0.0 (OpenSSL 3.0.4 21 Jun 2022) # 此时用的是3.0.4 要降低它的版本
cryptography : 37.0.3
Platform : Windows-10-10.0.19043-SP0
更改方案:
pip uninstall cryptography
pip install cryptography==36.0.2
再次输入 scrapy version --verbose
### 正确版本:
Scrapy : 2.5.1
lxml : 4.9.0.0
libxml2 : 2.9.12
cssselect : 1.1.0
parsel : 1.6.0
w3lib : 1.22.0
Twisted : 22.4.0
Python : 3.9.12 (tags/v3.9.12:b28265d,Mar 23 2022,23:52:46) [MSC v.1929 64 bit (AMD64)]
pyOpenSSL : 22.0.0 (OpenSSL 1.1.1n 15 Mar 2022) # 正确
cryptography : 36.0.2
Platform : Windows-10-10.0.19043-SP0
五、Scrapy实例
接下来,用scrapy来完成一个超级简单的爬虫,目标: 深入理解Scrapy工作的流程,以及各个模块之间是如何搭配工作的.
5.1 创建项目
scrapy startproject 项目名称
# eg:
scrapy startproject mySpider_2
创建好项目后,在pycharm里观察到scrapy创建了一个文件夹,里面的目录结构如下:
mySpider_2 # 项目所在文件夹,建议用pycharm打开该文件夹
├── mySpider_2 # 项目根目录
│ ├── __init__.py
│ ├── items.py # 封装数据的格式 一个一个的类,相当于models.py
│ ├── middlewares.py # 所有中间件
│ ├── pipelines.py # 所有的管道 持久化相关写在这(items.py中类的对象)
│ ├── settings.py # 爬虫配置信息
│ └── spiders # 爬虫文件夹,稍后里面会写入爬虫代码
│ └── __init__.py
└── scrapy.cfg # scrapy项目配置信息 上线部署相关 别动它
5.2 创建爬虫
cd 文件夹 # 进入项目所在文件夹
scrapy genspider 爬虫名称 允许抓取的域名范围
# eg:
cd mySpider_2
scrapy genspider youxi 4399.com
至此,爬虫创建完毕,打开文件夹看一下.
├── mySpider_2
│ ├── __init__.py
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ └── youxi.py # 多了一个这个
|
└── scrapy.cfg
5.3 编写数据解析过程
主要就是编写 spiders 中爬虫的 parse()方法
# 完善youxi.py中的内容
import scrapy
class YouxiSpider(scrapy.Spider):
name = 'youxi' # 该名字非常关键,在启动该爬虫的时候,需要这个名字
allowed_domains = ['4399.com'] # 爬虫抓取的域.
start_urls = ['http://www.4399.com/flash/'] # 起始页
def parse(self,response,**kwargs):
# response.text # 页面源代码
# response.json() # 提取json数据
# response.xpath() # 通过xpath方式提取
# response.css() # 通过css方式提取
# 返回的是selector对象(标签元素),若需要提取内容
.extract_first() # 取一个 返回是字符串
.extract() # 取所有 返回是列表形式
# 用最熟悉的方式: xpath提取游戏名称,游戏类别,发布时间等信息
li_list = response.xpath("//ul[@class='n-game cf']/li")
for li in li_list:
name = li.xpath("./a/b/text()").extract_first()
category = li.xpath("./em/a/text()").extract_first()
date = li.xpath("./em/text()").extract_first()
dic = {
"name": name,
"category": category,
"date": date
}
# 将提取到的数据提交到管道内.
# 注意,这里只能返回 request对象,字典,item数据,or None
yield dic
### 注意: 通常就是 借助item对象 来进行将数据 传输到管道中,进而持久化保存
spider返回的内容只能是字典、requestes对象、item数据、None ,其他内容一律报错
5.4 编写持久化
主要就是 编写数据的存储 涉及文件 items.py 和 pipelines.py
首先修改settings.py文件中的pipeline信息
ITEM_PIPELINES = {
# 前面是pipeline的类名地址 后面是优先级,优先级越低,越先执行
'mySpider_2.pipelines.Myspider2Pipeline': 300,
# 指定pipline,可以指定多个 保存到多个位置 (文件、数据库等)
}
简单修改一下pipeline中的代码:
class Myspider2Pipeline:
# 这个方法的声明不能动!!! 在spider返回的数据,会自动的调用这里的process_item方法.
def process_item(self,item,spider):
# 真正存储的地方
print(item)
return item # 一定不要忘了return item,交给后续的pipline继续使用
5.5 运行爬虫
scrapy crawl 爬虫名字
# eg:
scrapy crawl youxi
六、自定义数据传输结构item
在上述案例中,使用字典作为数据传递的载体,但是如果数据量非常大. 由于字典的key是随意创建的. 极易出现问题
Scrapy中提供item,作为数据格式的声明位置.
可以在items.py文件提前定义好,该爬虫在进行数据传输时的数据格式,然后再写代码的时候,就有了数据名称的依据了.
- item.py文件
import scrapy
class GameItem(scrapy.Item):
# 定义数据结构
name = scrapy.Field()
category = scrapy.Field()
date = scrapy.Field()
class Person:
private String name;
private int age;
dic = {name: "alex",age: 18}
p = Person( "alex",18)
- spiders下的youxi.py
from mySpider_2.items import GameItem
# 以下代码在spider中的parse替换掉原来的字典
item = GameItem()
# 封装数据 到 item中
item["name"] = name
item["category"] = category
item["date"] = date
yield item
- pipelines.py
class Myspider2Pipeline:
def process_item(self,item,spider):
# 真正存储的地方
print(item) # 从item中取出数据,进行持久化存储
return item # 一定不要忘了return item,交给后续的pipline继续使用
七、scrapy使用小总结
至此,我们对scrapy有了一个非常初步的了解和使用. 快速总结一下scrapy框架的使用流程:
-
创建爬虫项目.
scrapy startproject 项目名
-
进入项目目录.
cd 项目名
-
创建爬虫
scrapy genspider 爬虫名 抓取网站的域名
-
编写
item.py
文件,定义好数据传输类 item -
调整spider
修改spider中的parse方法,对返回的响应response对象进行解析, 返回item
-
在pipeline中对数据进行保存工作.
-
调整settings配置文件
将pipeline设置为生效,并设置好优先级
干掉robots协议
干掉日志的信息(想留下有用的内容), 只需要调整日志记录级别
LOG_LEVEL = "WARNING" -
启动爬虫
scrapy crawl 爬虫名