Python Scrapy 爬蟲教程之選擇器 Selectors

Selectors

在抓取一個(gè)web頁(yè)面的時(shí)候,大多數(shù)任務(wù)在于從HTML源中提取數(shù)據(jù)猎荠。有很多可用的的庫(kù)支持這些操作,比如:

  • BeautifulSoup:這是一個(gè)在 Python 編程中非常流行的網(wǎng)頁(yè)抓取庫(kù)蜀备,它依照HTML代碼的結(jié)構(gòu)來構(gòu)造 Python 對(duì)象关摇,并且能正確處理不良標(biāo)記,但是它有一個(gè)弱項(xiàng):太慢
  • lxml:以非常 python 化的 ElementTree 接口為基礎(chǔ)碾阁,建立 XML 解析庫(kù)(同時(shí)也能解析 HTML)

Scrapy 有自己的提取數(shù)據(jù)的機(jī)制输虱。它們稱之為 selectors(選擇器),因?yàn)閺?HTML 文檔中篩選特定內(nèi)容脂凶,可以使用XPathCSS表達(dá)式宪睹。

XPath是一個(gè)篩選 XML 文檔節(jié)點(diǎn)的語言,也能用于篩選 HTML 文檔蚕钦。
CSS 是一個(gè)應(yīng)用格式到 HTML 文檔的語言亭病,它定義選擇器與特定 HTML 元素格式之間的關(guān)系。

注意:
Scrapy 選擇器是 `parsel` 庫(kù)的輕量級(jí)再封裝嘶居;封裝的目的是為了與 Scrapy 響應(yīng)對(duì)象提供更好的整合命贴。
`parsel` 是一個(gè)獨(dú)立的網(wǎng)頁(yè)抓取庫(kù),可以不依賴于 Scrapy食听。它使用 `lxml` 庫(kù)作為底層引擎胸蛛,并且在 lxml 頂層接口再實(shí)現(xiàn)了一個(gè)簡(jiǎn)單API。這意味著 Scrapy 選擇器的速度與解析準(zhǔn)確性與 lxml 十分接近樱报。

一葬项、使用選擇器

scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html 為例

1.1 構(gòu)建選擇器

使用 .selector 暴露 Response 對(duì)象的 Selector 實(shí)例:

In [4]: response.selector.xpath('//a/text()').getall()
Out[4]:
['Name: My image 1 ',
 'Name: My image 2 ',
 'Name: My image 3 ',
 'Name: My image 4 ',
 'Name: My image 5 ']

更簡(jiǎn)潔的方式查詢,Scrapy 提供了兩種簡(jiǎn)寫:response.xpath()response.css()

In [5]: response.css('a::text').getall()
In [6]: response.xpath('//a/text()').getall()

Scrapy selectors 是 Selector 類的實(shí)例迹蛤,通過傳輸 TextResponse 對(duì)象或作為 unicode 的補(bǔ)全來構(gòu)成民珍。
通常不需要手動(dòng)構(gòu)造 Scrapy selectors襟士,原因如下:response 對(duì)象可以用于 Spider 回調(diào),所以大部分場(chǎng)景下會(huì)偏向于使用 response.css()response.xpath() 作為簡(jiǎn)寫嚷量。通過使用 response.selectorresponse.xpath() response.css() 可以確保響應(yīng)體只被解析一次陋桂。

todo:這里的 Selector 類 和 Spider 與 response 之間的回調(diào) 問題,以后在研究源碼的時(shí)候著重看看怎么處理的

但是在必要條件下蝶溶,需要直接使用 Selector 嗜历,比如要從如下的內(nèi)容中構(gòu)造對(duì)象:

>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').get()
'good'

Selector 會(huì)自動(dòng)基于輸入類型選擇最好的解析規(guī)則(XML 或 HTML)。

1.2 選擇器

接下來先介紹如何使用Scrapy shell(它提供了可交互的測(cè)試)
使用方法scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html

在 shell 加載完成后抖所,可以通過 response 直接獲取響應(yīng)梨州,通過 response.selector 屬性獲取選擇器

由于該網(wǎng)頁(yè)返回的是 HTML 內(nèi)容,所以選擇器會(huì)自動(dòng)使用 HTML 解析模式田轧。

為title標(biāo)簽內(nèi)的文本構(gòu)造 XPath 訪問方式

