爬虫提高之scrapy框架

scrapy框架的学习
介绍
前面我们学习了基础的爬虫实现方法和selenium以及mongodb数据库,那么接下来会我们学习一个上场率非常高的爬虫框架:scrapy

内容
scrapy的基础概念和工作流程
scrapy入门使用
scrapy的深入
crawlspider的使用

2.3.1scrapy的基础概念和流程
目标
了解学习scrapy的目的
能够说出异步和非阻塞的区别
掌握scrapy的流程

  1. 为什么要学习scrapy

通过前面的学习,我们已经能够解决90%的爬虫问题了,那么scrapy是为了解决剩下的10%的问题么,不是,scrapy框架能够让我们的爬虫效率更高

那么剩下的10%怎么办呢?在课堂上我们学习的是通用的爬虫技能和爬虫方法,剩下的10%大家可能以后都不会遇到,或者在公司中再去学习

  1. 什么是scrapy
    Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速的抓取。

Scrapy 使用了Twisted['twɪstɪd]异步网络框架,可以加快我们的下载速度。

文档地址:http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/overview.html

  1. 异步和非阻塞的区别
    前面我们说Twisted是一个异步的网络框架,经常我们也听到一个词语叫做非阻塞,那么他们有什么区别呢?

异步:调用在发出之后,这个调用就直接返回,不管有无结果
非阻塞:关注的是程序在等待调用结果(消息,返回值)时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程。
4. scrapy是工作流程
4.1 回顾之前的爬虫流程

4.2 上面的流程可以改写为

4.3 scrapy的流程

其流程可以描述如下:

调度器把requests–>引擎–>下载中间件—>下载器
下载器发送请求,获取响应---->下载中间件---->引擎—>爬虫中间件—>爬虫
爬虫提取url地址,组装成request对象---->爬虫中间件—>引擎—>调度器
爬虫提取数据—>引擎—>管道
管道进行数据的处理和保存
注意:

图中绿色线条的表示数据的传递
注意图中中间件的位置,决定了其作用
注意其中引擎的位置,所有的模块之前相互独立,只和引擎进行交互
4.4 scrapy中每个模块的具体作用

小结
本小结重点
掌握scrapy中数据的传递过程
掌握scrapy中每个模块的作用
理解异步和非阻塞的区别

2.3.2scrapy的入门使用(一)
目标
掌握如何创建项目
掌握如何创建爬虫
熟悉创建项目后每个文件的作用
掌握pipeline的使用
掌握scrapy中logging的使用

  1. scrapy项目实现流程
    创建一个scrapy项目:scrapy startproject mySpider

生成一个爬虫:scrapy genspider itcast "itcast.cn

提取数据:完善spider,使用xpath等方法

保存数据:pipeline中保存数据

  1. 创建scrapy项目
    下面以抓取传智师资库来学习scrapy的入门使用:http://www.itcast.cn/channel/teacher.shtml

命令:scrapy startproject +<项目名字>

示例:scrapy startproject myspider

生成的目录和文件结果如下:

  1. 创建爬虫
    命令:scrapy genspider +<爬虫名字> + <允许爬取的域名>

示例:scrapy genspider itcast itcast.cn

生成的目录和文件结果如下:

  1. 完善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参数的使用

  1. 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中的配置一起配置的使用

  1. 认识scrapy的debug信息
    每次程序启动后,默认情况下,终端都会出现很多的debug信息,那么下面我们来简单认识下这些信息

  2. 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为例展示方法的使用:

  1. 动手
    需求:爬取苏宁易购所有下所有图书和图书分类信息,以及子链接页面的价格内容。

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类完成一个爬虫

  1. crawlspider是什么
    回顾之前的代码中,我们有很大一部分时间在寻找下一页的url地址或者是内容的url地址上面,这个过程能更简单一些么?

思路:

从response中提取所有的满足规则的url地址
自动的构造自己requests请求,发送给引擎
对应的crawlspider就可以实现上述需求,匹配满足条件的url地址,才发送给引擎,同时能够指定callback函数

  1. 认识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中模拟登陆的三种方式

  1. 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" ]
  1. 使用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进行表单提交的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值