[url=http://doc.scrapy.org/en/0.16/topics/items.html#scrapy.item.Item]scrapy.item.Item[/url]类来声明并定义它们的属性作为[url=http://doc.scrapy.org/en/0.16/topics/items.html#scrapy.item.Field]scrapy.item.Field [/url]对象,就像是一个对象关系映射(假如你不熟悉ORMs,你将会看见它是一个简单的任务).
我们将需要的item模块化,来控制从demoz.org网站获取的数据,比如我们将要去抓取网站的名字,url和描述信息。我们定义这三种属性的域。我们编辑items.py文件,它在向导目录中。我们Item类看起来像这样。
from scrapy.item import Item, Field
class DmozItem(Item):
title = Field()
link = Field()
desc = Field()
这个看起来复杂的,但是定义这些item能让你用其他Scrapy组件的时候知道你的item到底是什么
[b]我们第一个Spider[/b]
Spiders是用户写的类,它用来去抓取一个网站的信息(或者一组网站) 。
我们定义一个初始化的URLs列表去下载,如何跟踪链接,如何去解析这些页面的内容去提取 [url=http://doc.scrapy.org/en/0.16/topics/items.html#topics-items]items[/url].创建一个Spider,你必须是[url=http://doc.scrapy.org/en/0.16/topics/spiders.html#scrapy.spider.BaseSpider]scrapy.spider.BaseSpider[/url]的子类, 并定义三个主要的,强制性的属性。
[url=http://doc.scrapy.org/en/0.16/topics/spiders.html#scrapy.spider.BaseSpider.name]名字[/url]: Spider的标识. 它必须是唯一的, 那就是说,你不能在不同的Spiders中设置相同的名字。
[url=http://doc.scrapy.org/en/0.16/topics/spiders.html#scrapy.spider.BaseSpider.start_urls]开始链接[/url]:Spider将会去爬这些URLs的列表。所以刚开始的下载页面将要包含在这些列表中。其他子URL将会从这些起始URL中继承性生成。
[url=http://doc.scrapy.org/en/0.16/topics/spiders.html#scrapy.spider.BaseSpider.parse]parse() [/url]是spider的一个方法, 调用时候传入从每一个URL传回的[url=http://doc.scrapy.org/en/0.16/topics/request-response.html#scrapy.http.Response]Response[/url]对象作为参数。response是方法的唯一参数。
这个方法负责解析response数据和提出抓取的数据(作为抓取的items),跟踪URLs
[url=http://doc.scrapy.org/en/0.16/topics/spiders.html#scrapy.spider.BaseSpider.parse] parse()[/url]方法负责处理response和返回抓取数据(作为[url=http://doc.scrapy.org/en/0.16/topics/items.html#scrapy.item.Item]Item[/url]对象) 和跟踪更多的URLs(作为[url=http://doc.scrapy.org/en/0.16/topics/request-response.html#scrapy.http.Request]request[/url]的对象)
这是我们的第一个Spider的代码;它保存在moz/spiders文件夹中,被命名为dmoz_spider.py:
from scrapy.spider import BaseSpider
class DmozSpider(BaseSpider):
name = "dmoz"
allowed_domains = ["dmoz.org"]
start_urls = [
"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
]
def parse(self, response):
filename = response.url.split("/")[-2]
open(filename, 'wb').write(response.body)
[b]爬[/b]
为了使你的spider工作, 到项目的顶级目录让后运行:
crawl dmoz命令使spider去爬dmoz.org网站的信息。你将会得到如下类似的信息:
2008-08-20 03:51:13-0300 [scrapy] INFO: Started project: dmoz
2008-08-20 03:51:13-0300 [tutorial] INFO: Enabled extensions: ...
2008-08-20 03:51:13-0300 [tutorial] INFO: Enabled downloader middlewares: ...
2008-08-20 03:51:13-0300 [tutorial] INFO: Enabled spider middlewares: ...
2008-08-20 03:51:13-0300 [tutorial] INFO: Enabled item pipelines: ...
2008-08-20 03:51:14-0300 [dmoz] INFO: Spider opened
2008-08-20 03:51:14-0300 [dmoz] DEBUG: Crawled <http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: <None>)
2008-08-20 03:51:14-0300 [dmoz] DEBUG: Crawled <http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: <None>)
2008-08-20 03:51:14-0300 [dmoz] INFO: Spider closed (finished)
注意那些行包含[dmoz], 它和我们的spider相关。你能够看见每行初始化的URL日志信息。因为这些URLs是起始页面,所以他们没有引用referrers。 所以在每行的末尾部门,你能看见(referer: <None>).
但是有趣的是,在我们的parse方法作用下,两个文件被创建: Books and Resources, 它保航两个URLs的内容
[b]刚刚发生了什么事情?[/b]
Scrapy为每一个start_urls创建一个[url=http://doc.scrapy.org/en/0.16/topics/request-response.html#scrapy.http.Request]scrapy.http.Request[/url]对象,并将爬虫的parse 方法指定为回调函数。
这些Request首先被调度,然后被执行,之后通过parse()方法,将[url=http://doc.scrapy.org/en/0.16/topics/request-response.html#scrapy.http.Response]scrapy.http.Response[/url]对象被返回,结果也被反馈给爬虫。
[b]提取Items
选择器介绍[/b]
我们有多种方式去提取网页中数据。Scrapy 使用的是[url=http://www.w3.org/TR/xpath]XPath[/url]表达式,通常叫做[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#topics-selectors]XPath selectors[/url]。如果想了解更多关于选择器和提取数据的机制,可以看看如下教程[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#topics-selectors]XPath selectors documentation[/url].
这里有一些表达式的例子和它们相关的含义:
[list]
[*] /html/head/title: 选择<title>元素,在HTML文档的<head>元素里[/*]
[*] /html/head/title/text(): 选择<title>元素里面的文本[/*]
[*] //td: 选择所有的<td>元素[/*]
[*] //div[@class="mine"]: 选择所有的div元素里面class属性为mine的[/*]
[/list]
这里有许多的例子关于怎么使用XPath,可以说XPath表达式是非常强大的。如果你想要学习更多关于XPath,我们推荐如下教程[url=http://www.w3schools.com/XPath/default.asp]this XPath tutorial[/url].
为了更好使用XPaths, Scrapy提供了一个[url=http://XPathSelector]XPathSelector[/url]类,它有两种方式, [url=http://doc.scrapy.org/en/0.16/topics/selectors.html#scrapy.selector.HtmlXPathSelector]HtmlXPathSelector[/url](HTML相关数据)和[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#scrapy.selector.XmlXPathSelector]XmlXPathSelector[/url](XML相关数据)。如果你想使用它们,你必须实例化一个[url=http://doc.scrapy.org/en/0.16/topics/request-response.html#scrapy.http.Response]Response[/url]对象.
你能够把selectors作为对象,它代表文件结构中的节点。所以,第1个实例的节点相当于root节点,或者称为整个文档的节点。
选择器有三种方法(点击方法你能够看见完整的API文档)。
[list]
[*] [url=http://doc.scrapy.org/en/0.16/topics/selectors.html#scrapy.selector.XPathSelector.select]select()[/url]: 返回选择器的列表,每一个select表示一个xpath表达式选择的节点。[/*]
[*] [url=http://doc.scrapy.org/en/0.16/topics/selectors.html#scrapy.selector.XPathSelector.extract]extract()[/url]: 返回一个unicode字符串 ,该字符串XPath选择器返回的数据。[/*]
[*] [url=http://doc.scrapy.org/en/0.16/topics/selectors.html#scrapy.selector.XPathSelector.re]re()[/url] : 返回unicode字符串列表,字符串作为参数由正则表达式提取出来。[/*]
[/list]
[b]在Shell里面使用选择器[/b]
为了更加形象的使用选择器,我们将会使用[url=http://doc.scrapy.org/en/0.16/topics/shell.html#topics-shell]Scrapy shell[/url],它同时需要你的系统安装IPython (一个扩展的Python控制台)。
如果使用shell,你必须到项目的顶级目录上,让后运行如下命令:
scrapy shell http://www.dmoz.org/Computers/Programming/Languages/Python/Books/
shell将会显示如下的信息
[ ... Scrapy log here ... ]
[s] Available Scrapy objects:
[s] 2010-08-19 21:45:59-0300 [default] INFO: Spider closed (finished)
[s] hxs <HtmlXPathSelector (http://www.dmoz.org/Computers/Programming/Languages/Python/Books/) xpath=None>
[s] item Item()
[s] request <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
[s] response <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
[s] spider <BaseSpider 'default' at 0x1b6c2d0>
[s] xxs <XmlXPathSelector (http://www.dmoz.org/Computers/Programming/Languages/Python/Books/) xpath=None>
[s] Useful shortcuts:
[s] shelp() Print this help
[s] fetch(req_or_url) Fetch a new request or URL and update shell objects
[s] view(response) View response in a browser
In [1]:
当shell装载之后,你将会得到一个response的本地变量。所以你输入reponse.body,你能够看见response的body部分或者你能够输入response.headers,你能够看见reponse.headers部分。
shell同样实例化了两个选择器,一个是HTML(在hvx变量里),一个是XML(在xxs变量里)。所以我们尝试怎么使用它们:
In [1]: hxs.select('//title')
Out[1]: [<HtmlXPathSelector (title) xpath=//title>]
In [2]: hxs.select('//title').extract()
Out[2]: [u'<title>Open Directory - Computers: Programming: Languages: Python: Books</title>']
In [3]: hxs.select('//title/text()')
Out[3]: [<HtmlXPathSelector (text) xpath=//title/text()>]
In [4]: hxs.select('//title/text()').extract()
Out[4]: [u'Open Directory - Computers: Programming: Languages: Python: Books']
In [5]: hxs.select('//title/text()').re('(\w+):')
Out[5]: [u'Computers', u'Programming', u'Languages', u'Python']
[b]提取数据Extracting the data[/b]
现在我们开始尝试在这几个页面里提取真正的信息。
你能够在控制台里面输入response.body,检查源代码里面的XPaths是否与预期相同。然而,检查原始的HTML代码是一件非常枯燥乏味的事情。假如你想让你的工作变的简单,你使用Firefox扩展的插件例如Firebug来做这项任务。更多关于介绍信息请看[url=http://doc.scrapy.org/en/0.16/topics/firebug.html#topics-firebug]Using Firebug for scraping[/url]和[url=http://doc.scrapy.org/en/0.16/topics/firefox.html#topics-firefox]Using Firefox for scraping[/url]。
当你检查了页面源代码之后,你将会发现页面的信息放在一个<ul>元素里面,事实上,确切地说是第二个<ul>元素。
所以我们选择每一个<li>元素使用如下的代码:
网站的描述信息可以使用如下代码:
hxs.select('//ul/li/text()').extract()
网站的标题:
hxs.select('//ul/li/a/text()').extract()
网站的链接:
hxs.select('//ul/li/a/@href').extract()
如前所述,每个select()调用返回一个selectors列表,所以我们可以结合select()去挖掘更深的节点。我们将会用到这些特性,所以:
sites = hxs.select('//ul/li')
for site in sites:
title = site.select('a/text()').extract()
link = site.select('a/@href').extract()
desc = site.select('text()').extract()
print title, link, desc
Note
如果想了解更多的嵌套选择器,可以参考[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#topics-selectors-nesting-selectors]Nesting selectors[/url]和[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#topics-selectors-relative-xpaths]Working with relative XPaths[/url]相关的[url=http://doc.scrapy.org/en/0.16/topics/selectors.html#topics-selectors]Selectors[/url]文档
将代码添加到我们spider中:
from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
class DmozSpider(BaseSpider):
name = "dmoz"
allowed_domains = ["dmoz.org"]
start_urls = [
"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
]
def parse(self, response):
hxs = HtmlXPathSelector(response)
sites = hxs.select('//ul/li')
for site in sites:
title = site.select('a/text()').extract()
link = site.select('a/@href').extract()
desc = site.select('text()').extract()
print title, link, desc
现在我们再次抓取dmoz.org,你将看到站点在输出中被打印 ,运行命令:
[b]使用我们的 item[/b]
[url=http://doc.scrapy.org/en/0.16/topics/items.html#scrapy.item.Item]Item[/url]对象是自定义python字典;使用标准字典类似的语法,你能够访问它们的字段(就是以前我们定义的属性)
>>> item = DmozItem()
>>> item['title'] = 'Example title'
>>> item['title']
'Example title'
Spiders希望将抓取的数据放在 Item对象里。所以,为了返回我们抓取的数据,最终的代码要如下这么写 :
from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from tutorial.items import DmozItem
class DmozSpider(BaseSpider):
name = "dmoz"
allowed_domains = ["dmoz.org"]
start_urls = [
"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
]
def parse(self, response):
hxs = HtmlXPathSelector(response)
sites = hxs.select('//ul/li')
items = []
for site in sites:
item = DmozItem()
item['title'] = site.select('a/text()').extract()
item['link'] = site.select('a/@href').extract()
item['desc'] = site.select('text()').extract()
items.append(item)
return items
[b]Note
[/b]
你能够找到完整功能的spider在dirbot项目里,同样你可以访问https://github.com/scrapy/dirbot
现在重新抓取dmoz.org网站:
[dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
{'desc': [u' - By David Mertz; Addison Wesley. Book in progress, full text, ASCII format. Asks for feedback. [author website, Gnosis Software, Inc.\n],
'link': [u'http://gnosis.cx/TPiP/'],
'title': [u'Text Processing in Python']}
[dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
{'desc': [u' - By Sean McGrath; Prentice Hall PTR, 2000, ISBN 0130211192, has CD-ROM. Methods to build XML applications fast, Python tutorial, DOM and SAX, new Pyxie open source XML processing library. [Prentice Hall PTR]\n'],
'link': [u'http://www.informit.com/store/product.aspx?isbn=0130211192'],
'title': [u'XML Processing with Python']}
[b]
存储抓取的数据[/b]
最简单的方式去存储抓取的数据是使用[url=http://doc.scrapy.org/en/0.16/topics/feed-exports.html#topics-feed-exports]Feed exports[/url],使用如下的命令:
scrapy crawl dmoz -o items.json -t json
它将会产生一个items.json文件,它包含所有抓取的items(序列化的[url=http://en.wikipedia.org/wiki/JSON]JSON[/url])。
在一些小的项目里(例如我们的教程中),那就足够啦。然而,假如你想要执行更多复杂的抓取items,你能够写一个 [url=http://doc.scrapy.org/en/0.16/topics/item-pipeline.html#topics-item-pipeline]Item Pipeline[/url]。 因为在项目创建的时候,一个专门用于Item Pipelines的占位符文件已经随着项目一起被建立,目录在tutorial/pipelines.py。如果你只需要存取这些抓取后的items的话,就不需要去实现任何的条目管道。