In [7]: response.xpath('//title/text()')
Out[7]: [<Selector xpath='//title/text()' data='Example website'>]

可以看到返回的是一個(gè)選擇器對(duì)象列表暴匠,如果想要提取文本內(nèi)容,需要使用選擇器的 .get() 或者 .getall() 方法傻粘,如下所示:

In [8]: response.xpath('//title/text()').get()
Out[8]: 'Example website'

.get() 永遠(yuǎn)只返回一個(gè)結(jié)果每窖;

如果選擇器有多個(gè)匹配,那么只返回匹配的第一個(gè)內(nèi)容弦悉;
如果只有沒有匹配岛请,將返回 None

.getall() 返回一個(gè)結(jié)果列表

注意 CSS 選擇器可以使用 CSS3 的偽元素(pseudoelements)選擇文本內(nèi)容和屬性節(jié)點(diǎn)。

In [9]: response.css('title::text')
Out[9]: [<Selector xpath='descendant-or-self::title/text()' data='Example website'>]

In [10]: response.css('title::text').get()
Out[10]: 'Example website'

可以得知警绩,.xpath().css() 方法都會(huì)返回一個(gè) SelectorList 實(shí)例對(duì)象崇败,該實(shí)例對(duì)象是選擇器的列表。

這個(gè)接口還可以用于快速選擇嵌套的數(shù)據(jù):

In [11]: response.css('img').xpath('@src').getall()
Out[11]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

自定義返回結(jié)果

如果在獲取元素時(shí)肩祥,沒有發(fā)現(xiàn)對(duì)應(yīng)的元素后室,那么將返回 None;但是也可以自定義返回結(jié)果:

In [12]: response.css('ab_post').get('no_element')
Out[12]: 'no_element'

css 獲取屬性值 .attrib

在上面的例子中混狠,獲取 src 屬性使用了 ‘@src’ XPath岸霹,在 CSS 中通過 .attrib 也可以查詢?cè)搶傩浴?/p>

In [13]: [img.attrib['src'] for img in response.css('img')]
Out[13]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

.attrib 作為簡(jiǎn)寫同樣也可以直接獲取 SelectorList, 它返回第一個(gè)被匹配的元素:

In [14]: response.css('img').attrib['src']
Out[14]: 'image1_thumb.jpg'

這種做法在只需要一個(gè)結(jié)果時(shí)非常有用,比如當(dāng)根據(jù)id或頁(yè)面上唯一的元素選擇時(shí):

In [18]: response.css('base').attrib['href']
Out[18]: 'http://example.com/'

綜合用法

獲取 base 標(biāo)簽的 href 屬性內(nèi)容

In [18]: response.css('base').attrib['href']
Out[18]: 'http://example.com/'

In [19]: response.xpath('//base/@href').get()
Out[19]: 'http://example.com/'

In [20]: response.css('base::attr(href)').get()
Out[20]: 'http://example.com/'

In [21]: response.css('base').attrib['href']
Out[21]: 'http://example.com/'

獲取標(biāo)簽的 href 屬性內(nèi)容(限定內(nèi)容包含 “image”)

In [24]: response.xpath('//a[contains(@href, "image")]/@href').getall()
Out[24]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

In [25]: response.css('a[href*=image]::attr(href)').getall()
Out[25]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

獲取標(biāo)簽 href 內(nèi)容包含“image”的子標(biāo)簽 src 屬性值

In [26]: response.xpath('//a[contains(@href, "image")]/img/@src').getall()
Out[26]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

In [27]: response.css('a[href*=image] img::attr(src)').getall()
Out[27]:
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

1.3 擴(kuò)展CSS選擇器

根據(jù) W3C 標(biāo)準(zhǔn)将饺,CSS 選擇器不能提供選擇文本節(jié)點(diǎn)或?qū)傩灾档墓δ堋?br> 但是在網(wǎng)頁(yè)中抓取內(nèi)容是非常必要的贡避,Scrapy 為此實(shí)現(xiàn)了許多非標(biāo)準(zhǔn)的偽元素。

  • 選擇文本節(jié)點(diǎn)予弧,使用 ::text
  • 選擇屬性值刮吧,使用 ::attr(name) 這里的 name 屬性對(duì)應(yīng)的名稱

