Scrapy 教程(Scrapy Tutorial)

官方教程here  http://doc.scrapy.org/en/latest/intro/tutorial.html
 
Item
Item是保存爬取到的数据的容器,使用方法与python中的dict类似,此外它还提供了额外的保护机制来避免拼写错误导致的未定义错误。
要声明一个Item,需要创建一个csrapy.Item类,并且定义scrapy.Field属性
比如,要保存从某个网站(dmoz.org)爬取的数据,首先要对item建模。如果我们希望得到站点的name,url和description,则需要分别为这三个属性定义字段。编辑tutorial目录中的items.py文件:
通过定义item,可以很方便地使用Scrapy组件。

 
first Spider
spider是用户编写的用于从一个网站或者一组网站爬取数据的类
其中包括一个用于下载初始URL列表,以及跟进链接和对页面中的内容进行分析提取item的方法。
创建Spider必须要继承scrapy.class.BaseSpider类,并且定义以下三个属性
  • name:用于标识Spider,必须是唯一的。
  • start_urls: 是Spider最开始进行爬取的URL列表,因此最先获取的页面应该是其中之一。后续的URL应该从这些初始URL列表中获取到的数据中获得。
  • parse():  spider中的一个方法,每个初始URL完成下载后返回的Response对象作为唯一参数传递给方法。
          该方法负责解析返回的数据(response data),提取数据(生成item),以及其他需要处理的URL(Request 对象)
下面是第一个spider代码,将其保存为dmoz_spider.py,并存放在tutorial/spiders目录下:
 
Crawling
进入项目根目录,输入下面命令启动spider
scrapy crawl dmoz
查看输出,可以看到输出的log中包含定义在start_urls列表中的每一个初始URL。可以看到其没有指向其他页面( referer: <None>)
有两个包含url所对应内容的文件被创建了。
 
在刚才这个过程中,Scrapy为Spider的属性start_urls的每个URL创建了scrapy.Request对象,并且将parse()方法作为回调函数(callback)赋值给Request对象。
Request对象经过调度,执行并返回scrapy.http.response对象并送回给spider的parse()方法
 
Extracting Items
Seletor选择器介绍
从网页中提取信息有很多种方法。Scrapy使用了一种基于XPath或者CSS表达式的机制:Scrapy Seletors。更多关于selectors和其他提取机制的信息请参考 Selectors documentation
下面是一些关于XPath表达式的例子和其含义:
  • /html/head/title: 选择HTML文档中 <head> 标签内的 <title> 元素
  • /html/head/title/text(): 选择上面提到的 <title> 元素的文字
  • //td: 选择所有的 <td> 元素
  • //div[@class="mine"]: 选择所有具有 class="mine" 属性的 div 元素
上面仅是几个简单的例子,想了解更多可以参  this tutorial to learn XPath through examples和  this tutorial to learn “how to think in XPath”.
 
note:CSS vs XPath
虽然可以仅使用css选择器从网页上提取信息,然而XPath更为强大,因为其不仅可以利用结构进行操作,还可以利用页面内容:比如你可以选择内容包含文本“Next Page”的链接。正因为如此,即便已经掌握了CSS选择器,仍然推荐学习XPath。
 
为了利用CSS和XPath表达式,Scrapy提供了Selector类,还提供了快捷方法来避免每次需要从response中提取信息时都要实例化selector的麻烦。
可以将selector看做是一个表示文档结构的对象。所以第一个生成的selector就是根节点,或者说是整个文档。
selector有四个基本方法
  • xpath():返回一个selectors列表,每个selector表示参数中的xpath表达式获取的节点。
  • css():返回一个selectors列表,每个selector表示参数中的CSS表达式获取的节点。
  • extract():将选中的节点数据序列化为unicode字符串并返回
  • re():返回一个依据参数中的正则表达式提取的unicode字符串列表
 
在Shell中使用选择器
为了介绍selector的使用方法,使用内置的scrapy-shell,scrapy-shell需要安装IPython(一个扩展的Python终端)。
进入项目根目录,执行下列命令来启动终端
scrapy shell "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/"
shell载入后,得到一个包含response数据的本地response变量,输入response.body,将会看到response包体,输入response.headers将会输出response包头
更为重要的是,每个response都会返回一个selector选择器,你可以通过调用response.selector.xpath()或者response.selector.css()对response进行查询。还有很多类似于response.xpath或response.css()这种直接映射到response.selector.xpath()和response.selector.css()的方法。
 
Extracting the data
虽然可以在终端输入response.body,通过查看网页源代码来确定xpath表达式,但是检查原始html代码是非常繁琐的。为了更加方便推荐使用Firefox的开发者工具或者拓展比如FIrebug。具体使用方法参见  Using Firebug for scraping
查看网页源代码之后会发现网站的信息在第二个<ul>标签内。
我们可以通过下面一行代码选择该网站里的所有<li>元素:
response.xpath('//ul/li')
再从这里面找出网站的描述:
response.xpath('//ul/li/text()').extract()
网站里的链接:
response.xpath('//ul/li/a/@href').extract()
 
