当前位置: 移动技术网 > IT编程>脚本编程>Python > 爬虫(十六):Scrapy框架(三) Spider Middleware、Item Pipeline、对接Selenium

爬虫(十六):Scrapy框架(三) Spider Middleware、Item Pipeline、对接Selenium

2020年01月05日  | 移动技术网IT编程  | 我要评论

人民日报社长,官场中文网,拉登团购

1. spider middleware

spider middleware是介入到scrapy的spider处理机制的钩子框架。

当downloader生成response之后,response会被发送给spider,在发送给spider之前,response会首先经过spider middleware处理,当spider处理生成item和request之后,item request还会经过spider middleware的处理。

spider middleware有三个作用:

  • 我们可以在downloader生成的response发送给spider之前,也就是在response发送给spider之前对response进行处理。
  • 我们可以在spider生成的request发送给scheduler之前,也就是在request发送给scheduler之前对request进行处理。
  • 我们可以在spider生成的item发送给item pipeline之前,也就是在item发送给item pipeline之前对item进行处理。

1.1 使用说明

需要说明的是,scrapy其实已经提供了许多spider middleware,它们被spider_middlewares_base这个变盘所定义。

spider_middlewares_base变量的内容如下:

{
    'scrapy.spidermiddlewares.httperror.httperrormiddleware':50,
    'scrapy spidermiddlewares offsite of site iddleware':500,
    'scrapy.spidermiddlewares.referer.referermiddleware':700,
    'scrapy.spidermiddlewares.urllength.urllengthmiddleware':800,
    'scrapy.spidermiddlewares.depth.depthmiddleware':900,
}

和downloader middleware一样,spider middleware首先加入到spider_middlewares设置中,该设置会和scrapy中spider_middlewares_base定义的spider middleware合并。然后根据键值的数字优先级排序,得到一个有序列表。第一middleware是最靠近引擎的,最后一个middleware是最靠近spide的。

1.2 核心方法

scrapy内置的spider middleware为scrapy提供了基础的功能。如果我们想要扩展其功能,只需要实现某几个方法即可。

每个spider middleware都定义了以下一个或多个方法的类,核心方法有如下4个。

  • process_spider_input(response,spider)
  • process_spider_output(response,result,spider)
  • process_spider_exception(response,exception,spider)
  • proce ss_start_requests(start_requests,spider)

只需要实现其中一个方法就可以定义一个spider middleware。

(1) process_spider_input(response,spider)

当response被spider middleware处理时,process_spider_input()方法被调用。

process_spider_input()方法的参数有如下两个:

response,是response对象,即被处理的response。

spider,是spider对象,即该response对应的spider。

process_spider_input()应该返回none或者抛出一个异常。

如果它返回none,scrapy将会继续处理该response,调用所有其他的spider middleware,直到spider处理该response。

如果它抛出一个异常,scrapy将不会调用任何其他spider middleware的process_spider_input()方法,而调用request的errback()方法。errback的输出将会被重新输入到中间件中,使用process_spider_output()方法来处理,当其抛出异常时则调用process_spider_exception()来处理。

(2) process _spider_output(response,result,spider)

当spider处理response返回结果时,process_spider_output()方法被调用。process_spider_output()方法的参数有如下三个:

response,是response对象,即生成该输出的response。

result,包含request或item对象的可迭代对象,即spider返回的结果。

spider,是spider对象,即其结果对应的spider。

process_spider_output()必须返回包含request或item对象的可迭代对象。

(3) process_spider_exception(response,exception,spider)

当spider或spider middleware的process_spider_input()方法抛出异常时,process_spider_exception()方法被调用。

process_spider_exception()方法的参数有如下三个:

response,是response对象,即异常被抛出时被处理的response。

exception,是exception对象,即被抛出的异常。

spider,是spider对象,即抛出该异常的spider。

process_spider_exception()必须要么返回none,要么返回一个包含response或item对象的可迭代对象。

如果它返问none,scrapy将继续处理该异常,调用其他spider middleware中的process_spider_exception()方法,直到所有spider middleware都被调用。

如果它返回一个可迭代对象,spider middleware的process_spider_output()方法被调用,其他的process_spider_exception()不会被调用。

(4) process_start_requests (start_requests,spider)

process_start_requests()方法以spider启动的request为参数被调用,执行的过程类似于process_spider_output(),只不过它没有相关联的response并且必须返回request。

proces s_start_requests()方法的参数有如下两个:

start_requests,是包含request的可迭代对象,即start requests。

spider,是spider对象,即start requests所属的spider。

process_start_requests()必须返回另一个包含request对象的可迭代对象。