實(shí)例

  • title::text 篩選 <title> 元素的子節(jié)點(diǎn)文本
  • *::text 篩選當(dāng)前選擇器上下文的所有子文本節(jié)點(diǎn)
  • foo::text 如果 foo 元素存在但是不包含文本,那么返回結(jié)果為空掖蛤。
    • 這意味著 .css('foo::text').get() 即使元素存在也返回 None杀捻,如果需要修改返回值,可以添加 *.get(default='')
  • a::attr(href) 篩選 href 屬性的值

1.4 嵌套選擇器

篩選模式( .xapth().css() )都返回同一類型選擇器的列表蚓庭,所以你可以對(duì)這些篩選列表進(jìn)行調(diào)用致讥,下面是例子:

In [28]: links = response.xpath('//a[contains(@href, "image")]')

In [29]: links.getall()
Out[29]:
['<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>']

In [30]: for index, link in enumerate(links):
    ...:     args = (index, link.xpath('@href').get(), link.xpath('img/@src').get())
    ...:     print('Link numver %d points to url %r and image %r' % args)
    ...:
Link numver 0 points to url 'image1.html' and image 'image1_thumb.jpg'
Link numver 1 points to url 'image2.html' and image 'image2_thumb.jpg'
Link numver 2 points to url 'image3.html' and image 'image3_thumb.jpg'
Link numver 3 points to url 'image4.html' and image 'image4_thumb.jpg'
Link numver 4 points to url 'image5.html' and image 'image5_thumb.jpg'

1.5 選擇元素屬性

有多種方式獲取屬性的值仅仆。

XPath 選擇屬性

首先,可以使用 XPath 語法:

In [31]: response.xpath('//a/@href').getall()
Out[31]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

XPath 語法有多種優(yōu)勢(shì):在標(biāo)準(zhǔn)的 XPath 格式中垢袱,@attributes 可以作為 XPath 表達(dá)式的一部分墓拜,等等,可以通過屬性值來篩選內(nèi)容请契。

CSS 選擇屬性

Scrapy 也提供了 CSS 選擇器的擴(kuò)展(::attr(...))咳榜,它允許這樣獲得屬性值:

In [32]: response.css('a::attr(href)').getall()
Out[32]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

Selector attrib 屬性

除了這種做法,還有 Selector 的 .attrib 屬性姚糊。
如果你更希望使用 Python 代碼,而不是通過 XPath 或者 CSS 擴(kuò)展來獲取屬性授舟,那么這種方式也比較有用:

In [33]: [a.attrib['href'] for a in response.css('a')]
Out[33]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

這種 python 代碼直接讀取的方式也可作用于 SelectorList 上救恨;
將會(huì)將返回字典,包含第一個(gè)匹配元素的屬性和值释树。
如果只需要選擇器返回一個(gè)結(jié)果肠槽,那么這么做很方便:

In [34]: response.css('base').attrib
Out[34]: {'href': 'http://example.com/'}

In [35]: response.css('base').attrib['href']
Out[35]: 'http://example.com/'

1.6 在選擇器中使用正則表達(dá)式

Selector 類還有 .re() 方法用于提取正則匹配的數(shù)據(jù)。
然而奢啥,不同于 .xpath() 和 .css() 方法秸仙,.re() 返回的是一個(gè)字符串列表。
所以 .re() 不能做內(nèi)嵌的 .re() 調(diào)用桩盲。

提取圖片名字的例子

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

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

二寂纪、XPaths 的使用

在 Scrapy 中高效使用 XPath 的一些建議

擴(kuò)展鏈接: http://www.zvon.org/comp/r/tut-XPath_1.html
擴(kuò)展鏈接2:https://blog.scrapinghub.com/2014/07/17/xpath-tips-from-the-web-scraping-trenches/

2.1 使用 XPath 相對(duì)路徑

假設(shè)你在嵌套選擇器中使用 XPath,如果路徑以 '/' 開頭赌结,那么 Xpath 會(huì)指向文檔的絕對(duì)位置捞蛋,而不是你現(xiàn)在調(diào)用的選擇器。

比如柬姚,如果想提取 <div>元素 中的所有 <a> 元素拟杉。

首先,獲取 <div> 元素量承,然后遍歷 dvi 內(nèi)容時(shí)使用 '//a' 定位元素

