Scrapy框架的使用之Selector的用法

Scrapy提供了本身的數據提取方法,即Selector(選擇器)。Selector是基於lxml來構建的,支持XPath選擇器、CSS選擇器以及正則表達式,功能全面,解析速度和準確度很是高。
css

本節將介紹Selector的用法。html

1. 直接使用

Selector是一個能夠獨立使用的模塊。咱們能夠直接利用Selector這個類來構建一個選擇器對象,而後調用它的相關方法如xpath()css()等來提取數據。
web

例如,針對一段HTML代碼,咱們能夠用以下方式構建Selector對象來提取數據:正則表達式

from scrapy import Selector

body = '<html><head><title>Hello World</title></head><body></body></html>'
selector = Selector(text=body)
title = selector.xpath('//title/text()').extract_first()
print(title)複製代碼

運行結果以下所示:shell

Hello World複製代碼

咱們在這裏沒有在Scrapy框架中運行,而是把Scrapy中的Selector單獨拿出來使用了,構建的時候傳入text參數,就生成了一個Selector選擇器對象,而後就能夠像前面咱們所用的Scrapy中的解析方式同樣,調用xpath()css()等方法來提取了。數組

在這裏咱們查找的是源代碼中的title中的文本,在XPath選擇器最後加text()方法就能夠實現文本的提取了。bash

以上內容就是Selector的直接使用方式。同Beautiful Soup等庫相似,Selector其實也是強大的網頁解析庫。若是方便的話,咱們也能夠在其餘項目中直接使用Selector來提取數據。微信

接下來,咱們用實例來詳細講解Selector的用法。網絡

2. Scrapy Shell

因爲Selector主要是與Scrapy結合使用,如Scrapy的回調函數中的參數response直接調用xpath()或者css()方法來提取數據,因此在這裏咱們藉助Scrapy Shell來模擬Scrapy請求的過程,來說解相關的提取方法。
框架

咱們用官方文檔的一個樣例頁面來作演示:http://doc.scrapy.org/en/latest/_static/selectors-sample1.html。

開啓Scrapy Shell,在命令行輸入以下命令:

scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html複製代碼

咱們就進入到Scrapy Shell模式。這個過程實際上是,Scrapy發起了一次請求,請求的URL就是剛纔命令行下輸入的URL,而後把一些可操做的變量傳遞給咱們,如requestresponse等,以下圖所示。

咱們能夠在命令行模式下輸入命令調用對象的一些操做方法,回車以後實時顯示結果。這與Python的命令行交互模式是相似的。

接下來,演示的實例都將頁面的源碼做爲分析目標,頁面源碼以下所示:

<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>複製代碼

3. XPath選擇器

進入Scrapy Shell以後,咱們將主要操做response這個變量來進行解析。由於咱們解析的是HTML代碼,Selector將自動使用HTML語法來分析。

response有一個屬性selector,咱們調用response.selector返回的內容就至關於用responsebody構造了一個Selector對象。經過這個Selector對象咱們能夠調用解析方法如xpath()css()等,經過向方法傳入XPath或CSS選擇器參數就能夠實現信息的提取。

咱們用一個實例感覺一下,以下所示:

>>> result = response.selector.xpath('//a')
>>> result
[<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>,
 <Selector xpath='//a' data='<a href="image2.html">Name: My image 2 <'>,
 <Selector xpath='//a' data='<a href="image3.html">Name: My image 3 <'>,
 <Selector xpath='//a' data='<a href="image4.html">Name: My image 4 <'>,
 <Selector xpath='//a' data='<a href="image5.html">Name: My image 5 <'>]
>>> type(result)
scrapy.selector.unified.SelectorList複製代碼

打印結果的形式是Selector組成的列表,其實它是SelectorList類型,SelectorList和Selector均可以繼續調用xpath()css()等方法來進一步提取數據。

在上面的例子中,咱們提取了a節點。接下來,咱們嘗試繼續調用xpath()方法來提取a節點內包含的img節點,以下所示:

>>> result.xpath('./img')
[<Selector xpath='./img' data='<img src="image1_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image2_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image3_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image4_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image5_thumb.jpg">'>]複製代碼

咱們得到了a節點裏面的全部img節點,結果爲5。

