提交 027097f0 编写于 作者: W wizardforcel

2019-07-06 10:57:01

上级 90c8dc48
+ [简介](docs/index.md)
+ [第一步](docs/2.md)
+ [Scrapy at a glance](docs/3.md)
+ [安装指南](docs/4.md)
+ [Scrapy 教程](docs/5.md)
+ [版本控制和API稳定性](docs/50.md)
+ [实例](docs/6.md)
+ [基本概念](docs/7.md)
+ [命令行工具](docs/8.md)
+ [Spider](docs/9.md)
+ [选择器](docs/10.md)
+ [项目](docs/11.md)
+ [项目加载器](docs/12.md)
+ [Scrapy shell](docs/13.md)
+ [项目管道](docs/14.md)
+ [Feed 导出](docs/15.md)
+ [请求和响应](docs/16.md)
+ [链接提取器](docs/17.md)
+ [设置](docs/18.md)
+ [例外情况](docs/19.md)
+ [内置服务](docs/20.md)
+ [Logging](docs/21.md)
+ [统计数据集合](docs/22.md)
+ [发送电子邮件](docs/23.md)
+ [远程登录控制台](docs/24.md)
+ [Web服务](docs/25.md)
+ [解决具体问题](docs/26.md)
+ [常见问题](docs/27.md)
+ [调试spiders](docs/28.md)
+ [Spider 合约](docs/29.md)
+ [常用做法](docs/30.md)
+ [通用爬虫](docs/31.md)
+ [使用浏览器的开发人员工具进行抓取](docs/32.md)
+ [调试内存泄漏](docs/33.md)
+ [下载和处理文件和图像](docs/34.md)
+ [部署 Spider](docs/35.md)
+ [AutoThrottle 扩展](docs/36.md)
+ [Benchmarking](docs/37.md)
+ [作业:暂停和恢复爬行](docs/38.md)
+ [延伸 Scrapy](docs/39.md)
+ [体系结构概述](docs/40.md)
+ [下载器中间件](docs/41.md)
+ [Spider 中间件](docs/42.md)
+ [扩展](docs/43.md)
+ [核心API](docs/44.md)
+ [信号](docs/45.md)
+ [条目导出器](docs/46.md)
+ [其余所有](docs/47.md)
+ [发行说明](docs/48.md)
+ [为 Scrapy 贡献](docs/49.md)
此差异已折叠。
# 项目
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
抓取的主要目标是从非结构化源(通常是网页)中提取结构化数据。scrappyspider可以以python dicts的形式返回提取的数据。虽然方便和熟悉,但python dicts缺乏结构:很容易在字段名中输入错误或返回不一致的数据,特别是在有许多spider的大型项目中。
要定义通用输出数据格式,scrapy提供 [`Item`](#scrapy.item.Item "scrapy.item.Item") 类。 [`Item`](#scrapy.item.Item "scrapy.item.Item") 对象是用于收集抓取数据的简单容器。他们提供了一个 [dictionary-like](https://docs.python.org/2/library/stdtypes.html#dict) 具有声明可用字段的方便语法的API。
各种零碎组件使用项目提供的额外信息:导出器查看声明的字段以确定要导出的列,可以使用项目字段元数据自定义序列化。 `trackref` 跟踪项实例以帮助查找内存泄漏(请参阅 [使用调试内存泄漏 trackref](leaks.html#topics-leaks-trackrefs) 等)。
## 声明项目
使用简单的类定义语法和 [`Field`](#scrapy.item.Field "scrapy.item.Field") 物体。下面是一个例子:
```py
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](https://www.djangoproject.com/) 会注意到 Scrapy 物品的声明类似于 [Django Models](https://docs.djangoproject.com/en/dev/topics/db/models/) 但是,由于不存在不同字段类型的概念,因此片段项要简单得多。
## 项目字段
[`Field`](#scrapy.item.Field "scrapy.item.Field") 对象用于为每个字段指定元数据。例如,用于 `last_updated` 上面示例中所示的字段。
可以为每个字段指定任何类型的元数据。对接受的值没有限制 [`Field`](#scrapy.item.Field "scrapy.item.Field") 物体。出于同样的原因,没有所有可用元数据键的引用列表。中定义的每个键 [`Field`](#scrapy.item.Field "scrapy.item.Field") 对象可以由不同的组件使用,只有那些组件知道它。您还可以定义和使用任何其他 [`Field`](#scrapy.item.Field "scrapy.item.Field") 为了你自己的需要,也要输入你的项目。的主要目标 [`Field`](#scrapy.item.Field "scrapy.item.Field") 对象是提供一种在一个地方定义所有字段元数据的方法。通常,行为依赖于每个字段的组件使用特定的字段键来配置该行为。您必须参考它们的文档来查看每个组件使用的元数据键。
重要的是要注意 [`Field`](#scrapy.item.Field "scrapy.item.Field") 用于声明该项的对象不会保留分配为类属性的状态。相反,可以通过 [`Item.fields`](#scrapy.item.Item.fields "scrapy.item.Item.fields") 属性。
## 处理项目
下面是一些使用项执行的常见任务的示例,使用 `Product` 项目 [declared above](#topics-items-declaring) . 您会注意到API与 [dict API](https://docs.python.org/2/library/stdtypes.html#dict) .
### 创建项目
```py
>>> product = Product(name='Desktop PC', price=1000)
>>> print(product)
Product(name='Desktop PC', price=1000)
```
### 获取字段值
```py
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC
>>> product['price']
1000
>>> product['last_updated']
Traceback (most recent call last):
...
KeyError: 'last_updated'
>>> product.get('last_updated', 'not set')
not set
>>> product['lala'] # getting unknown field
Traceback (most recent call last):
...
KeyError: 'lala'
>>> product.get('lala', 'unknown field')
'unknown field'
>>> 'name' in product # is name field populated?
True
>>> 'last_updated' in product # is last_updated populated?
False
>>> 'last_updated' in product.fields # is last_updated a declared field?
True
>>> 'lala' in product.fields # is lala a declared field?
False
```
### 设置字段值
```py
>>> product['last_updated'] = 'today'
>>> product['last_updated']
today
>>> product['lala'] = 'test' # setting unknown field
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
```
### 访问所有填充的值
要访问所有填充的值,只需使用 [dict API](https://docs.python.org/2/library/stdtypes.html#dict) ::
```py
>>> product.keys()
['price', 'name']
>>> product.items()
[('price', 1000), ('name', 'Desktop PC')]
```
### 复制项目
要复制项目,必须首先决定是要浅副本还是深副本。
如果您的物品包含 [mutable](https://docs.python.org/glossary.html#term-mutable) 值如列表或字典,一个浅拷贝将在所有不同的拷贝中保持对相同可变值的引用。
例如,如果您有一个带有标记列表的项目,并且您创建了该项目的浅副本,那么原始项目和副本都具有相同的标记列表。向其中一个项目的列表中添加标记也会将标记添加到另一个项目中。
如果这不是所需的行为,请使用深度复制。
[documentation of the copy module](https://docs.python.org/library/copy.html) 更多信息。
要创建项目的浅副本,可以调用 `copy()` 在现有项上 (`product2 = product.copy()` )或从现有项实例化项类 (`product2 = Product(product)`
要创建深度复制,请调用 `deepcopy()` 相反 (`product2 = product.deepcopy()`
### 其他常见任务
从项目创建听写:
```py
>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}
```
从dicts创建项目:
```py
>>> Product({'name': 'Laptop PC', 'price': 1500})
Product(price=1500, name='Laptop PC')
>>> Product({'name': 'Laptop PC', 'lala': 1500}) # warning: unknown field in dict
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
```
## 扩展项目
您可以通过声明原始项的子类来扩展项(添加更多字段或更改某些字段的元数据)。
例如::
```py
class DiscountedProduct(Product):
discount_percent = scrapy.Field(serializer=str)
discount_expiration_date = scrapy.Field()
```
您还可以通过使用前面的字段元数据并附加更多值或更改现有值来扩展字段元数据,如:
```py
class SpecificProduct(Product):
name = scrapy.Field(Product.fields['name'], serializer=my_serializer)
```
添加(或替换)了 `serializer` 的元数据键 `name` 字段,保留所有以前存在的元数据值。
## 项目对象
```py
class scrapy.item.Item([arg])
```
返回从给定参数中可选初始化的新项。
项目复制标准 [dict API](https://docs.python.org/2/library/stdtypes.html#dict) 包括其构造函数。项提供的唯一附加属性是:
```py
fields
```
包含 _all declared fields_ 对于这个项目,不仅仅是那些填充的。键是字段名,值是 [`Field`](#scrapy.item.Field "scrapy.item.Field") 中使用的对象 [Item declaration](#topics-items-declaring) .
## 字段对象
```py
class scrapy.item.Field([arg])
```
这个 [`Field`](#scrapy.item.Field "scrapy.item.Field") 类只是内置的别名 [dict](https://docs.python.org/2/library/stdtypes.html#dict) class and doesn't provide any extra functionality or attributes. In other words, [`Field`](#scrapy.item.Field "scrapy.item.Field") 对象是普通的旧python dict。单独的类用于支持 [item declaration syntax](#topics-items-declaring) 基于类属性。
\ No newline at end of file
此差异已折叠。
# Scrapy shell
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
scrappyshell是一个交互式shell,您可以在其中快速调试 scrape 代码,而不必运行spider。它本来是用来测试数据提取代码的,但实际上您可以使用它来测试任何类型的代码,因为它也是一个常规的PythonShell。
shell用于测试xpath或css表达式,并查看它们是如何工作的,以及它们从您试图抓取的网页中提取的数据。它允许您在编写spider时交互地测试表达式,而不必运行spider来测试每个更改。
一旦你熟悉了 Scrapy Shell,你就会发现它是开发和调试 Spider 的宝贵工具。
## 配置shell
如果你有 [IPython](https://ipython.org/) 安装后,scrapy shell将使用它(而不是标准的python控制台)。这个 [IPython](https://ipython.org/) 控制台功能更强大,提供智能自动完成和彩色输出等功能。
我们强烈建议您安装 [IPython](https://ipython.org/) ,特别是在使用Unix系统时(其中 [IPython](https://ipython.org/) 擅长)。见 [IPython installation guide](https://ipython.org/install.html) 更多信息。
Scrapy还支持 [bpython](https://www.bpython-interpreter.org/) ,并将尝试在 [IPython](https://ipython.org/) 不可用。
通过Scrapy的设置,您可以将其配置为使用 `ipython``bpython` 或标准 `python` Shell,无论安装了什么。这是通过设置 `SCRAPY_PYTHON_SHELL` 环境变量;或通过在 [scrapy.cfg](commands.html#topics-config-settings) ::
```py
[settings]
shell = bpython
```
## 启动Shell
要启动 Scrapy shell,可以使用 [`shell`](commands.html#std:command-shell) 命令如下:
```py
scrapy shell <url>
```
何处 `&lt;url&gt;` 是要抓取的URL。
[`shell`](commands.html#std:command-shell) 也适用于本地文件。如果你想玩一个网页的本地副本,这很方便。 [`shell`](commands.html#std:command-shell) 了解本地文件的以下语法::
```py
# UNIX-style
scrapy shell ./path/to/file.html
scrapy shell ../other/path/to/file.html
scrapy shell /absolute/path/to/file.html
# File URI
scrapy shell file:///absolute/path/to/file.html
```
注解
使用相对文件路径时,请显式并用 `./` (或) `../` 相关时)。 `scrapy shell index.html` 不会像人们预期的那样工作(这是设计上的,而不是错误)。
因为 [`shell`](commands.html#std:command-shell) 喜欢HTTP URL而不是文件URI,以及 `index.html` 在句法上类似于 `example.com`[`shell`](commands.html#std:command-shell) 会治疗 `index.html` 作为域名并触发DNS查找错误::
```py
$ scrapy shell index.html
[ ... scrapy shell starts ... ]
[ ... traceback ... ]
twisted.internet.error.DNSLookupError: DNS lookup failed:
address 'index.html' not found: [Errno -5] No address associated with hostname.
```
[`shell`](commands.html#std:command-shell) 如果文件调用了 `index.html` 存在于当前目录中。同样,要明确。
## 使用Shell
scrappyshell只是一个普通的python控制台(或者 [IPython](https://ipython.org/) 控制台,如果你有它的话),它提供一些额外的快捷功能,以方便。
### 可用快捷方式
> * `shelp()` -打印有关可用对象和快捷方式列表的帮助
> * `fetch(url[, redirect=True])` - fetch a new response from the given URL and update all related objects accordingly. You can optionaly ask for HTTP 3xx redirections to not be followed by passing `redirect=False`
> * `fetch(request)` -从给定的请求中获取新的响应,并相应地更新所有相关对象。
> * `view(response)` -在本地Web浏览器中打开给定的响应以进行检查。这将增加一个 [&lt;base&gt; tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) 到响应主体,以便外部链接(如图像和样式表)正确显示。但是请注意,这将在您的计算机中创建一个临时文件,该文件不会自动删除。
### 可用的 Scrapy 对象
Scrapy Shell自动从下载的页面创建一些方便的对象,例如 [`Response`](request-response.html#scrapy.http.Response "scrapy.http.Response") 对象与 [`Selector`](selectors.html#scrapy.selector.Selector "scrapy.selector.Selector") 对象(用于HTML和XML内容)。
这些对象是:
> * `crawler` -电流 [`Crawler`](api.html#scrapy.crawler.Crawler "scrapy.crawler.Crawler") 对象。
> * `spider` -已知用于处理URL的 Spider ,或 [`Spider`](spiders.html#scrapy.spiders.Spider "scrapy.spiders.Spider") 如果没有为当前URL找到spider,则为。
> * `request` -A [`Request`](request-response.html#scrapy.http.Request "scrapy.http.Request") 上次提取的页的对象。您可以使用修改此请求 [`replace()`](request-response.html#scrapy.http.Request.replace "scrapy.http.Request.replace") 或者使用 `fetch` 捷径。
> * `response` -A [`Response`](request-response.html#scrapy.http.Response "scrapy.http.Response") 包含上次提取的页的对象
> * `settings` - the current [Scrapy settings](settings.html#topics-settings)
## Shell会话示例
下面是一个典型的shell会话的例子,我们从抓取https://scrappy.org页面开始,然后继续抓取https://reddit.com页面。最后,我们修改(reddit)请求方法来发布和重新获取它,得到一个错误。我们通过在Windows中键入ctrl-d(在UNIX系统中)或ctrl-z来结束会话。
请记住,在这里提取的数据在您尝试时可能不相同,因为这些页面不是静态的,在您测试时可能已经更改了。这个例子的唯一目的是让您熟悉下脚料Shell的工作原理。
首先,我们发射炮弹:
```py
scrapy shell 'https://scrapy.org' --nolog
```
然后,shell获取URL(使用scrapy下载器)并打印可用对象和有用快捷方式的列表(您会注意到这些行都以 `[s]` 前缀):
```py
[s] Available Scrapy objects:
[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s] crawler <scrapy.crawler.Crawler object at 0x7f07395dd690>
[s] item {}
[s] request <GET https://scrapy.org>
[s] response <200 https://scrapy.org/>
[s] settings <scrapy.settings.Settings object at 0x7f07395dd710>
[s] spider <DefaultSpider 'default' at 0x7f0735891690>
[s] Useful shortcuts:
[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s] fetch(req) Fetch a scrapy.Request and update local objects
[s] shelp() Shell help (print this help)
[s] view(response) View response in a browser
>>>
```
之后,我们可以开始玩对象:
```py
>>> response.xpath('//title/text()').get()
'Scrapy | A Fast and Powerful Scraping and Web Crawling Framework'
>>> fetch("https://reddit.com")
>>> response.xpath('//title/text()').get()
'reddit: the front page of the internet'
>>> request = request.replace(method="POST")
>>> fetch(request)
>>> response.status
404
>>> from pprint import pprint
>>> pprint(response.headers)
{'Accept-Ranges': ['bytes'],
'Cache-Control': ['max-age=0, must-revalidate'],
'Content-Type': ['text/html; charset=UTF-8'],
'Date': ['Thu, 08 Dec 2016 16:21:19 GMT'],
'Server': ['snooserv'],
'Set-Cookie': ['loid=KqNLou0V9SKMX4qb4n; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Dec-2018 16:21:19 GMT; secure',
'loidcreated=2016-12-08T16%3A21%3A19.445Z; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Dec-2018 16:21:19 GMT; secure',
'loid=vi0ZVe4NkxNWdlH7r7; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Dec-2018 16:21:19 GMT; secure',
'loidcreated=2016-12-08T16%3A21%3A19.459Z; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Dec-2018 16:21:19 GMT; secure'],
'Vary': ['accept-encoding'],
'Via': ['1.1 varnish'],
'X-Cache': ['MISS'],
'X-Cache-Hits': ['0'],
'X-Content-Type-Options': ['nosniff'],
'X-Frame-Options': ['SAMEORIGIN'],
'X-Moose': ['majestic'],
'X-Served-By': ['cache-cdg8730-CDG'],
'X-Timer': ['S1481214079.394283,VS0,VE159'],
'X-Ua-Compatible': ['IE=edge'],
'X-Xss-Protection': ['1; mode=block']}
>>>
```
## 从spiders调用shell来检查响应
有时,您希望检查在您的 Spider 的某个点上正在处理的响应,如果只是检查您期望的响应是否到达那里的话。
这可以通过使用 `scrapy.shell.inspect_response` 功能。
下面是一个例子,说明如何从您的 Spider 中命名它:
```py
import scrapy
class MySpider(scrapy.Spider):
name = "myspider"
start_urls = [
"http://example.com",
"http://example.org",
"http://example.net",
]
def parse(self, response):
# We want to inspect one specific response.
if ".org" in response.url:
from scrapy.shell import inspect_response
inspect_response(response, self)
# Rest of parsing code.
```
当你运行 Spider 时,你会得到类似的东西:
```py
2014-01-23 17:48:31-0400 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://example.com> (referer: None)
2014-01-23 17:48:31-0400 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://example.org> (referer: None)
[s] Available Scrapy objects:
[s] crawler <scrapy.crawler.Crawler object at 0x1e16b50>
...
>>> response.url
'http://example.org'
```
然后,您可以检查提取代码是否工作:
```py
>>> response.xpath('//h1[@class="fn"]')
[]
```
不,不是这样的。所以您可以在Web浏览器中打开响应,看看它是否是您期望的响应:
```py
>>> view(response)
True
```
最后,单击ctrl-d(或在Windows中单击ctrl-z)退出shell并继续爬网:
```py
>>> ^D
2014-01-23 17:50:03-0400 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://example.net> (referer: None)
...
```
请注意,您不能使用 `fetch` 这里的快捷方式,因为 Scrapy 的引擎被Shell挡住了。然而,当你离开Shell后, Spider 会继续在它停止的地方爬行,如上图所示。
\ No newline at end of file
# 项目管道
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
在一个项目被 Spider 抓取之后,它被发送到项目管道,该管道通过几个按顺序执行的组件来处理它。
每个项管道组件(有时称为“项管道”)都是一个实现简单方法的Python类。它们接收一个项目并对其执行操作,还决定该项目是否应继续通过管道,或者是否应删除并不再处理。
项目管道的典型用途有:
* 清理HTML数据
* 验证抓取的数据(检查项目是否包含某些字段)
* 检查重复项(并删除它们)
* 将刮下的项目存储在数据库中
## 编写自己的项目管道
每个item pipeline组件都是一个python类,必须实现以下方法:
```py
process_item(self, item, spider)
```
对每个项管道组件调用此方法。 [`process_item()`](#process_item "process_item") 必须:返回包含数据的dict,返回 [`Item`](items.html#scrapy.item.Item "scrapy.item.Item") (或任何后代类)对象,返回 [Twisted Deferred](https://twistedmatrix.com/documents/current/core/howto/defer.html) or raise [`DropItem`](exceptions.html#scrapy.exceptions.DropItem "scrapy.exceptions.DropItem") 例外。删除的项不再由其他管道组件处理。
| 参数: |
* **item** ([`Item`](items.html#scrapy.item.Item "scrapy.item.Item") object or a dict) -- 物品被刮掉了
* **spider** ([`Spider`](spiders.html#scrapy.spiders.Spider "scrapy.spiders.Spider") object) -- 刮掉物品的 Spider
|
| --- | --- |
此外,它们还可以实现以下方法:
```py
open_spider(self, spider)
```
当spider打开时调用此方法。
| 参数: | **spider** ([`Spider`](spiders.html#scrapy.spiders.Spider "scrapy.spiders.Spider") object) -- 打开的 Spider |
| --- | --- |
```py
close_spider(self, spider)
```
当spider关闭时调用此方法。
| 参数: | **spider** ([`Spider`](spiders.html#scrapy.spiders.Spider "scrapy.spiders.Spider") object) -- 关闭的 Spider |
| --- | --- |
```py
from_crawler(cls, crawler)
```
如果存在,则调用此ClassMethod从 [`Crawler`](api.html#scrapy.crawler.Crawler "scrapy.crawler.Crawler") . 它必须返回管道的新实例。爬虫对象提供对所有零碎核心组件(如设置和信号)的访问;它是管道访问它们并将其功能连接到零碎的一种方式。
| 参数: | **crawler** ([`Crawler`](api.html#scrapy.crawler.Crawler "scrapy.crawler.Crawler") object) -- 使用此管道的爬虫程序 |
| --- | --- |
## 项目管道示例
### 无价格的价格验证和删除项目
让我们看看下面的假设管道,它调整了 `price` 不包括增值税的项目的属性( `price_excludes_vat` 属性),并删除不包含价格的项目:
```py
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)
```
### 将项目写入JSON文件
下面的管道将所有刮掉的项目(从所有 Spider )存储到一个单独的管道中 `items.jl` 文件,每行包含一个以JSON格式序列化的项:
```py
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
```
注解
jsonWriterPipeline的目的只是介绍如何编写项管道。如果您真的想将所有的抓取项存储到JSON文件中,那么应该使用 [Feed exports](feed-exports.html#topics-feed-exports) .
### 将项目写入MongoDB
在本例中,我们将使用pymongo_uu将项目写入mongodb_u。在Scrapy设置中指定MongoDB地址和数据库名称;MongoDB集合以item类命名。
这个例子的要点是演示如何使用 [`from_crawler()`](#from_crawler "from_crawler") 方法和如何正确清理资源。::
```py
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
```
### 项目截图
这个例子演示了如何返回延迟的 [`process_item()`](#process_item "process_item") 方法。它使用splash_u呈现项目url的屏幕截图。管道向本地运行的splash_uuu实例发出请求。在下载请求并触发延迟回调之后,它将项目保存到文件中,并将文件名添加到项目中。
```py
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
```
### 重复筛选器
查找重复项并删除已处理的项的筛选器。假设我们的项目有一个唯一的ID,但是我们的spider返回具有相同ID的多个项目:
```py
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 item
```
## 激活项目管道组件
若要激活项管道组件,必须将其类添加到 [`ITEM_PIPELINES`](settings.html#std:setting-ITEM_PIPELINES) 设置,如以下示例中所示:
```py
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
```
在此设置中分配给类的整数值决定了它们的运行顺序:项从低值类传递到高值类。习惯上把这些数字定义在0-1000范围内。
\ No newline at end of file
# Feed 导出
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
0.10 新版功能.
在实现scraper时,最经常需要的功能之一是能够正确地存储被抓取的数据,这通常意味着用被抓取的数据(通常称为“导出提要”)生成一个“导出文件”,供其他系统使用。
Scrapy随提要导出一起提供了开箱即用的功能,它允许您使用多个序列化格式和存储后端生成带有已擦除项的提要。
## 序列化格式
为了序列化抓取的数据,提要导出使用 [Item exporters](exporters.html#topics-exporters) . 开箱即用支持这些格式:
> * [JSON](#topics-feed-format-json)
> * [杰森线](#topics-feed-format-jsonlines)
> * [CSV](#topics-feed-format-csv)
> * [XML](#topics-feed-format-xml)
但是您也可以通过 [`FEED_EXPORTERS`](#std:setting-FEED_EXPORTERS) 设置。
### JSON
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `json`
> * 出口商: [`JsonItemExporter`](exporters.html#scrapy.exporters.JsonItemExporter "scrapy.exporters.JsonItemExporter")
> * 见 [this warning](exporters.html#json-with-large-data) 如果您使用的是大型提要的JSON。
### 杰森线
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `jsonlines`
> * 出口商: [`JsonLinesItemExporter`](exporters.html#scrapy.exporters.JsonLinesItemExporter "scrapy.exporters.JsonLinesItemExporter")
### CSV
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `csv`
> * 出口商: [`CsvItemExporter`](exporters.html#scrapy.exporters.CsvItemExporter "scrapy.exporters.CsvItemExporter")
> * 指定要导出的列及其顺序的步骤使用 [`FEED_EXPORT_FIELDS`](#std:setting-FEED_EXPORT_FIELDS) . 其他feed导出器也可以使用此选项,但对于csv很重要,因为与许多其他导出格式不同,csv使用固定头。
### XML
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `xml`
> * 出口商: [`XmlItemExporter`](exporters.html#scrapy.exporters.XmlItemExporter "scrapy.exporters.XmlItemExporter")
### 泡菜
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `pickle`
> * 出口商: [`PickleItemExporter`](exporters.html#scrapy.exporters.PickleItemExporter "scrapy.exporters.PickleItemExporter")
### 元帅
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT): `marshal`
> * 出口商: `MarshalItemExporter`
## 储藏室
当使用feed导出时,您定义使用uri(通过 [`FEED_URI`](#std:setting-FEED_URI) 设置)。提要导出支持由URI方案定义的多个存储后端类型。
开箱支持的存储后端包括:
> * [本地文件系统](#topics-feed-storage-fs)
> * [FTP](#topics-feed-storage-ftp)
> * [S3](#topics-feed-storage-s3) (需要botocore_u或boto_u)
> * [标准输出](#topics-feed-storage-stdout)
如果所需的外部库不可用,则某些存储后端可能不可用。例如,只有在安装了botocore_u或boto_u库的情况下,S3后端才可用(scrapy仅在python 2上支持boto_u)。
## 存储URI参数
存储URI还可以包含在创建源时被替换的参数。这些参数是:
> * `%(time)s` -在创建源时被时间戳替换
> * `%(name)s` -替换为 Spider 名称
任何其他命名参数都将被同名的spider属性替换。例如, `%(site_id)s` 将被替换为 `spider.site_id` 属性为正在创建源的时刻。
下面举例说明:
> * 使用每个spider一个目录存储在ftp中:
> * `ftp://user:password@ftp.example.com/scraping/feeds/%(name)s/%(time)s.json`
> * 使用每个spider一个目录存储在S3中:
> * `s3://mybucket/scraping/feeds/%(name)s/%(time)s.json`
## 存储后端
### 本地文件系统
源存储在本地文件系统中。
> * URI方案: `file`
> * 示例性URI: `file:///tmp/export.csv`
> * 所需外部库:无
请注意,对于本地文件系统存储(仅限),如果您指定类似 `/tmp/export.csv` . 不过,这只在UNIX系统上工作。
### FTP
这些提要存储在FTP服务器中。
> * URI方案: `ftp`
> * 示例性URI: `ftp://user:pass@ftp.example.com/path/to/export.csv`
> * 所需外部库:无
### S3
源存储在 [Amazon S3](https://aws.amazon.com/s3/) .
> * URI方案: `s3`
> * URI示例:
> * `s3://mybucket/path/to/export.csv`
> * `s3://aws_key:aws_secret@mybucket/path/to/export.csv`
> * 所需的外部库: [botocore](https://github.com/boto/botocore) (python 2和python 3)或 [boto](https://github.com/boto/boto) (仅Python 2)
AWS凭证可以作为用户/密码在URI中传递,也可以通过以下设置传递:
> * [`AWS_ACCESS_KEY_ID`](settings.html#std:setting-AWS_ACCESS_KEY_ID)
> * [`AWS_SECRET_ACCESS_KEY`](settings.html#std:setting-AWS_SECRET_ACCESS_KEY)
还可以使用此设置为导出的源定义自定义ACL:
> * [`FEED_STORAGE_S3_ACL`](#std:setting-FEED_STORAGE_S3_ACL)
### 标准输出
进料被写入废料处理的标准输出。
> * URI方案: `stdout`
> * 示例性URI: `stdout:`
> * 所需外部库:无
## 设置
以下是用于配置源导出的设置:
> * [`FEED_URI`](#std:setting-FEED_URI) (强制性)
> * [`FEED_FORMAT`](#std:setting-FEED_FORMAT)
> * [`FEED_STORAGES`](#std:setting-FEED_STORAGES)
> * [`FEED_STORAGE_S3_ACL`](#std:setting-FEED_STORAGE_S3_ACL)
> * [`FEED_EXPORTERS`](#std:setting-FEED_EXPORTERS)
> * [`FEED_STORE_EMPTY`](#std:setting-FEED_STORE_EMPTY)
> * [`FEED_EXPORT_ENCODING`](#std:setting-FEED_EXPORT_ENCODING)
> * [`FEED_EXPORT_FIELDS`](#std:setting-FEED_EXPORT_FIELDS)
> * [`FEED_EXPORT_INDENT`](#std:setting-FEED_EXPORT_INDENT)
### FEED_URI
违约: `None`
导出源的URI。见 [存储后端](#topics-feed-storage-backends) 用于支持的URI方案。
启用源导出需要此设置。
### FEED_FORMAT
要用于源的序列化格式。见 [序列化格式](#topics-feed-format) 对于可能的值。
### FEED_EXPORT_ENCODING
违约: `None`
要用于源的编码。
如果未设置或设置为 `None` (默认)它对除JSON输出之外的所有内容都使用UTF-8,JSON输出使用安全的数字编码。( `\uXXXX` 序列)出于历史原因。
使用 `utf-8` 如果您也想要为JSON使用UTF-8。
### FEED_EXPORT_FIELDS
违约: `None`
要导出的字段列表,可选。例子: `FEED_EXPORT_FIELDS = ["foo", "bar", "baz"]` .
使用feed_export_fields选项定义要导出的字段及其顺序。
当feed-export-fields为空或无时(默认),scrappy使用dicts或 [`Item`](items.html#scrapy.item.Item "scrapy.item.Item") Spider 正在屈服的亚纲。
如果导出器需要一组固定的字段(这是 [CSV](#topics-feed-format-csv) export format)和feed_export_字段为空或无,然后scrapy尝试从导出的数据中推断字段名-当前它使用第一个项目中的字段名。
### FEED_EXPORT_INDENT
违约: `0`
用于在每个级别上缩进输出的空间量。如果 `FEED_EXPORT_INDENT` 是非负整数,则数组元素和对象成员将以该缩进级别进行漂亮打印。缩进量 `0` (默认值)或负数,将把每个项目放到一个新行上。 `None` 选择最紧凑的表示形式。
当前仅由执行 [`JsonItemExporter`](exporters.html#scrapy.exporters.JsonItemExporter "scrapy.exporters.JsonItemExporter")[`XmlItemExporter`](exporters.html#scrapy.exporters.XmlItemExporter "scrapy.exporters.XmlItemExporter") ,即当您要导出到 `.json``.xml` .
### FEED_STORE_EMPTY
违约: `False`
是否导出空源(即没有项目的源)。
### FEED_STORAGES
违约: `{{}}`
包含项目支持的其他提要存储后端的dict。键是URI方案,值是指向存储类的路径。
### FEED_STORAGE_S3_ACL
违约: `''` (空字符串)
包含项目导出到AmazonS3的源的自定义ACL的字符串。
有关可用值的完整列表,请访问 [Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) 亚马逊S3文档部分。
### FEED_STORAGES_BASE
违约::
```py
{
'': 'scrapy.extensions.feedexport.FileFeedStorage',
'file': 'scrapy.extensions.feedexport.FileFeedStorage',
'stdout': 'scrapy.extensions.feedexport.StdoutFeedStorage',
's3': 'scrapy.extensions.feedexport.S3FeedStorage',
'ftp': 'scrapy.extensions.feedexport.FTPFeedStorage',
}
```
包含由Scrapy支持的内置提要存储后端的dict。您可以通过分配 `None` 到他们的URI方案 [`FEED_STORAGES`](#std:setting-FEED_STORAGES) . 例如,要禁用内置FTP存储后端(不替换),请将其放入 `settings.py` ::
```py
FEED_STORAGES = {
'ftp': None,
}
```
### FEED_EXPORTERS
违约: `{{}}`
包含项目支持的其他导出器的dict。键是序列化格式,值是指向 [Item exporter](exporters.html#topics-exporters) 类。
### FEED_EXPORTERS_BASE
违约::
```py
{
'json': 'scrapy.exporters.JsonItemExporter',
'jsonlines': 'scrapy.exporters.JsonLinesItemExporter',
'jl': 'scrapy.exporters.JsonLinesItemExporter',
'csv': 'scrapy.exporters.CsvItemExporter',
'xml': 'scrapy.exporters.XmlItemExporter',
'marshal': 'scrapy.exporters.MarshalItemExporter',
'pickle': 'scrapy.exporters.PickleItemExporter',
}
```
包含由Scrapy支持的内置饲料导出器的dict。您可以通过分配 `None` 到其序列化格式 [`FEED_EXPORTERS`](#std:setting-FEED_EXPORTERS) . 例如,要禁用内置的csv导出器(不替换),请将其放入 `settings.py` ::
```py
FEED_EXPORTERS = {
'csv': None,
}
```
\ No newline at end of file
此差异已折叠。
# 链接提取器
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
链接提取器是对象,其唯一目的是从网页中提取链接( [`scrapy.http.Response`](request-response.html#scrapy.http.Response "scrapy.http.Response") 对象),最终将遵循。
`scrapy.linkextractors.LinkExtractor` 在Scrapy中可用,但是您可以通过实现一个简单的接口来创建自己的自定义链接提取器来满足您的需要。
每个链接提取器唯一拥有的公共方法是 `extract_links` ,接收 [`Response`](request-response.html#scrapy.http.Response "scrapy.http.Response") 对象并返回 `scrapy.link.Link` 物体。链接提取器将被实例化一次及其 `extract_links` 方法多次调用,并使用不同的响应提取要跟踪的链接。
链接提取器用于 [`CrawlSpider`](spiders.html#scrapy.spiders.CrawlSpider "scrapy.spiders.CrawlSpider") 类(在scrappy中可用),通过一组规则,但您也可以在spider中使用它,即使您不从 [`CrawlSpider`](spiders.html#scrapy.spiders.CrawlSpider "scrapy.spiders.CrawlSpider") 因为它的目的很简单:提取链接。
## 内置链接提取程序参考
[`scrapy.linkextractors`](#module-scrapy.linkextractors "scrapy.linkextractors: Link extractors classes") 模块。
默认的链接提取程序是 `LinkExtractor` ,与 [`LxmlLinkExtractor`](#scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor "scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor") ::
```py
from scrapy.linkextractors import LinkExtractor
```
以前的Scrapy版本中还有其他的链接提取器类,但现在已经不推荐使用了。
### LxmlLinkExtractor
```py
class scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor(allow=(), deny=(), allow_domains=(), deny_domains=(), deny_extensions=None, restrict_xpaths=(), restrict_css=(), tags=('a', 'area'), attrs=('href', ), canonicalize=False, unique=True, process_value=None, strip=True)
```
LXMLlinkextractor是推荐的带有便捷过滤选项的链接提取程序。它是使用LXML的健壮的HTMLParser实现的。
| 参数: |
* **allow** (_a regular expression_ _(or_ _list of__)_) -- (绝对)URL必须匹配才能提取的单个正则表达式(或正则表达式列表)。如果没有给定(或为空),它将匹配所有链接。
* **deny** (_a regular expression_ _(or_ _list of__)_) -- (绝对)URL必须匹配的单个正则表达式(或正则表达式列表)才能排除(即不提取)。它优先于 `allow` 参数。如果未给定(或为空),则不会排除任何链接。
* **allow_domains** (_str_ _or_ _list_) -- 包含用于提取链接的域的单个值或字符串列表。
* **deny_domains** (_str_ _or_ _list_) -- 包含域的单个值或字符串列表,这些域不会被视为提取链接的域。
* **deny_extensions** (_list_) -- 包含在提取链接时应忽略的扩展名的单个值或字符串列表。如果没有给出,它将默认为 `IGNORED_EXTENSIONS` 在中定义的列表 [scrapy.linkextractors](https://github.com/scrapy/scrapy/blob/master/scrapy/linkextractors/__init__.py) 包。
* **restrict_xpaths** (_str_ _or_ _list_) -- 是一个xpath(或xpath的列表),它定义响应中应该从中提取链接的区域。如果给定,则只扫描由这些xpath选择的文本中的链接。见下面的例子。
* **restrict_css** (_str_ _or_ _list_) -- 一个CSS选择器(或选择器列表),它定义响应中应该从中提取链接的区域。行为与 `restrict_xpaths` .
* **restrict_text** (_a regular expression_ _(or_ _list of__)_) -- 链接文本必须匹配才能提取的单个正则表达式(或正则表达式列表)。如果没有给定(或为空),它将匹配所有链接。如果给出了一个正则表达式列表,那么如果链接与至少一个匹配,则将提取该链接。
* **tags** (_str_ _or_ _list_) -- 提取链接时要考虑的标记或标记列表。默认为 `('a', 'area')` .
* **attrs** (_list_) -- 在查找要提取的链接时应考虑的属性或属性列表(仅适用于在 `tags` 参数)。默认为 `('href',)`
* **canonicalize** (_boolean_) -- 规范化每个提取的URL(使用w3lib.url.canonicalize_url)。默认为 `False` . 请注意,规范化URL用于重复检查;它可以更改服务器端可见的URL,因此对于使用规范化URL和原始URL的请求,响应可能不同。如果您使用linkextractor跟踪链接,那么保持默认链接更为可靠。 `canonicalize=False` .
* **unique** (_boolean_) -- 是否对提取的链接应用重复筛选。
* **process_value** (_callable_) -- 一种函数,接收从扫描的标记和属性中提取的每个值,并能修改该值并返回一个新值,或返回 `None` 完全忽略链接。如果没有给出, `process_value` 默认为 `lambda x: x` . …highlight::html例如,要从此代码中提取链接,请执行以下操作::&lt;a href=“javascript:gotopage('../other/page.html');return false“&gt;link text.&lt;a&gt;。highlight::python您可以在 `process_value` ::def process_value(value):m=re.search(“[javascript:gotopage](javascript:gotopage)('(.*?)'”,value)如果m:返回m.group(1)
* **strip** (_boolean_) -- 是否从提取的属性中删除空白。根据HTML5标准,必须从 `href` 属性 `&lt;a&gt;``&lt;area&gt;` 还有许多其他元素, `src` 属性 `&lt;img&gt;``&lt;iframe&gt;` 元素等,因此linkextractor默认情况下会删除空格字符。集合 `strip=False` 关闭它(例如,如果从允许前导/尾随空格的元素或属性中提取URL)。
|
| --- | --- |
\ No newline at end of file
此差异已折叠。
此差异已折叠。
# 第一步
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
\ No newline at end of file
# 内置服务
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 解决具体问题
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 延伸 Scrapy
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册