In [38]: divs = response.xpath('//div')

In [39]: for p in divs.xpath('//a'):
    ...:     print(p.get())
    ...:

結(jié)果返回為空搬设。

實(shí)際上,第39行代碼中的路徑撕捍,意味著重新以文檔起點(diǎn)為基準(zhǔn)構(gòu)建path拿穴,而不是在 <div> 元素中構(gòu)建。

可以做這樣的修改:

In [56]: for p in divs.xpath('.//a'):
    ...:     print(p)
    ...:
<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 <'>

更常規(guī)的使用方法忧风,直接使用 a 即可:

In [56]: for p in divs.xpath('a'):
    ...:     print(p)
    ...:

2.2 當(dāng)通過類 class 查詢時(shí)贞言,盡量考慮 CSS

由于一個(gè)元素可以包含多個(gè) CSS classes,通過 XPath 鎖定 class 的方式去選擇元素會(huì)冗長(zhǎng)無比阀蒂。

你需要這樣操作 contains(@class, 'someclass') 來彌補(bǔ)多個(gè)類的情況该窗。

事實(shí)證明弟蚀,Scrapy 選擇器允許你使用「鏈選擇器」,所以大多數(shù)情況下可以采用:先使用 CSS 篩選class酗失,然后再切換到 XPath 繼續(xù)篩選义钉。

>>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">')
>>> sel.css('.shout').xpath('./time/@datetime').get()
'2014-07-23 19:00'

這種靈活使用的「鏈?zhǔn)胶Y選」非常實(shí)用,別忘了在使用 .xpath() 的時(shí)候规肴,路徑添加 '.'捶闸。

2.3 區(qū)別 //node[1] 和 (//node)[1]

//node[1] 返回的是所有父節(jié)點(diǎn)下,第一次出現(xiàn)的節(jié)點(diǎn)
(//node)[1] 返回所有文檔匹配節(jié)點(diǎn)的第一個(gè)拖刃,只返回一個(gè)值

2.4 在條件中使用文本節(jié)點(diǎn) ??

獲取文本內(nèi)容最好選擇 XPath https://www.w3.org/TR/xpath/all/#section-String-Functions删壮,而不要使用 .//text() 而選擇 '.' 。

這是因?yàn)?.//text() 表達(dá)式會(huì)生成一個(gè)節(jié)點(diǎn)集合兑牡,當(dāng) 節(jié)點(diǎn)集合(node-set) 轉(zhuǎn)換成字符串央碟,這個(gè)過程經(jīng)常發(fā)生傳遞參數(shù)到字符串函數(shù)中,如 conatins() 或 starts_with() 函數(shù)均函,這會(huì)導(dǎo)致只會(huì)處理第一個(gè)元素亿虽。

>>> sel = Selector(text='<a href="#">Click here to go to the <strong>Next Page</strong>')
>>> sel.xpath('//a//text()').getall()
['Click here to go to the ', 'Next Page']
>>> sel.xpath("string(//a[1]//text())").getall() # convert it to string
['Click here to go to the ']

這里只返回了列表中的第一個(gè)元素

處理方式:將整個(gè)節(jié)點(diǎn)轉(zhuǎn)換為字符串,這樣就會(huì)把字內(nèi)容都加在一起

>>> sel.xpath('//a[1]').getall()
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']
>>> sel.xpath('string(//a[1])').getall()
['Click here to go to the Next Page']

從子節(jié)點(diǎn)中匹配字符串

如:

>>> sel.xpath("http://a[contains(.//text(), 'Next Page')]").getall()
[]

使用 '.' 苞也,情況變得不同

>>> sel.xpath("http://a[contains(., 'Next Page')]").getall()
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']

2.5 XPath 中的表達(dá)式

XPath 允許你在 XPath 表達(dá)式中引用變量洛勉,語法:$somevariable。
這有點(diǎn)類似于參數(shù)查詢或 SQL 中的查詢占位符 '?'如迟,意味著你可以預(yù)制查詢點(diǎn)收毫,再設(shè)置值。

舉例一:不通過硬編碼的方式殷勘,通過 id 屬性來匹配元素

In [66]: response.xpath('//div[@id=$val]/a/text()', val='images').get()
Out[66]: 'Name: My image 1 '

