scrapy 详解

 1、什么是scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速的抓取。Scrapy 使用了Twisted[‘tw?st?d]异步网络框架

文档地址:https://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/commands.html

2、scrapy环境配置  

pip install Scrapy

3、scrapy的流程

scrapy 详解

其流程可以描述如下:

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

注意:

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

scrapy中每个模块的具体作用

scrapy 详解

4、创建scrapy项目

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

#示例:
scrapy startproject myspider

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

scrapy 详解

5、 创建爬虫

首先进入创建的项目目录里面的spiders目录,然后执行下面的命令创建爬虫

# 命令:
scrapy genspider +<爬虫名字> + <允许爬取的域名>

# 示例:
scrapy genspider itcast itcast.cn

scrapy 详解

6、完善spider, 将数据传递到pipeline

完善spider即通过方法进行数据的提取等操作 

# -*- coding: utf-8 -*-
import scrapy


class ItcastSpider(scrapy.Spider):
    name = ‘itcast‘  # 爬虫名  [爬虫启动时,使用scrapy crawl itcast]
    allowed_domains = [‘itcast.cn‘]  # 允许爬取的范围,防止爬虫爬到其他网站
    start_urls = [‘http://www.itcast.cn/channel/teacher.shtml‘]  # 爬虫最开始抓取的url地址

    def parse(self, response):  # 数据提取方法,处理start_url地址中的响应,接受下载中间件传过来的response响应

        # 先分组,再进行数据的提取
        li_list = response.xpath(‘//div[@class="tea_con"]/div/ul/li‘)
        for li in li_list:
            item = {}
            item[‘name‘] = li.xpath(‘.//h3/text()‘).extract_first()
            item[‘title‘] = li.xpath(‘.//h4/text()‘).extract_first()
            item[‘desc‘] = li.xpath(‘.//p/text()‘).extract_first()
            print(item)
            yield item   # 将数据传给pipeline 

注意:

  1. response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
  2. extract() 返回一个包含有字符串的列表
  3. extract_first() 返回列表中的第一个字符串,列表为空没有返回None
  4. spider中的parse方法必须有
  5. 需要抓取的url地址必须属于allowed_domains,但是start_urls中的url地址没有这个限制
  6. 启动爬虫的时候注意启动的位置,是在项目路径下启动

为什么要使用yield?

  • 让整个函数变成一个生成器,有什么好处呢?
  • 遍历这个函数的返回值的时候,挨个把数据读到内存,不会造成内存的瞬间占用过高
  • python3中的range和python2中的xrange同理

注意:

  • yield能够传递的对象只能是:BaseItemRequestdictNone

7、完善pipeline 

scrapy 详解

scrapy 详解

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

8、 scrapy启动爬虫项目

# 进去爬虫项目目录,执行以下命令
scrapy crawl "爬虫名"

scrapy 详解

8、 scrapy实现翻页请求

对于要提取如下图中所有页面上的数据该怎么办?

scrapy 详解

回顾requests模块是如何实现翻页请求的:

  • 找到下一页的URL地址
  • 调用requests.get(url)

思路:

  1. 找到下一页的url地址
  2. 构造url地址的请求,传递给引擎

8.1 实现翻页请求

  1. 使用方法

    在获取到url地址之后,可以通过scrapy.Request(url,callback)得到一个request对象,通过yield关键字就可以把这个request对象交给引擎

  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中的地址会被反复请求,否则程序不会启动
# -*- coding: utf-8 -*-
import re
from copy import deepcopy
import scrapy


class SnSpider(scrapy.Spider):
    name = ‘sn‘
    allowed_domains = [‘suning.com‘]
    start_urls = [‘https://book.suning.com/‘]

    def parse(self, response):
           # 解析数据
           pass
           # 获取下一页url地址
           next_part_url = ""
           yield scrapy.Request(next_part_url, callback=self.parse_book_list, meta={"item": deepcopy(item)})
           # scrapy.Request()能构建一个request对象,同时指定提取数据的callback函数 

    def parse_next_list(self, response):
        # 处理下一页的数据提取

3. 添加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‘

9、 定义Item

  1. 定义Item的原因

    定义item即提前规划好哪些字段需要抓取,scrapy.Field()仅仅是提前占坑,通过item.py能够让别人清楚自己的爬虫是在抓取什么,同时定义好哪些字段是需要抓取的,没有定义的字段不能使用,防止手2

  2. 定义Item

  scrapy 详解 

3. 使用Item

  Item使用之前需要先导入并且实例化,之后的使用方法和使用字典相同   

from yangguang.items import YangguangItem
 item = YangguangItem() #实例化  

10、 输出日志LOG的设置

为了让我们自己希望输出到终端的内容能容易看一些,我们可以在setting中设置log级别

在setting中添加一行(全部大写):LOG_LEVEL = "WARNING”

默认终端显示的是debug级别的log信息

  scrapy的debug信息

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

scrapy 详解

11、认识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 下载中间件

12、管道中的open_spiderclose_spider 的方法

在管道中,除了必须定义process_item之外,还可以定义两个方法:

  • open_spider(spider) :能够在爬虫开启的时候执行一次
  • close_spider(spider) :能够在爬虫关闭的时候执行一次

所以,上述方法经常用于爬虫和数据库的交互,在爬虫开启的时候建立和数据库的连接,在爬虫关闭的时候断开和数据库的连接

下面的代码分别以操作文件和mongodb为例展示方法的使用:

scrapy 详解

 scrapy 详解

13、crawlspider类的使用

  1. crawlspider是什么

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

思路:

  • 从response中提取所有的满足规则的url地址
  • 自动的构造自己requests请求,发送给引擎

对应的crawlspider就可以实现上述需求,匹配满足条件的url地址,才发送给引擎,同时能够指定callback函数

  2. 认识crawlspider爬虫

  2.1 创建crawlspdier爬虫的命令

scrapy genspider –t crawl itcast itcast.cn

  2.2 观察爬虫内的默认内容

spider中默认生成的内容如下,其中重点在rules中

  • rules是一个元组或者是列表,包含的是Rule对象
  • Rule表示规则,其中包含LinkExtractor,callbackfollow
  • 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的使用

指定两个正则规则,匹配url

rules = (
        Rule(LinkExtractor(allow=r‘position_detail.php\?id=\d+&keywords=&tid=0&lid=0‘), callback=‘parse_item‘),
        Rule(LinkExtractor(allow=r‘position.php\?&start=\d+#a‘), follow=True),
    )
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class TcSpider(CrawlSpider):
    name = ‘tc‘
    allowed_domains = [‘tencent.com‘]
    start_urls = [‘https://hr.tencent.com/position.php‘]

    rules = (
        Rule(LinkExtractor(allow=r‘position_detail.php\?id=\d+&keywords=&tid=0&lid=0‘), callback=‘parse_item‘),
        Rule(LinkExtractor(allow=r‘position.php\?&start=\d+#a‘), follow=True),
    )

    def parse_item(self, response):
        item = {}
        # i[‘domain_id‘] = response.xpath(‘//input[@id="sid"]/@value‘).extract()
        # i[‘name‘] = response.xpath(‘//div[@id="name"]‘).extract()
        # i[‘description‘] = response.xpath(‘//div[@id="description"]‘).extract()
        item["title"] = response.xpath("//td[@id=‘sharetitle‘]/text()").extract_first()  # 提取标题
        item[‘duty‘] = response.xpath("//div[text()=‘工作职责:‘]/following-sibling::ul[1]/li/text()").extract()
        item[‘require‘] = response.xpath("//div[text()=‘工作要求:‘]/following-sibling::ul[1]/li/text()").extract()
        print(item)

  2.4 crawlspider使用的注意点

scrapy 详解

  2.5 crawlspider的补充知识点(了解)

scrapy 详解

14、下载中间件和模拟登陆

  14.1  scrapy中下载中间件的使用

  1. 使用方法:

    编写一个Downloader Middlewares和我们编写一个pipeline一样,定义一个类,然后在setting中开启

  2. Downloader Middlewares默认的方法:

    • process_request(self, request, spider):

当每个request通过下载中间件时,该方法被调用。
返回None值:继续请求
返回Response对象:不在请求,把response返回给引擎
返回Request对象:把request对象交给调度器进行后续的请求
    • process_response(self, request, response, spider):
- 当下载器完成http请求,传递响应给引擎的时候调用
 - 返回Resposne:交给process_response来处理
 - 返回Request对象:交给调取器继续请求

3. 定义实现随机User-Agent的下载中间件

class UserAgentMiddleware(object):
     def process_request(self,request,spider):
         agent = random.choice(agents)
         request.headers[‘User-Agent‘] = agent

4. 定义实现随机使用代理的下载中间件

class ProxyMiddleware(object):
     def process_request(self,request,spider):
         proxy = random.choice(proxies)
         request.meta[‘proxy‘] = proxy

  14.2. 使用scrapy进行模拟登陆

  2.1 回顾之前的模拟登陆的方法

  1. requests是如何模拟登陆的?

    1. 直接携带cookies请求页面
    2. 找接口发送post请求存储cookie
  2. selenium是如何模拟登陆的?

    1. 找到对应的input标签,输入文字点击登录

scrapy来说,有两个方法模拟登陆:

  1、直接携带cookie
  2、找到发送post请求的url地址,带上信息,发送请求

  2.2 scrapy携带cookie进行模拟登陆

  1. 携带cookie进行模拟登陆应用场景:

    1. cookie过期时间很长,常见于一些不规范的网站
    2. 能在cookie过期之前把搜有的数据拿到
    3. 配合其他程序使用,比如其使用selenium把登陆之后的cookie获取到保存到本地,scrapy发送请求之前先读取本地cookie
  2. 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

3. 实现携带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)

     4. 在settings中开启cookie_debug

在settings.py中通过设置COOKIES_DEBUG=TRUE 能够在终端看到cookie的传递传递过程

scrapy 详解

  2.3 scrapy发送post请求

scrapy中发送post请求的方法 通过scrapy.FormRequest能够发送post请求,同时需要添加fromdata参数作为请求体,以及callback 

使用scrapy模拟登陆github

思路分析

  1. 找到post的url地址

    点击登录按钮进行抓包,然后定位url地址为https://github.com/session

  2. 找到请求体的规律

    分析post请求的请求体,其中包含的参数均在前一次的响应中

  3. 验证是否登录成功

    通过请求个人主页,观察是否包含用户名

#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)
 

相关推荐