值得注意的是,選擇器的最前方加 .(點),這表明提取元素內部的數據,若是沒有加點,則表明從根節點開始提取。此處咱們用了./img的提取方式,則表明從a節點裏進行提取。若是此處咱們用//img,則仍是從html節點裏進行提取。

咱們剛纔使用了response.selector.xpath()方法對數據進行了提取。Scrapy提供了兩個實用的快捷方法,response.xpath()response.css(),它們兩者的功能徹底等同於response.selector.xpath()response.selector.css()。方便起見,後面咱們統一直接調用responsexpath()css()方法進行選擇。

如今咱們獲得的是SelectorList類型的變量,該變量是由Selector對象組成的列表。咱們能夠用索引單獨取出其中某個Selector元素,以下所示:

>>> result[0]
<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>複製代碼

咱們能夠像操做列表同樣操做這個SelectorList

可是如今獲取的內容是Selector或者SelectorList類型,並非真正的文本內容。那麼具體的內容怎麼提取呢?

好比咱們如今想提取出a節點元素,就能夠利用extract()方法,以下所示:

>>> result.extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']複製代碼

這裏使用了extract()方法,咱們就能夠把真實須要的內容獲取下來。

咱們還能夠改寫XPath表達式,來選取節點的內部文本和屬性,以下所示:

>>> response.xpath('//a/text()').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
>>> response.xpath('//a/@href').extract()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']複製代碼

咱們只須要再加一層/text()就能夠獲取節點的內部文本,或者加一層/@href就能夠獲取節點的href屬性。其中,@符號後面內容就是要獲取的屬性名稱。

如今咱們能夠用一個規則把全部符合要求的節點都獲取下來,返回的類型是列表類型。

可是這裏有一個問題:若是符合要求的節點只有一個,那麼返回的結果會是什麼呢?咱們再用一個實例來感覺一下,以下所示:

>>> response.xpath('//a[@href="image1.html"]/text()').extract()
['Name: My image 1 ']複製代碼

咱們用屬性限制了匹配的範圍,使XPath只能夠匹配到一個元素。而後用extract()方法提取結果,其結果仍是一個列表形式,其文本是列表的第一個元素。但不少狀況下,咱們其實想要的數據就是第一個元素內容,這裏咱們經過加一個索引來獲取,以下所示:

>>> response.xpath('//a[@href="image1.html"]/text()').extract()[0]
'Name: My image 1 '複製代碼

可是,這個寫法很明顯是有風險的。一旦XPath有問題,那麼extract()後的結果多是一個空列表。若是咱們再用索引來獲取,那不就會可能致使數組越界嗎?

因此,另一個方法能夠專門提取單個元素,它叫做extract_first()。咱們能夠改寫上面的例子以下所示:

>>> response.xpath('//a[@href="image1.html"]/text()').extract_first()
'Name: My image 1 '複製代碼

這樣,咱們直接利用extract_first()方法將匹配的第一個結果提取出來,同時咱們也不用擔憂數組越界的問題。

另外咱們也能夠爲extract_first()方法設置一個默認值參數,這樣當XPath規則提取不到內容時會直接使用默認值。例如將XPath改爲一個不存在的規則,從新執行代碼,以下所示:

>>> response.xpath('//a[@href="image1"]/text()').extract_first()
>>> response.xpath('//a[@href="image1"]/text()').extract_first('Default Image')
'Default Image'複製代碼

這裏,若是XPath匹配不到任何元素,調用extract_first()會返回空,也不會報錯。

在第二行代碼中,咱們還傳遞了一個參數看成默認值,如Default Image。這樣若是XPath匹配不到結果的話,返回值會使用這個參數來代替,能夠看到輸出正是如此。

如今爲止,咱們瞭解了Scrapy中的XPath的相關用法,包括嵌套查詢、提取內容、提取單個內容、獲取文本和屬性等。

4. CSS選擇器

接下來,咱們看看CSS選擇器的用法。

Scrapy的選擇器同時還對接了CSS選擇器,使用response.css()方法可使用CSS選擇器來選擇對應的元素。

例如在上文咱們選取了全部的a節點,那麼CSS選擇器一樣能夠作到,以下所示:

>>> response.css('a')
[<Selector xpath='descendant-or-self::a' data='<a href="image1.html">Name: My image 1 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image2.html">Name: My image 2 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image3.html">Name: My image 3 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image4.html">Name: My image 4 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image5.html">Name: My image 5 <'>]複製代碼