舉例二:找到包含五個(gè)<a>子標(biāo)簽的<div>標(biāo)簽的id值

In [69]: response.xpath('//div[count(a)=$cnt]/@id', cnt=5).get()
Out[69]: 'images'

在調(diào)用 .xpath() 的時(shí)候牛哺,必須綁定引用變量的值,否值會(huì)拋出 'ValueError: XPath error' 錯(cuò)誤劳吠。

如果想了解更多引润,這部分內(nèi)容是以 parsel 庫(kù)為基礎(chǔ),更多詳情和例子在 parse 教程中有介紹

2.6 移除域名

在使用爬蟲項(xiàng)目時(shí)痒玩,經(jīng)常會(huì)使用到「清除域名淳附,只處理元素名」的操作,從而獲得更簡(jiǎn)單/方便的 XPath蠢古。
你可以使用 Selector.remove_namespaces() 方法來移除域名奴曙。

首先,找到一個(gè)博客網(wǎng)站用于實(shí)踐

(base) ?  cherry  scrapy shell https://feeds.feedburner.com/PythonInsider

如果網(wǎng)頁(yè)設(shè)定了命名空間草讶,一旦我們想要嘗試獲取某些標(biāo)簽對(duì)象洽糟,會(huì)全部失敗,返回None,因?yàn)?Atom XML 會(huì)混淆這些節(jié)點(diǎn)標(biāo)簽坤溃。

但是如果先調(diào)用了 Selector.remove_namespaces() 方法拍霜,那么就可以直接獲取這些名稱。

如果你在好奇為什么域名空間移除操作為什么不默認(rèn)執(zhí)行薪介,而是采取手動(dòng)的方式祠饺。簡(jiǎn)單來說有兩個(gè)原因:

  1. 移除域名空間需要遍歷和修改文檔中所有節(jié)點(diǎn),這意味著在Scrapy對(duì)所有文檔執(zhí)行默認(rèn)抓取操作過程中汁政,不可避免的產(chǎn)生高昂的操作量道偷。
  2. 預(yù)防在某些情況下不需要使用域名空間,盡管這個(gè)操作很少見记劈。

2.7 使用 EXSLT 擴(kuò)展 ??

由于基于 lxml 頂層再封裝勺鸦,所以 Scrapy 也支持一些 EXSLT 擴(kuò)展。
它可以預(yù)注冊(cè)域名空間從而使用 XPath 表達(dá)式目木。

預(yù)置 域名 使用方式
re http://exslt.org/regular-expression http://exslt.org/regexp/index.html
set http://exsl.org/sets http://exslt.org/set/index.html

2.7.1 正則表達(dá)式

在 test() 方法中换途,可以提供許多 XPath 沒法提供的方法

doc = u"""
<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html">third item</a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
</div>
"""
>>> sel = Selector(text=doc, type='html')
>>> sel.xpath('//li//@href').getall()
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
>>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').getall()
['link1.html', 'link2.html', 'link4.html', 'link5.html']

