Scrapy學習篇:Selector詳解

在你爬取網頁的時候,最普遍的事情就是在頁面源碼中提取需要的數據,我們有幾個庫可以幫你完成這個任務:

  1. BeautifulSoup是python中一個非常流行的抓取庫, 它還能合理的處理錯誤格式的標籤,但是有一個唯一缺點就是:它運行很慢。
  2. lxml是一個基於ElementTree的XML解析庫(同時還能解析HTML), 不過lxml並不是Python標準庫

而Scrapy實現了自己的數據提取機制,它們被稱為選擇器,通過XPath或CSS表達式在HTML文檔中來選擇特定的部分

XPath是一用來在XML中選擇節點的語言,同時可以用在HTML上面。 CSS是一種HTML文檔上面的樣式語言。

Scrapy選擇器構建在lxml基礎之上,所以可以保證速度和準確性。

本章我們來詳細講解下選擇器的工作原理,還有它們極其簡單和相似的API,比lxml的API少多了,因為lxml可以用於很多其他領域。

關於選擇器

Scrapy幫我們下載完頁面後,我們怎樣在滿是html標籤的內容中找到我們所需要的元素呢,這裡就需要使用到選擇器了,它們是用來定位元素並且提取元素的值。先來舉幾個例子看看:

  • /html/head/title: 選擇<title>節點, 它位於html文檔的節點內/<title>
  • /html/head/title/text(): 選擇上面的<title>節點的內容./<title>
  • //td: 選擇頁面中所有的元素
  • //div[@class=”mine”]: 選擇所有擁有屬性class="mine"的div元素

Scrapy使用css和xpath選擇器來定位元素,它有四個基本方法:

  • xpath(): 返回選擇器列表,每個選擇器代表使用xpath語法選擇的節點
  • css(): 返回選擇器列表,每個選擇器代表使用css語法選擇的節點
  • extract(): 返回被選擇元素的unicode字符串
  • re(): 返回通過正則表達式提取的unicode字符串列表

使用選擇器

下面我們通過Scrapy shell演示下選擇器的使用,假設我們有如下的一個網頁http://doc.scrapy.org/en/latest/_static/selectors-sample1.html,內容如下:



<base>

<title>Example website/<title>










首先我們打開shell

scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html

運行

>>> response.xpath('//title/text()')
[<selector>]
>>> response.css('title::text')
[<selector>]/<selector>/<selector>

結果可以看出,xpath()和css()方法返回的是SelectorList實例,是一個選擇器列表,你可以選擇嵌套的數據:

>>> response.css('img').xpath('@src').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

必須使用.extract()才能提取最終的數據,如果你只想獲得第一個匹配的,可以使用.extract_first()

>>> response.xpath('//div[@id="images"]/a/text()').extract_first()
u'Name: My image 1 '

如果沒有找到,會返回None,還可選擇默認值

>>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found')
'not-found'

而CSS選擇器還可以使用CSS3標準:

>>> response.css('title::text').extract()
[u'Example website']

下面是幾個比較全面的示例:

>>> response.xpath('//base/@href').extract()
[u'http://example.com/']

>>> response.css('base::attr(href)').extract()
[u'http://example.com/']

>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']

>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']

>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

嵌套選擇器

xpath()和css()返回的是選擇器列表,所以你可以繼續使用它們的方法。舉例來講:

>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.extract()
[u' ',
u' ',
u' ',
u' ',
u' ']

>>> for index, link in enumerate(links):
... args = (index, link.xpath('@href').extract(), link.xpath('img/@src').extract())
... print 'Link number %d points to url %s and image %s' % args

Link number 0 points to url [u'image1.html'] and image [u'image1_thumb.jpg']
Link number 1 points to url [u'image2.html'] and image [u'image2_thumb.jpg']
Link number 2 points to url [u'image3.html'] and image [u'image3_thumb.jpg']
Link number 3 points to url [u'image4.html'] and image [u'image4_thumb.jpg']
Link number 4 points to url [u'image5.html'] and image [u'image5_thumb.jpg']

使用正則表達式

Selector有一個re()方法通過正則表達式提取數據,它返回的是unicode字符串列表,你不能再去嵌套使用

>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\\s*(.*)')
[u'My image 1',
u'My image 2',
u'My image 3',
u'My image 4',
u'My image 5']

>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\\s*(.*)')
u'My image 1'

XPath相對路徑

當你嵌套使用XPath時候,不要使用/開頭的,因為這個會相對文檔根節點開始算起,需要使用相對路徑

>>> divs = response.xpath('//div')
>>> for p in divs.xpath('.//p'): # extracts all

inside
... print p.extract()

# 或者下面這個直接使用p也可以
>>> for p in divs.xpath('p'):
... print p.extract()

XPath建議

使用text作為條件時

避免使用.//text(),直接使用.

>>> sel.xpath("//a[contains(., 'Next Page')]").extract()
[u' ']

//node[1]和(//node)[1]區別

  • //node[1]: 選擇所有位於第一個子節點位置的node節點
  • (//node)[1]: 選擇所有的node節點,然後返回結果中的第一個node節點

通過class查找時優先考慮CSS

>> from scrapy import Selector
>>> sel = Selector(text='
<time>Special date/<time>
')
>>> sel.css('.shout').xpath('./time/@datetime').extract()
[u'2014-07-23 19:00']


分享到:


相關文章: