scrapy框架的学习
介绍
前面我们学习了基础的爬虫实现方法和selenium以及mongodb数据库,那么接下来会我们学习一个上场率非常高的爬虫框架:scrapy
内容
scrapy的基础概念和工作流程
scrapy入门使用
scrapy的深入
crawlspider的使用
2.3.1scrapy的基础概念和流程
目标
了解学习scrapy的目的
能够说出异步和非阻塞的区别
掌握scrapy的流程
- 为什么要学习scrapy
通过前面的学习,我们已经能够解决90%的爬虫问题了,那么scrapy是为了解决剩下的10%的问题么,不是,scrapy框架能够让我们的爬虫效率更高
那么剩下的10%怎么办呢?在课堂上我们学习的是通用的爬虫技能和爬虫方法,剩下的10%大家可能以后都不会遇到,或者在公司中再去学习
- 什么是scrapy
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速的抓取。
Scrapy 使用了Twisted['twɪstɪd]异步网络框架,可以加快我们的下载速度。
文档地址:http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/overview.html
- 异步和非阻塞的区别
前面我们说Twisted是一个异步的网络框架,经常我们也听到一个词语叫做非阻塞,那么他们有什么区别呢?
异步:调用在发出之后,这个调用就直接返回,不管有无结果
非阻塞:关注的是程序在等待调用结果(消息,返回值)时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程。
4. scrapy是工作流程
4.1 回顾之前的爬虫流程
4.2 上面的流程可以改写为
4.3 scrapy的流程
其流程可以描述如下:
调度器把requests–>引擎–>下载中间件—>下载器
下载器发送请求,获取响应---->下载中间件---->引擎—>爬虫中间件—>爬虫
爬虫提取url地址,组装成request对象---->爬虫中间件—>引擎—>调度器
爬虫提取数据—>引擎—>管道
管道进行数据的处理和保存
注意:
图中绿色线条的表示数据的传递
注意图中中间件的位置,决定了其作用
注意其中引擎的位置,所有的模块之前相互独立,只和引擎进行交互
4.4 scrapy中每个模块的具体作用
小结
本小结重点
掌握scrapy中数据的传递过程
掌握scrapy中每个模块的作用
理解异步和非阻塞的区别
2.3.2scrapy的入门使用(一)
目标
掌握如何创建项目
掌握如何创建爬虫
熟悉创建项目后每个文件的作用
掌握pipeline的使用
掌握scrapy中logging的使用
- scrapy项目实现流程
创建一个scrapy项目:scrapy startproject mySpider
生成一个爬虫:scrapy genspider itcast "itcast.cn
提取数据:完善spider,使用xpath等方法
保存数据:pipeline中保存数据
- 创建scrapy项目
下面以抓取传智师资库来学习scrapy的入门使用:http://www.itcast.cn/channel/teacher.shtml
命令:scrapy startproject +<项目名字>
示例:scrapy startproject myspider
生成的目录和文件结果如下:
- 创建爬虫
命令:scrapy genspider +<爬虫名字> + <允许爬取的域名>
示例:scrapy genspider itcast itcast.cn
生成的目录和文件结果如下:
- 完善spider
完善spider即通过方法进行数据的提取等操作
注意:
response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
extract() 返回一个包含有字符串的列表
extract_first() 返回列表中的第一个字符串,列表为空没有返回None
spider中的parse方法必须有
需要抓取的url地址必须属于allowed_domains,但是start_urls中的url地址没有这个限制
启动爬虫的时候注意启动的位置,是在项目路径下启动
5. 数据传递到pipeline
为什么要使用yield?
让整个函数变成一个生成器,有什么好处呢?
遍历这个函数的返回值的时候,挨个把数据读到内存,不会造成内存的瞬间占用过高
python3中的range和python2中的xrange同理
注意:
yield能够传递的对象只能是:BaseItem,Request,dict,None
6. 完善pipeline
pipeline在settings中能够开启多个,为什么需要开启多个?
不同的pipeline可以处理不同爬虫的数据
不同的pipeline能够进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存
pipeline使用注意点
使用之前需要在settings中开启
pipeline在setting中键表示位置(即pipeline在项目中的位置可以自定义),值表示距离引擎的远近,越近数据会越先经过
有多个pipeline的时候,process_item的方法必须return item,否则后一个pipeline取到的数据为None值
pipeline中process_item的方法必须有,否则item没有办法接受和处理
process_item方法接受item和spider,其中spider表示当前传递item过来的spider
7. 输出日志LOG的设置
为了让我们自己希望输出到终端的内容能容易看一些,我们可以在setting中设置log级别
在setting中添加一行(全部大写):LOG_LEVEL = "WARNING”
默认终端显示的是debug级别的log信息
小结
本小结重点
掌握如何创建项目
掌握如何创建爬虫
熟悉创建项目后每个文件的作用
掌握pipeline的使用
2.3.3scrapy的入门使用(二)
目标
掌握如何构造和发送请求
掌握Item的使用
掌握meta参数的使用
- scrapy实现翻页请求
对于要提取如下图中所有页面上的数据该怎么办?
回顾requests模块是如何实现翻页请求的:
找到下一页的URL地址
调用requests.get(url)
思路:
找到下一页的url地址
构造url地址的请求,传递给引擎
1.1 实现翻页请求
使用方法
在获取到url地址之后,可以通过scrapy.Request(url,callback)得到一个request对象,通过yield关键字就可以把这个request对象交给引擎
具体使用
添加User-Agent
同时可以再在setting中设置User-Agent:
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
通过爬取腾讯招聘的页面的招聘信息,学习如何实现翻页请求
地址:http://hr.tencent.com/position.php
思路分析:
获取首页的数据
寻找下一页的地址,进行翻页获取数据
1.2 scrapy.Request的更多参数
scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])
注意:
括号中的参数为可选参数
callback:表示当前的url的响应交给哪个函数去处理
meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等
dont_filter:默认会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
2. 定义Item
定义Item的原因
定义item即提前规划好哪些字段需要抓取,scrapy.Field()仅仅是提前占坑,通过item.py能够让别人清楚自己的爬虫是在抓取什么,同时定义好哪些字段是需要抓取的,没有定义的字段不能使用,防止手误
定义Item
使用Item
Item使用之前需要先导入并且实例化,之后的使用方法和使用字典相同
from yangguang.items import YangguangItem
item = YangguangItem() #实例化
通过爬取阳光热线问政平台来学习item的使用
目标:所有的投诉帖子的编号、帖子的url、帖子的标题和内容,url: http://wz.sun0769.com/index.php/question/questionType?type=4&page=0
思路分析
列表页的数据
确定列表页url地址和程序结束条件
存在下一页时
不存在下一页时
确定详情页的数据
数据从列表页传递到详情页使用:meta参数
包含图片时
不包含图片时
动手
抓取https://www.guokr.com/ask/highlight/上列表页的内容
小结
本小结重点
掌握scrapy发送请求的方法
知道scrapy中dont_filer的使用
掌握scrapy中meta参数的使用
掌握scrapy中Item的使用
2.3.4scrapy的深入使用
目标
认识scrapy的debug信息
知道scrapy shell如何使用
认识setting中的配置一起配置的使用
-
认识scrapy的debug信息
每次程序启动后,默认情况下,终端都会出现很多的debug信息,那么下面我们来简单认识下这些信息 -
scrapy shell的使用
scrapy shell是scrapy提供的一个终端工具,能够通过它查看scrapy中对象的属性和方法,以及测试xpath
使用方法:scrapy shell http://www.itcast.cn/channel/teacher.shtml
在终端输入上述命令后,能够进入python的交互式终端
小知识点:
response.url:当前响应的url地址
response.request.url:当前响应对应的请求的url地址
response.headers:响应头
response.body:响应体,也就是html代码,默认是byte类型
response.requests.headers:当前响应的请求头
3. 认识scrapy中的setting文件
为什么项目中需要配置文件
在配置文件中存放一些公共变量,在后续的项目中便便修改,注意其中的变量名一般全部大写
配置文件中的变量使用方法
导入即可使用
settings.py中的重点字段和内涵
USER_AGENT 设置ua
ROBOTSTXT_OBEY 是否遵守robots协议,默认是遵守
CONCURRENT_REQUESTS 设置并发请求的数量,默认是16个
DOWNLOAD_DELAY 下载延迟,默认无延迟
COOKIES_ENABLED 是否开启cookie,即每次请求带上前一次的cookie,默认是开启的
DEFAULT_REQUEST_HEADERS 设置默认请求头
SPIDER_MIDDLEWARES 爬虫中间件,设置过程和管道相同
DOWNLOADER_MIDDLEWARES 下载中间件
4. 管道中的open_spider和close_spider 的方法
在管道中,除了必须定义process_item之外,还可以定义两个方法:
open_spider(spider) :能够在爬虫开启的时候执行一次
close_spider(spider) :能够在爬虫关闭的时候执行一次
所以,上述方法经常用于爬虫和数据库的交互,在爬虫开启的时候建立和数据库的连接,在爬虫关闭的时候断开和数据库的连接
下面的代码分别以操作文件和mongodb为例展示方法的使用:
- 动手
需求:爬取苏宁易购所有下所有图书和图书分类信息,以及子链接页面的价格内容。
url : https://book.suning.com/
思路分析:
确定数据抓取的入口 数据包含一个大分类,每个大分类包含多个中间分类,每个中间分类包含多个小分类
注意其中中间分类的位置并不在大分类中,小分类的位置也不在中间分类中
大分类的位置如下:
中间分类和小分类的位置如下:
确定url和程序终止的条件 列表页首页的地址就是小分类的地址,通过请求这些地址,能够进入列表页的首页
但是翻页的url地址不在响应中,而且每个页面的数据是由两个请求获取的
通过抓包,能够知道URL地址的规律,pagenum每次+1
那么如果确定什么时候程序终止呢?
通过观察url地址对应的响应,能够知道总的页码数和当前页码数,当前页码数<总的页码数时,能够构造出下一页的请求
翻页的两个url地址为:
next_url_temp_1 = “https://list.suning.com/emall/showProductList.do?ci={}&pg=03&cp={}&il=0&iy=0&adNumber=0&n=1&ch=4&sesab=ABBAAA&id=IDENTIFYING&cc=010”
next_url_temp_2 = “https://list.suning.com/emall/showProductList.do?ci={}&pg=03&cp={}&il=0&iy=0&adNumber=0&n=1&ch=4&sesab=ABBAAA&id=IDENTIFYING&cc=010&paging=1&sub=0”
其中ci的值和cp的值在变化,ci为url地址中的参数,cp为页码数
确定列表页数据的位置
列表页的数据都在li标签下,数据都在url地址对应的响应中
但是注意第一页的数据是由小分类的url地址和另一个ajax请求共同获取的,ajax中的数据位置
价格数据的位置 价格不再url地址对应的响应中,而在:https://pas.suning.com/nspcsale_0_000000000167229140_000000000167229140_0070121210_10_010_0100101_226503_1000000_9017_10106____R9011184_1.1.html?callback=pcData&_=1526313493577
这个url地址中的前三个参数在url地址中,后两个参数在详情页url地址的响应中,对应的临时的url地址为:https://pas.suning.com/nspcsale_0_000000000{}000000000{}{}10_010_0100101_226503_1000000_9017_10106___{}{}.html?callback=pcData&=1526011028849
小结
本小结重点
认识scrapy的debug信息
知道scrapy shell如何使用
认识setting中的配置一起配置的使用
2.3.5crawlspider类的使用
目标
掌握crawlspider爬虫的创建方式
掌握crawlspdier中rules的书写
能够通过crawlspider类完成一个爬虫
- crawlspider是什么
回顾之前的代码中,我们有很大一部分时间在寻找下一页的url地址或者是内容的url地址上面,这个过程能更简单一些么?
思路:
从response中提取所有的满足规则的url地址
自动的构造自己requests请求,发送给引擎
对应的crawlspider就可以实现上述需求,匹配满足条件的url地址,才发送给引擎,同时能够指定callback函数
- 认识crawlspider爬虫
2.1 创建crawlspdier爬虫的命令
scrapy genspider –t crawl itcast itcast.cn
2.2 观察爬虫内的默认内容
spider中默认生成的内容如下,其中重点在rules中
rules是一个元组或者是列表,包含的是Rule对象
Rule表示规则,其中包含LinkExtractor,callback和follow
LinkExtractor:连接提取器,可以通过正则或者是xpath来进行url地址的匹配
callback :表示经过连接提取器提取出来的url地址响应的回调函数,可以没有,没有表示响应不会进行回调函数的处理
follow:表示进过连接提取器提取的url地址对应的响应是否还会继续被rules中的规则进行提取,True表示会,Flase表示不会
class Itcast1Spider(CrawlSpider):
name = 'itcast1'
allowed_domains = ['itcast.cn']
start_urls = ['http://itcast.cn/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
i = {}
#使用xpath进行数据的提取或者url地址的提取
return i
2.3 crawlspider的使用
通过crawlspider爬取腾讯招聘的详情页的招聘信息,url:http://hr.tencent.com/position.php
思路分析:
列表页翻页
定义一个规则,来进行列表页的翻页,follow需要设置为True,列表页url地址的规律如下
详情页翻页
定义一个规则,实现从列表页进入详情页,并且指定回调函数
详情页数据的提取
在详情页url地址对应的回调函数中,实现数据提取
2.4 crawlspider使用的注意点
2.5 crawlspider的补充知识点(了解)
小结
本小结重点
掌握crawlspider创建爬虫的命令
掌握crawlspider中规则的编写
掌握Rule中callback和follow的使用方式
2.3.6下载中间件和模拟登陆
目标
掌握常见的下载中间件的编写方式
掌握模拟登陆的思路
掌握scrapy中模拟登陆的三种方式
- scrapy中下载中间件的使用
使用方法:
编写一个Downloader Middlewares和我们编写一个pipeline一样,定义一个类,然后在setting中开启
Downloader Middlewares默认的方法:
process_request(self, request, spider):
当每个request通过下载中间件时,该方法被调用。
返回None值:继续请求
返回Response对象:不在请求,把response返回给引擎
返回Request对象:把request对象交给调度器进行后续的请求
process_response(self, request, response, spider):
- 当下载器完成http请求,传递响应给引擎的时候调用
- 返回Resposne:交给process_response来处理
- 返回Request对象:交给调取器继续请求
定义实现随机User-Agent的下载中间件
class UserAgentMiddleware(object):
def process_request(self,request,spider):
agent = random.choice(agents)
request.headers['User-Agent'] = agent
定义实现随机使用代理的下载中间件
class ProxyMiddleware(object):
def process_request(self,request,spider):
proxy = random.choice(proxies)
request.meta['proxy'] = proxy
User-Agent池在这里
USER_AGENTS = [ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5" ]
- 使用scrapy进行模拟登陆
2.1 回顾之前的模拟登陆的方法
requests是如何模拟登陆的?
直接携带cookies请求页面
找接口发送post请求存储cookie
selenium是如何模拟登陆的?
找到对应的input标签,输入文字点击登录
scrapy来说,有两个方法模拟登陆:
1、直接携带cookie
2、找到发送post请求的url地址,带上信息,发送请求
2.2 scrapy携带cookie进行模拟登陆
携带cookie进行模拟登陆应用场景:
cookie过期时间很长,常见于一些不规范的网站
能在cookie过期之前把搜有的数据拿到
配合其他程序使用,比如其使用selenium把登陆之后的cookie获取到保存到本地,scrapy发送请求之前先读取本地cookie
scrapy的start_requests方法的学习
scrapy中start_url是通过start_requests来进行处理的,其实现代码如下
def start_requests(self):
cls = self.__class__
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
for url in self.start_urls:
yield Request(url, dont_filter=True)
所以对应的,如果start_url地址中的url是需要登录后才能访问的url地址,则需要重写start_request方法并在其中手动添加上cookie
实现携带cookie登录人人网
注意:scrapy中cookie不能够放在headers中,在构造请求的时候有专门的cookies参数,能够接受字典形式的coookie
import scrapy
import re
class RenrenSpider(scrapy.Spider):
name = 'renren'
allowed_domains = ['renren.com']
start_urls = ['http://www.renren.com/941954027/profile']
def start_requests(self):
cookie_str = "cookie_str"
cookie_dict = {i.split("=")[0]:i.split("=")[1] for i in cookie_str.split("; ")}
yield scrapy.Request(
self.start_urls[0],
callback=self.parse,
cookies=cookie_dict,
# headers={"Cookie":cookie_str}
)
def parse(self, response):
ret = re.findall("新用户287",response.text)
print(ret)
yield scrapy.Request(
"http://www.renren.com/941954027/profile?v=info_timeline",
callback=self.parse_detail
)
def parse_detail(self,response):
ret = re.findall("新用户287",response.text)
print(ret)
在settings中开启cookie_debug
在settings.py中通过设置COOKIES_DEBUG=TRUE 能够在终端看到cookie的传递传递过程
2.3 scrapy发送post请求
scrapy中发送post请求的方法 通过scrapy.FormRequest能够发送post请求,同时需要添加fromdata参数作为请求体,以及callback
yield scrapy.FormRequest(
"https://github.com/session",
formdata={
"authenticity_token":authenticity_token,
"utf8":utf8,
"commit":commit,
"login":"noobpythoner",
"password":"zhoudawei123"
},
callback=self.parse_login
)
使用scrapy模拟登陆github
思路分析
找到post的url地址
点击登录按钮进行抓包,然后定位url地址为https://github.com/session
找到请求体的规律
分析post请求的请求体,其中包含的参数均在前一次的响应中
验证是否登录成功
通过请求个人主页,观察是否包含用户名
代码实现如下:
#spider/github.py
# -*- coding: utf-8 -*-
import scrapy
import re
class GithubSpider(scrapy.Spider):
name = 'github'
allowed_domains = ['github.com']
start_urls = ['https://github.com/login']
def parse(self, response):
authenticity_token = response.xpath("//input[@name='authenticity_token']/@value").extract_first()
utf8 = response.xpath("//input[@name='utf8']/@value").extract_first()
commit = response.xpath("//input[@name='commit']/@value").extract_first()
yield scrapy.FormRequest(
"https://github.com/session",
formdata={
"authenticity_token":authenticity_token,
"utf8":utf8,
"commit":commit,
"login":"noobpythoner",
"password":"***"
},
callback=self.parse_login
)
def parse_login(self,response):
ret = re.findall("noobpythoner",response.text,re.I)
print(ret)
2.3 scrapy进行表单提交
方法介绍
scrapy中具有一个方法:scrapy.Formrequest.from_response能够自动的从响应中寻找form表单,然后把formdata中的数据提交到action对应的url地址中
使用实例如下
def parse(self, response):
yield scrapy.FormRequest.from_response(
response,#自动的从中寻找action对应的url地址
formdata={
"login":"noobpythoner",
"password":"***"
},
callback = self.parse_login
)
使用scrapy.Formrequest.from_response进行模拟登陆github
小结
本小结重点
掌握下载中间件的使用方式
了解中间件中的方法和返回值
掌握scrapy携带cookie进行登录的方法
掌握scrapy发送post请求的方法
掌握scrapy进行表单提交的方法