发布时间:2019/11/18 16:36:54   更新时间:2020/07/31 19:48:38
Request对象是在Spider中生成的,并在整个系统中传递,直到它们到达Downloader,该Downloader执行请求并返回一个Response对象,该对象返回到发出请求的Spider中。
无论Request和Response类有子类,其功能添加基类中不是必需的。
抓取网页时,最需要执行的任务是从HTML源中提取数据。Scrapy带有自己的数据提取机制。之所以称为选择器,是因为它们“选择”了XPath或CSS表达式指定的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')的快捷方式
根据W3C标准,CSS选择器不支持选择文本节点或属性值。但是在Web抓取上下文中选择这些元素非常重要,以至于Scrapy(parsel)实现了两个非标准的伪元素:
选择文本节点,使用 ::text
要选择属性值,请使用::attr(name)其中name是您想要其值的属性的名称
response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
首先,您将获得所有<div>元素:
divs = response.xpath('//div')
然后再用相对xpath注册底下的:
for p in divs.xpath('.//p'):
>>> 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")
为了定义通用的输出数据格式,Scrapy提供了Item类。项目对象是用于收集收集到的数据的简单容器。它们提供了一个类似于字典的API,使用方便的语法声明它们的可用字段。
不同的Scrapy组件使用Item提供的额外信息:出口商查看已声明的字段以确定要导出的列,可以使用Item字段元数据自定义序列化,trackref跟踪项实例来帮助查找内存泄漏(请参阅使用trackref调试内存泄漏),等等。
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提供了一种方便的机制。尽管可以使用自己的类字典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)才能运行。
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导出使用Item导出器。开箱即用地支持以下格式:
* JSON格式
* JSON LINES
* CSV
* XML格式
* Pickle
* Marshal
但是您也可以通过FEED_EXPORTERS设置扩展支持的格式 。
存储后端是:
* 本地文件系统
* FTP
* 标准输出