Scrapy提供了本身的數據提取方法,即Selector(選擇器)。Selector是基於lxml來構建的,支持XPath選擇器、CSS選擇器以及正則表達式,功能全面,解析速度和準確度很是高。
css
本節將介紹Selector的用法。html
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的用法。網絡
因爲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,而後把一些可操做的變量傳遞給咱們,如request
、response
等,以下圖所示。
咱們能夠在命令行模式下輸入命令調用對象的一些操做方法,回車以後實時顯示結果。這與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>複製代碼
進入Scrapy Shell以後,咱們將主要操做response
這個變量來進行解析。由於咱們解析的是HTML代碼,Selector將自動使用HTML語法來分析。
response
有一個屬性selector
,咱們調用response.selector
返回的內容就至關於用response
的body
構造了一個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()
。方便起見,後面咱們統一直接調用response
的xpath()
和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的相關用法,包括嵌套查詢、提取內容、提取單個內容、獲取文本和屬性等。
接下來,咱們看看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()
方法兩者自由組合實現嵌套查詢,兩者是徹底兼容的。
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()
方法,就能夠進行正則匹配了。
以上內容即是Scrapy選擇器的用法,它包括兩個經常使用選擇器和正則匹配功能。熟練掌握XPath語法、CSS選擇器語法、正則表達式語法能夠大大提升數據提取效率。
本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓
如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Coder
weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)