scrapy官网概要(二)之spider的主要部分

作者:清一

类别:数据处理及人工智能   

发布时间:2019/11/18 16:36:54   更新时间:2019/11/18 16:43:42


请求(request)和响应(response)对象

RequestResponse对象,用于爬网网站。

Request对象是在Spider中生成的,并在整个系统中传递,直到它们到达Downloader,该Downloader执行请求并返回一个Response对象,该对象返回到发出请求的Spider中。

无论RequestResponse类有子类,其功能添加基类中不是必需的。

选择器(Selectors)

抓取网页时,最需要执行的任务是从HTML源中提取数据。Scrapy带有自己的数据提取机制。之所以称为选择器,是因为它们“选择”了XPathCSS表达式指定的HTML文档的某些部分。

XPath是一种用于选择XML文档中的节点的语言,该语言也可以与HTML一起使用。

CSS是一种将样式应用于HTML文档的语言。它定义了将这些样式与特定HTML元素相关联的选择器。

一般使用方法

>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'

 

.xpath()和.css()方法返回一个 SelectorList实例,这是新的选择列表。

.get()总是返回单个结果;如果有多个匹配项,则返回第一个匹配项的内容

.getall()返回包含所有结果的列表。

如果未找到任何元素,则返回None。或者用.get(default='not-found')更替。

连续选择

比如:

>>> response.css('img').xpath('@src').getall()

比如:

img.attrib['src'] for img in response.css('img')

attrib['src']是xpath('@src')的快捷方式

CSS选择器的扩展

根据W3C标准,CSS选择器不支持选择文本节点或属性值。但是在Web抓取上下文中选择这些元素非常重要,以至于Scrapy(parsel)实现了两个非标准的伪元素

选择文本节点,使用 ::text

要选择属性值,请使用::attr(name)其中name是您想要其值的属性的名称

正则表达式

response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')

相对xpath

首先,您将获得所有<div>元素:

divs = response.xpath('//div')

然后再用相对xpath注册底下的:

for p in divs.xpath('.//p'):

特殊使用方法(html_response、xml_response)

>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').get()
'good'

从响应构造- HtmlResponse是 TextResponse子类之一

sel = Selector(xml_response)
sel.xpath("//product")

项items

定义

为了定义通用的输出数据格式,Scrapy提供了Item类。项目对象是用于收集收集到的数据的简单容器。它们提供了一个类似于字典的API,使用方便的语法声明它们的可用字段。

不同的Scrapy组件使用Item提供的额外信息:出口商查看已声明的字段以确定要导出的列,可以使用Item字段元数据自定义序列化,trackref跟踪项实例来帮助查找内存泄漏(请参阅使用trackref调试内存泄漏),等等。

申明item类

import scrapy

 
class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    tags = scrapy.Field()
last_updated = scrapy.Field(serializer=str)

 

熟悉Django的人会注意到Scrapy Items的声明与Django Models类似,不同之处在于Scrapy Items更简单,因为没有不同字段类型的概念。

创建实例

product = Product(name='Desktop PC', price=1000)

 

项item加载器

定义

项目加载器为填充item提供了一种方便的机制。尽管可以使用自己的类字典API来填充项,但项加载器提供了一个更方便的API。

方法是在分配数据之前自动执行一些常见任务,比如解析提取的原始数据。

总结一句话:item提供了抓取数据的容器,而item加载器则提供了填充该容器的机制。

from scrapy.loader import ItemLoader
from myproject.items import Product

 
def parse(self, response):
    l = ItemLoader(item=Product(), response=response)
    l.add_xpath('name', '//div[@class="product_name"]')
    l.add_xpath('name', '//div[@class="product_title"]')
    l.add_xpath('price', '//p[@id="price"]')
    l.add_css('stock', 'p#stock]')
    l.add_value('last_updated', 'today') # you can also use literal values
    return l.load_item()

输入输出处理器

项目加载器为每个(项目)字段包含一个输入处理器和一个输出处理器。一旦接收到提取的数据(通过add_xpath()、add_css()或add_value()方法),输入处理器就会对其进行处理,并将输入处理器的结果收集到ItemLoader中。在收集所有数据之后,将调用ItemLoader.load_item()方法来填充和获取填充的Item对象。