2. item pipeline

item pipeline是项目管道。

item pipeline的调用发生在spider产生item之后。当spider解析完response之后,item就会传递到item pipeline,被定义的item pipeline组件会依次调用,完成一连串的处理过程,比如数据清洗、存储等。

item pipeline主要功能有四点:

清理html数据。

验证爬取数据,检查爬取字段。

查重并丢弃重复内容。

将爬取结果保存到数据库。

我们可以自定义item pipeline,只需要实现指定的方法,其中必须要实现的一个方法是: process_item(item,spider)。

另外还有如下几个比较实用的方法:

open_spider(spider)

close_spider(spider)

from_crawler(cls,crawler)

2.1 常用方法

(1) process_item(item,spider)

process_item()是必须要实现的方法,被定义的item pipeline会默认调用这个方法对item进行处理。比如,我们可以进行数据处理或者将数据写入到数据库等操作。它必须返回item类型的值或者抛出一个dropitem异常。

process_itern()方法的参数有两个:

item,是item对象,即被处理的item。

spider,是spider对象,即生成该item spider。

process_item()方法的返回类型归纳如下:

如果它返回的是item对象,那么此item会被低优先级的item pipeline的process_item()方法处理,直到所有的方法被调用完毕。

如果它抛出的是dropitem异常,那么此item会被丢弃,不再进行处理。

(2) open_spider(self,spider)

open_spider()方法是在spider开启的时候被自动调用的。在这里我们可以做一些初始化操作,如开启数据库连接等。其中,参数spider就是被开启的spider对象。

(3) close_spider(spider)

close_spider()方法是在spider关闭的时候自动调用的。在这里我们可以做一些收尾工作,如 关闭数据库连接等。其中,参数spider就是被关闭的spider对象。

(4) from_crawler(cls,crawler)

from_crawler()方法是一个类方法,用@classmethod标识,是一种依赖注入的方式。它的参数是crawler,通过crawler对象,我们可以拿到scrapy的所有核心组件,如全局配置的每个信息,然后创建pipeline实例。参数cls就是class,最后返回一个class实例。

2.2 管道示例

(1) 价格验证和删除没有价格的商品

调整price那些不包含增值税(price_excludes_vat属性)的项目的属性,并删除那些不包含价格的项目:

from scrapy.exceptions import dropitem

class pricepipeline(object):

    vat_factor = 1.15

    def process_item(self, item, spider):
        if item.get('price'):
            if item.get('price_excludes_vat'):
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise dropitem("missing price in %s" % item)

(2) 将项目写入json文件

将所有已删除的项目存储到一个items.json文件中,每一行包含一个以json格式序列化的项目:

import json

class jsonwriterpipeline(object):

    def open_spider(self, spider):
        self.file = open('items.json', 'w')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

(3) 将项目写入mongodb

在这个例子中,我们将使用pymongo将项目写入mongodb。mongodb地址和数据库名称在scrapy设置中指定;mongodb集合以item类命名。

import pymongo

class mongopipeline(object):

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('mongo_uri'),
            mongo_db=crawler.settings.get('mongo_database', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.mongoclient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert_one(dict(item))
        return item

(4) 截取项目的截图

从process_item()方法返回deferred。它使用splash渲染项目url的屏幕截图。pipeline向本地运行的splash示例发出请求。下载请求并延迟回调激活后,它会将项目保存到文件并将文件名添加到项目中。

import scrapy
import hashlib
from urllib.parse import quote


class screenshotpipeline(object):
    """pipeline that uses splash to render screenshot of
    every scrapy item."""

    splash_url = "http://localhost:8050/render.png?url={}"

    def process_item(self, item, spider):
        encoded_item_url = quote(item["url"])
        screenshot_url = self.splash_url.format(encoded_item_url)
        request = scrapy.request(screenshot_url)
        dfd = spider.crawler.engine.download(request, spider)
        dfd.addboth(self.return_item, item)
        return dfd

    def return_item(self, response, item):
        if response.status != 200:
            # error happened, return item.
            return item

        # save screenshot to file, filename will be hash of url.
        url = item["url"]
        url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
        filename = "{}.png".format(url_hash)
        with open(filename, "wb") as f:
            f.write(response.body)

        # store filename in item.
        item["screenshot_filename"] = filename
        return item

(5) 重复过滤

一个过滤器,用于查找重复项目,并删除已处理的项目。假设我们的项目具有唯一id,但我们的spider会返回具有相同id的多个项目:

from scrapy.exceptions import dropitem

class duplicatespipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise dropitem("duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return ite

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网