Scrapy框架的使用
2021.4.8 投了一个python爬虫岗位 呼~
希望能入职😭
Scrapy框架的使用
之前学习了pyspider框架的用法,可以利用它快速完成爬虫的编写。但是pyspider框架自身存在一些缺点,比如说:
- 可配置程度低
- 异常处理能力有限
- 对于强反爬网站力不从心
而Scrapy框架更加强大, 爬取效率高,扩展组件非常多,可配置、可扩展程度高,应用也最广泛。
介绍
Scrapy基于Twisted的异步处理框架,是纯Python实现的,架构清晰,模块之间耦合度低,非常灵活。同时非常轻便,只需要定制开发几个模块就可以轻松实现一个爬虫。通过多个组件之间的相互协作,不同组件完成各自的任务,以及组件对异步的支持,Scrapy最大限度地提高了网络带宽,提高了数据爬取效率
Spiders
定义了爬取的逻辑和网页中抽取的项目,主要负责解析响应并生成提取结果和新的请求Spider Middlewares
中间件,主要处理爬虫输入的响应和输出的结果以及新的请求Engine
处理整个系统的数据流、触发事务,是框架的核心Scheduler
调度器,接受引擎发送的请求并将其加入队列,在引擎再次请求时提供该请求给下载器Downloader
下载网页内容,并将网页内容返回给爬虫Downloader Middlewares
主要处理引擎与下载器之间的请求和响应Item Pipeline
负责处理爬虫从网页中抽取的内容,清洗、验证和存储数据Item
定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象
数据流
- Engine首先打开一个网站,找到处理该网站的Spider。
- Engine从Spider获取第一个要爬取的URL,并通过Scheduler以Request的形式调度
- Engine向Scheduler请求下一个要爬取的URL
- Scheduler返回下一个要爬取的URL给Engine,后者将该URL通过Downloader Middlewares发送给Downloader
- 一旦页面下载完毕,Downloader生成该页面的Response,并将其通过Downloader Middlewares发送给Engine
- Engine从下载器中接收到Response,并将其通过Spider Middlewares发送给Spider处理
- Spider处理Response,并返回爬取到的Item及新的Request给Engine
- Engine将Spider返回的Item给Item Pipeline,将新的Request给Scheduler
- 重复第2~第8步,直到Scheduler中没有更多的Request,Engine关闭该网站,爬取结束
使用方法
安装
pip3 install Scrapy
初始化Scrapy项目
初始化一个名为tutorial的scrapy项目:scrapy startproject tutorial
此时tuorial的目录结构:
创建爬虫
新建一个名为quotes的爬虫爬取quotes.toscrape.com这个网站:scrapy genspider quotes quotes.toscrape.com
查看quotes.py的内容:
import scrapy |
可以看到有三个属性:name、allowed_domains、start_urls和一个方法parse
- name是每个项目唯一的名字
- allowed_domains是目标网站
- start_urls是spider启动时爬取的url列表,初始请求由它来定义
- parse:默认情况下,被调用时start_urls里面的链接构成的请求完成下载执行之后,返回的响应就会作为唯一的参数传递给这个函数。parse函数负责解析返回的响应、提取数据或者进一步生成要处理的请求。
创建Item
Item时保存爬取数据的容器,使用方法与字典类似。
但相比字典多了额外的保护机制,可以避免拼写错误或者定义字段错误
创建Item需要继承scrapy.Item类,并且定义scrapy.Field字段。一般可以获取到的内容有text、author、tags。
定义Item时将items.py修改如下:
import scrapy |
解析Response
前面的代码可以看到parse()方法的参数response时start_urls里面的链接爬取后的结果。
所以在parse()方法中,我们可以直接对response变量包含的内容进行解析,从中可以得到网页源代码,找出结果中的链接而得到下一个请求。
查看http://quotes.toscrape.com/
其网页结构可以发现,每一页都是由多个class为quote的区块构成,而每个区块又包含text、author、tags。那么可以先找出quote然后提取出里面的内容
后续Request
上面的操作实现了从初始页面抓取内容。下一页的内容由上一页抓取的内容构建下一个请求获得。
底部的Next按钮查看源代码后可以发现链接时/page/2,全链接就是:https://quotes.toscrape.com/page/2
通过这个链接可以构建下一个请求
scrapy.Request
构造请求时需要用到scrapy.Request,这里需要传递两个参数url、callback- url: 要请求的链接
- callback: 回调函数。当指定了该回调函数的请求完成之后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求,回调函数如parse()所示。
实际上parse()就是解析text、author、tags的方法。下一页的结构和第一页已经解析过的页面结构时一样的,所以可以复用代码。
利用选择器得到下一页链接并生成请求,在parse()方法后追加如下代码:
next = response.css('.pager .next a::attr("href")').extract_first() |
第一句代码首先通过CSS选择器获取下一个页面的链接,即要获取a超链接中的href属性。这里用到了::attr(href)操作。然后再调用extract_first方法获取内容。
第二句代码调用了urljoin()方法,urljoin()方法可以将相对URL构造成绝对url。
第三句代码通过url和callback变量构造了一个新的请求,回调函数callback依然使用parse()方法。这个请求完成后,响应会重新经过parse方法处理,得到第二页的解析结果。
改写后的Spider类如下:
import scrapy |
启动
scrapy crawl quotes
scrapy首先会输出当前版本信息和正在启动的项目和一些配置情况
- 抓取的数据
保存文件
- 将结果保存为JSON文件
scrapy crawl quotes -o quotes.json
每一个Item输出一行JSON,输出后缀为jl,为jsonline的缩写:
scrapy crawl quotes -o quotes.jl
或:
scrapy crawl quotes -o quotes.jsonlines
输出格式还支持很多种,例如csv、xml、pickle、marshal等,还支持ftp、s3等远程传输,还可以通过自定义ItemExporter实现其他的输出
scrapy crawl quotes -o quotes.csv
scrapy crawl quotes -o quotes.xml
scrapy crawl quotes -o quotes.pickle
scrapy crawl quotes -o quotes.marshal
scrapy crawl quotes -o ftp://user:pass@ftp.example.com/path/to/quotes.csv
如果要输出到数据库,需要使用Item Pileline
使用Item Pipeline
负责处理爬虫从网页中抽取的内容,清洗、验证和存储数据
- 清理HTML数据
- 验证爬取数据,检查爬取字段
- 查重并且丢弃重复数据
- 将爬取的结果保存到数据库
使用方法
要实现Item Pipeline只需要定义一个类并实现process_item()方法即可。启用Item Pipeline后,Item Pipeline会自动调用这个方法。process_item方法必须返回包含数据的字典或Item对象,或者抛出DropItem异常
process_item()方法只有两个参数:
- item:由Spider生成的Item,相当于“原材料”
- spider:就是Spider实例
步骤:
修改项目中的pipelines.py
删除用命令行自动生成的文件内容,增加一个TextPipeline类,内容如下:from scrapy.exceptions import DropItem
class TutorialPipeline:
def __init__(self):
# 定义限制长度为50
self.limit = 50
def process_item(self, item, spider):
# 判断item的text属性是否存在,不存在则抛出DropItem异常
if item['text']:
# 大于50就截断然后拼接省略号,再将item返回
if len(item['text']) > self.limit:
item['text'] = item['text'][0:self.limit].rstrip() + '…'
return item
else:
return DropItem('Missing Text')存入数据库
将处理好的item——“加工后的材料”存入MongoDB,需要定义另一个Pipeline,同样在pipelines.py
里:
class MongoPipeline(object): |
- 修改setting.py
添加以下内容:
# fuzhi ITEM_PIPELINES字典,键名是Pipeline的类名称,键值是调用优先级,数字越小对应的Pipeline越先调用 |
本次爬取的数据存入的事是Mongodb数据库,用的是Mongodb提供的免费的网络集群,使用方法可以自行百度
点击CONNECT选择对应的python版本可以获得对应的MONGO_URI
将其复制并修改对应的密码即可
- 启动爬取
scrapy crawl quotes
我下载了Mongodb的GUI,可以看到对应的结果为:
完整代码在Github: https://github.com/Leekinghou/tutorial
2021.4.12 面试顺利通过了,开局一张笔试题主要是问python特性:
- python的特点
- 迭代器、生成器、可迭代对象和应用场景 + 解释装饰器
- 数据类型,可变/不可变类型
- *args、**kwargs的区别
- 两段代码的区别
- 手撕排序算法
- 赋值、浅拷贝、深拷贝
- map和zip的区别
- 其他
面试询问:
- Scrapy框架的组成、数据流动流程
- 反爬机制、应对方法
- 遇到过最有难度的目标网站
- 对OpenCV的了解(简历中的项目)
- 对Mysql、redis、Mongodb的应用
- 对Item pipeline的了解
- 对事务的了解
- 左连接是什么?
但是只开6k,租房吃饭不剩多少钱了🤦♂️
不去了叭。。