一樣,調用extract()方法就能夠提取出節點,以下所示:

>>> response.css('a').extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']複製代碼

用法和XPath選擇是徹底同樣的。

另外,咱們也能夠進行屬性選擇和嵌套選擇,以下所示:

>>> response.css('a[href="image1.html"]').extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>']
>>> response.css('a[href="image1.html"] img').extract()
['<img src="image1_thumb.jpg">']複製代碼

這裏用[href="image.html"]限定了href屬性,能夠看到匹配結果就只有一個了。另外若是想查找a節點內的img節點,只須要再加一個空格和img便可。選擇器的寫法和標準CSS選擇器寫法一模一樣。

咱們也可使用extract_first()方法提取列表的第一個元素,以下所示:

>>> response.css('a[href="image1.html"] img').extract_first()
'<img src="image1_thumb.jpg">'複製代碼

接下來的兩個用法不太同樣。節點的內部文本和屬性的獲取是這樣實現的,以下所示:

>>> response.css('a[href="image1.html"]::text').extract_first()
'Name: My image 1 '
>>> response.css('a[href="image1.html"] img::attr(src)').extract_first()
'image1_thumb.jpg'複製代碼

獲取文本和屬性須要用::text::attr()的寫法。而其餘庫如Beautiful Soup或pyquery都有單獨的方法。

另外,CSS選擇器和XPath選擇器同樣能夠嵌套選擇。咱們能夠先用XPath選擇器選中全部a節點,再利用CSS選擇器選中img節點,再用XPath選擇器獲取屬性。咱們用一個實例來感覺一下,以下所示:

>>> response.xpath('//a').css('img').xpath('@src').extract()
['image1_thumb.jpg', 'image2_thumb.jpg', 'image3_thumb.jpg', 'image4_thumb.jpg', 'image5_thumb.jpg']複製代碼

咱們成功獲取了全部img節點的src屬性。

所以,咱們能夠隨意使用xpath()css()方法兩者自由組合實現嵌套查詢,兩者是徹底兼容的。

5. 正則匹配

Scrapy的選擇器還支持正則匹配。好比,在示例的a節點中的文本相似於Name: My image 1,如今咱們只想把Name:後面的內容提取出來,這時就能夠藉助re()方法,實現以下:

>>> response.xpath('//a/text()').re('Name:\s(.*)')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']複製代碼

咱們給re()方法傳了一個正則表達式,其中(.*)就是要匹配的內容,輸出的結果就是正則表達式匹配的分組,結果會依次輸出。

若是同時存在兩個分組,那麼結果依然會被按序輸出,以下所示:

>>> response.xpath('//a/text()').re('(.*?):\s(.*)')
['Name', 'My image 1 ', 'Name', 'My image 2 ', 'Name', 'My image 3 ', 'Name', 'My image 4 ', 'Name', 'My image 5 ']複製代碼

相似extract_first()方法,re_first()方法能夠選取列表的第一個元素,用法以下:

>>> response.xpath('//a/text()').re_first('(.*?):\s(.*)')
'Name'
>>> response.xpath('//a/text()').re_first('Name:\s(.*)')
'My image 1 '複製代碼

不論正則匹配了幾個分組,結果都會等於列表的第一個元素。

值得注意的是,response對象不能直接調用re()re_first()方法。若是想要對全文進行正則匹配,能夠先調用xpath()方法再正則匹配,以下所示:

>>> response.re('Name:\s(.*)')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'HtmlResponse' object has no attribute 're'
>>> response.xpath('.').re('Name:\s(.*)<br>')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']
>>> response.xpath('.').re_first('Name:\s(.*)<br>')
'My image 1 '複製代碼

經過上面的例子,咱們能夠看到,直接調用re()方法會提示沒有re屬性。可是這裏首先調用了xpath('.')選中全文,而後調用re()re_first()方法,就能夠進行正則匹配了。

6. 結語

以上內容即是Scrapy選擇器的用法,它包括兩個經常使用選擇器和正則匹配功能。熟練掌握XPath語法、CSS選擇器語法、正則表達式語法能夠大大提升數據提取效率。


本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓

如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Coder

weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)

相關文章
相關標籤/搜索