l = ItemLoader(Product(), some_selector)
l.add_xpath('name', xpath1) # (1)
l.add_xpath('name', xpath2) # (2)
l.add_css('name', css) # (3)
l.add_value('name', 'test') # (4)
return l.load_item() # (5)

申明项目加载器

from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join

 
class ProductLoader(ItemLoader):

 
    default_output_processor = TakeFirst()

 
    name_in = MapCompose(unicode.title)
    name_out = Join()

 
    price_in = MapCompose(unicode.strip)

 

声明输入和输出处理器是例子中的_in和_out

申明输入输出处理器

import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags

 
def filter_price(value):
    if value.isdigit():
        return value

 
class Product(scrapy.Item):
    name = scrapy.Field(
        input_processor=MapCompose(remove_tags),
        output_processor=Join(),
    )
    price = scrapy.Field(
        input_processor=MapCompose(remove_tags, filter_price),
        output_processor=TakeFirst(),
    )

 

输入和输出处理器的优先顺序如下:

1、Item Loader字段特定的属性:field_in和field_out(最高优先级)

2、字段元数据(input_processor和output_processor键)

3、Item Loader的默认设置:ItemLoader.default_input_processor()ItemLoader.default_output_processor()(最低优先级)

内置处理器

即使您可以使用任何可调用函数作为输入和输出处理器,Scrapy仍提供了一些常用的处理器,如下所述。

Identity:原文返回。

TakeFirst:返回第一个非空值。

Join:用连接符连接返回。

Compose:用函数处理返回。

MapCompose:用函数map处理返回。

SelectJmes:使用提供给构造函数的json路径查询值并返回输出。需要jmespath(https://github.com/jmespath/jmespath.py)才能运行。

项item管道

spider抓取了一个项目后,将其发送到项目管道,该管道通过依次执行的几个组件对其进行处理。

每个项目管道组件(有时也称为“项目管道”)都是一个实现简单方法的Python类。他们接收到一个项并对其执行操作,还决定该项是否应继续通过管道或被删除并不再处理。

项目管道的典型用途是:

清理HTML数据

验证抓取的数据(检查项目是否包含某些字段)

检查重复项(并将其删除)

将抓取的item存储在数据库中

基本方法

process_item返回带有数据的字典,返回一个Item (或任何后代类)对象。还有存储操作等。

open_spider 打开spider时的操作。

close_spider 关闭spider时的操作。

例子

import json

 
class JsonWriterPipeline(object):

 
    def open_spider(self, spider):
        self.file = open('items.jl', '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

 

激活

要激活Item Pipeline组件,必须将其类添加到 ITEM_PIPELINES设置中,如以下示例所示:

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

下载和处理文件、图像的管道

Scrapy提供了可重用的项目管道,用于下载附加到特定项目的文件(例如,当您抓取产品并且还想在本地下载其图像时)。这些管道共享一些功能和结构(我们将它们称为媒体管道),但是通常您将使用“文件管道”或“图像管道”。

这两个管道都实现了以下功能:

* 避免重新下载最近下载的媒体

* 指定存储媒体的位置(文件系统目录,Amazon S3存储桶,Google Cloud Storage存储桶)

图片管道具有一些额外的功能来处理图片:

* 将所有下载的图像转换为通用格式(JPG)和模式(RGB)

* 缩图产生

* 检查图像的宽度/高度,以确保它们满足最小限制

管道还保留了那些当前计划下载的媒体URL的内部队列,并将到达的包含相同媒体的响应连接到该队列。当多个项目共享同一媒体时,这避免了多次下载同一媒体。

导出数据(feed exports)

实施抓取工具时最常需要的功能之一就是能够正确存储抓取的数据,并且这通常意味着生成带有抓取数据的“导出文件”(通常称为“导出提要”),以供其他系统使用。

导出格式

要序列化抓取的数据,Feed导出使用Item导出器。开箱即用地支持以下格式:

* JSON格式

* JSON LINES

* CSV

* XML格式

* Pickle

* Marshal

但是您也可以通过FEED_EXPORTERS设置扩展支持的格式 。

导出后端类型

存储后端是:

* 本地文件系统

* FTP

* S3(需要 botocore boto

* 标准输出


本文属于原创文章,未经许可,任何媒体、公司或个人不得刊发或转载。

本文网址:https://www.pyfield.com/blog/?id=42