正如之前所描述的一样,每个.xpath()方法返回一个selectors列表,所以我们可以通过链接多个.xpath()方法来挖掘更深层次里的节点,比如下面这种嵌套的selector:
for sel in response.xpath('//ul/li'):
     title=sel.xpath('a/text()').extract()
     link=sel.xpath('a/@href').extract()
     desc=sel.spath('text()').extract()
     print title,link,desc
 
在我们的spider中加入下面代码
 
import scrapy

class DmozSpider(scrapy.Spider):
    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):
        for sel in response.xpath('//ul/li'):
            title = sel.xpath('a/text()').extract()
            link = sel.xpath('a/@href').extract()
            desc = sel.xpath('text()').extract()
            print title, link, desc
 
使用命令scrapy crawl dmoz查看爬取结果
 
Using our item
Item对象是自定义的python字典,可以通过标准的字典语法获得其每个字段的值,比如:
     
item = DmozItem()
>>> item['title'] = 'Example title'
>>> item['title']
'Example title'
 
为了得到我们爬取到的数据,最终的Spider代码如下:
 
import scrapy
from tutorial.items import DmozItem
 
class DmozSpider(scrapy.Spider):
    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):
        for sel in response.xpath('//ul/li'):
            item = DmozItem()
            item['title'] = sel.xpath('a/text()').extract()
            item['link'] = sel.xpath('a/@href').extract()
            item['desc'] = sel.xpath('text()').extract()
            yield item
 

Following links
现在假设不仅仅想要爬取books和resources页面的数据,还希望获得整个python目录的所有数据。
现在我们已经知道如何从一个页面提取数据,我们还可以尝试提取感兴趣的链接,跟踪链接然后提取所有链接里的数据。
下面是将spider做了一些修改,使其达到上述目的
 
import scrapy
from tutorial.items import DmozItem

class DmozSpider(scrapy.Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/",
    ]

    def parse(self, response):
        for href in response.css("ul.directory.dir-col > li > a::attr('href')"):
            url = response.urljoin(href.extract())
            yield scrapy.Request(url, callback=self.parse_dir_contents)

    def parse_dir_contents(self, response):
        for sel in response.xpath('//ul/li'):
            item = DmozItem()
            item['title'] = sel.xpath('a/text()').extract()
            item['link'] = sel.xpath('a/@href').extract()
            item['desc'] = sel.xpath('text()').extract()
            yield item
 
现在parse()方法将页面中所有感兴趣的链接都提取出来,使用response.urljoin()方法组成了一个完整的绝对URL,创建并发出新请求,回调函数parse_dir_contents()最终会爬取到我们想要的数据。
Scrapy跟踪链接的原理是在回调函数中创建一个请求,scrapy会发送请求并且创建一个新的回调函数,当请求结束之后执行该回调函数。
使用这种方法就可以创建复杂的爬虫来根据自定义的规则来跟踪链接,根据访问的页面提取到不同种类的数据。
 
经常使用的一种模式是使用回调函数提取一些item,通过链接来跟踪下一个页然后利用同样的回调函数创建新的链接。
 
def parse_articles_follow_next_page(self, response):
    for article in response.xpath("//article"):
        item = ArticleItem()
        ... extract article data here
        yield item
    next_page = response.css("ul.navigation > li.next-page > a::attr('href')")
    if next_page:
        url = response.urljoin(next_page[0].extract())
        yield scrapy.Request(url, self.parse_articles_follow_next_page)
 
这些代码创建了一个循环,跟踪到下一个页面的所有链接直到最后一页。常用这种渐变的方法去爬取博客、论坛和其他有分页的站点。
 
还有一种常用的模式是使用多个页面的数据创建一个item。  using a trick to pass additional data to the callbacks.

Storing the scraped data
最简单存储爬取的数据的方式是使用 Feed exports:
scrapy crawl dmoz -o items.json
这将使用json格式对所有爬取到的items序列化,产生一个items.json文件。
在诸如本例中的小程序里,这种存储方式足够了。然后如果想要对爬取到的items进行更复杂的操作,可以编写Item Pipeline。类似于items.py,在创建项目时候产生的还有pipelines.py文件,用于编写Item Pipelines,如果只希望存储爬取到的items则不需要实现任何item pipelines。
 

Next steps
这篇教程只是Scrapy基础,并未涉及其他的很多特性。 Check the What else? section in Scrapy at a glance chapter for a quick overview of the most important ones.
posted @ 2015-12-19 21:26  colors  阅读(611)  评论(0编辑  收藏  举报