這里的 li[re:test(@class, "item-\d$')] 使用了正則表達(dá)式,這前面的 re() 處理節(jié)點(diǎn)文本不同嘶窄,是處理屬性值的正則表達(dá)式怀跛,而這個(gè)功能是 XPath 提供的 contains() 和 start-with() 進(jìn)階版本距贷,它可以實(shí)現(xiàn)更強(qiáng)的匹配功能柄冲。

注意 ??
C 語言庫(kù)的 libxslt 不支持本地 EXSLT 正則表達(dá)式,所以 lxml 的功能實(shí)現(xiàn)與 Python 的 re 掛鉤忠蝗。盡管如此现横,在 XPath 中使用正則表達(dá)式函數(shù)會(huì)對(duì)性能有影響。

2.7.2 set 操作

set 操作可以在抓取文本前很方便地排除部分文檔樹內(nèi)容阁最。

比如從某個(gè)網(wǎng)站提取微型結(jié)構(gòu)數(shù)據(jù):網(wǎng)址(http://schema.org/Product)戒祠。只提取 timescopes 和與 itemprops 有關(guān)的組。

props = scop.xpath('''set:difference(./descendant::*/@itemprop, .//*[@itemscope]/*/@itemprop)''')

2.8 其他的 XPath 擴(kuò)展

has-class 擴(kuò)展

<p class="foo bar-baz">First</p>
<p class="foo">Second</p>
<p class="bar">Third</p>
<p>Fourth</p>

可以這么使用

>>> response.xpath('//p[has-class("foo")]')
[<Selector xpath='//p[has-class("foo")]' data='<p class="foo bar-baz">First</p>'>,
 <Selector xpath='//p[has-class("foo")]' data='<p class="foo">Second</p>'>]
>>> response.xpath('//p[has-class("foo", "bar-baz")]')
[<Selector xpath='//p[has-class("foo", "bar-baz")]' data='<p class="foo bar-baz">First</p>'>]
>>> response.xpath('//p[has-class("foo", "bar")]')
[]

所以 XPath 路徑 '//p[has-class("foo", "bar-baz")]' 大致等同于 CSS 路徑 'p.foo.bar-baz'速种。

但是記住姜盈,XPath 的方式會(huì)更慢,因?yàn)樗羌?Python 函數(shù)配阵,這種方式僅適用于 CSS 選擇器無法正常描述的場(chǎng)景馏颂。

parsel 的簡(jiǎn)單擴(kuò)展

parsel.xpathfuncs.set_xpathfunc(fname, func)

三、引用內(nèi)建的選擇器

3.1 Selector 選擇器對(duì)象

class scrapy.selector.Selector(response=None, text=None, type=None, root=None, **kwargs)

Selector 對(duì)象是選擇部分響應(yīng)內(nèi)容的包裝棋傍。

response

是 HtmlResponse 或 XMLResponse 對(duì)象救拉,用于選擇和抓取數(shù)據(jù)

text

uincode 字符串或utf-8編碼的文本,在 response 不可用的情況下替代瘫拣。不會(huì)發(fā)生使用 text 或 reponse 亿絮。

type

定義了選擇器的類型,可以為 html/xml/None(默認(rèn))

  1. 如果 type 為 None,那么選擇器基于響應(yīng)派昧,會(huì)選擇最好的類型解析內(nèi)容黔姜;如果內(nèi)容為文本,則默認(rèn)為 html斗锭。
  2. 如果 type 為 None地淀,且傳入 response,那么選擇器的類型將會(huì)按照下面類型推斷:
    • HtmlResponse 對(duì)應(yīng)類型為 html
    • XmlResponse 對(duì)應(yīng)選擇器類型為 xml
    • 否則選擇器類型為 html
  3. 如果 type 設(shè)定了類型岖是,則不會(huì)對(duì)內(nèi)容格式進(jìn)行檢測(cè)帮毁,強(qiáng)制使用設(shè)置的類型。

方法一 xpath

xpath(query, namespace=None, **kwargs)

尋找匹配 xpath 查詢的節(jié)點(diǎn)豺撑,返回 SelectorList 選擇器列表實(shí)例烈疚,扁平化所有的元素。
元素列表也實(shí)現(xiàn)了 Selector 的接口聪轿。

  • query

XPath 查詢體爷肝,字符串

  • namaspace
    可選項(xiàng)

通過 register_namespace(prefix, uri) 注冊(cè)后的內(nèi)容,可以通過 prefis: namespace-uri 的形式使用陆错,不過僅限于當(dāng)前 Selector 調(diào)用灯抛。
TODO:這種方式的使用,有一點(diǎn)了解音瓷,但是不是很透徹对嚼,可待來日解決

任何新增的參數(shù)名稱都可以通過 XPath 表達(dá)式傳輸給 XPath 變量

為方便起見,這個(gè)方法可以被 response.xpath() 調(diào)用

方法二 css

css(query)

應(yīng)用給定的 CSS 選擇器绳慎,返回 SelectorList 實(shí)例

  • query

CSS 選擇器纵竖,字符串格式

在后臺(tái),CSS 查詢會(huì)被解析成 XPath杏愤,并執(zhí)行 .xpath() 方法

方法三 get

get()

加載并返回匹配的節(jié)點(diǎn)

attrib

返回基礎(chǔ)元素的屬性字典

re

re(regex, replace_entities=True)

應(yīng)用給定的正則表達(dá)式靡砌,返回匹配的 unicode 字符串列表

  • regex
    可以是編譯的正則表達(dá)式,字符串會(huì)通過 re.compile(regex) 編譯珊楼。

默認(rèn)情況下通殃,如 ‘&' 會(huì)被直接解析成對(duì)應(yīng)的內(nèi)容;replace_entities 為 False 則不會(huì)做這種改變厕宗。

re_first

re_first(regex, default=None, replace_entities=True)

應(yīng)用給定的正則表達(dá)式画舌,返回第一個(gè)被匹配的 unicode 字符串。
如果沒有匹配媳瞪,返回默認(rèn)的值(如果默認(rèn)值沒有提供骗炉,則默認(rèn)為None)

另一個(gè)可選參數(shù),如上

  • register_namespace(prefix, uri)
    注冊(cè)給定的域名空間蛇受,在當(dāng)前 Selector 中應(yīng)用句葵。
    沒有注冊(cè)域名空間,你無法從非標(biāo)準(zhǔn)的命名空間中選擇或提取數(shù)據(jù),具體查看下文 Selector examples on XML response

remove_namespaces()

移除所有的域名乍丈,允許直接跨過文檔獲取無域名空間的路徑剂碴。

_bool_()

如果沒有任何內(nèi)容,返回 True轻专,否則返回 False忆矛。
換句話說,選擇器是否有內(nèi)容

getall()

加載并返回匹配的所有節(jié)點(diǎn)请垛,以 unicode 字符串列表展示催训。

3.2 選擇器列表對(duì)象 SelectorList objects

class scrap.selector.SelectorList

SelectorList 對(duì)象是 list 子類,提供一些額外的方法

xpath(xpath, namespaces=None, **kwargs)

每個(gè)元素都調(diào)用 .xpath() 方法宗收,并扁平化返回另一個(gè) SelectorList 對(duì)象

  • query
  • namespace

css(query)

為每個(gè)元素調(diào)用 .css() 方法

  • query

getall()

為每個(gè)元素調(diào)用 .get() 方法

get(default=None)

返回第一個(gè)元素調(diào)用 .get() 的結(jié)果漫拭。
如果列表為空,則返回默認(rèn)的結(jié)果

re(regex, replace_entities=True)

為每個(gè)元素調(diào)用 .re() 方法混稽,扁平化返回列表

re_first(regex, default=None, replace_entities=True)

attrib

四采驻、例子

選擇 XML 響應(yīng)

抓取網(wǎng)址:https://support.google.com/merchants/answer/160589?hl=en&ref_topic=2473799

提取所有的價(jià)格,需要注冊(cè)一個(gè)域名空間:

sel = Selector(xml_reponse)

# 注冊(cè)域名空間
sel.regisgter_namespace("g", "http://base.google.com/ns/1.0")
sel.xpath("http://g:price").getall()

[1] https://docs.scrapy.org/en/latest/topics/selectors.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匈勋,一起剝皮案震驚了整個(gè)濱河市礼旅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洽洁,老刑警劉巖痘系,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異诡挂,居然都是意外死亡碎浇,警方通過查閱死者的電腦和手機(jī)临谱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門璃俗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悉默,你說我怎么就攤上這事城豁。” “怎么了抄课?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵唱星,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我跟磨,道長(zhǎng)间聊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任抵拘,我火速辦了婚禮哎榴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己尚蝌,他們只是感情好迎变,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著飘言,像睡著了一般衣形。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姿鸿,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天谆吴,我揣著相機(jī)與錄音,去河邊找鬼苛预。 笑死纪铺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碟渺。 我是一名探鬼主播鲜锚,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼苫拍!你這毒婦竟也來了芜繁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤绒极,失蹤者是張志新(化名)和其女友劉穎骏令,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垄提,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榔袋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铡俐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凰兑。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖审丘,靈堂內(nèi)的尸體忽然破棺而出吏够,到底是詐尸還是另有隱情,我是刑警寧澤滩报,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布锅知,位于F島的核電站,受9級(jí)特大地震影響脓钾,放射性物質(zhì)發(fā)生泄漏售睹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一可训、第九天 我趴在偏房一處隱蔽的房頂上張望昌妹。 院中可真熱鬧生真,春花似錦、人聲如沸捺宗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚜厉。三九已至长已,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昼牛,已是汗流浹背术瓮。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贰健,地道東北人胞四。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伶椿,于是被迫代替她去往敵國(guó)和親辜伟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354