1. 程式人生 > >爬蟲知識3:seletors選擇器、Xpath、 BeautifulSoup使用案例

爬蟲知識3:seletors選擇器、Xpath、 BeautifulSoup使用案例

   本文主要介紹了Scrapy常用的資料提取的方法,包括seletors、scrapy shell、xpath、css、BeautifulSoup的使用方法及案例。只要掌握xpath或者css任意一種即可。

1、seletors選擇器

1)介紹

        Scrapy提取資料有自己的一套機制。它們被稱作選擇器(seletors),構建於lxml庫之上,通過特定的XPath或者CSS表示式來等“選擇” HTML檔案中的某個部分。Selector有四個基本的方法,最常用的還是xpath:

  • XPath(query):傳入Xpath表示式queery,返回該表示式對應的所有節點的selector list。Xpath是一門在 XML 文件中查詢資訊的語言。XPath 可用來在 XML 文件中對元素和屬性進行遍歷。

  • css(query):傳入css表示式query,返回該表示式所對應的所有節點的selector list列表CSS是將HTML文件樣式化的語言。選擇器由它定義,並與特定的HTML元素的樣式相關聯。

  • re(): 根據傳入的正則表示式對資料進行提取,返回Unicode字串list列表

  • extract(): 序列化該節點為Unicode字串並返回list

2)匯入seletors

from scrapy.selector import Selector, HtmlXPathSelector

from scrapy.http import HtmlResponse

3)常用xpath使用方法:

常用語法:

  • /:根節點

  • //xxx/zzz:路徑

  • //div:去全域性的子孫中找所有的div元素

  • .//表示去當前物件的子孫中找

  • /xxx//div:去兒子物件中找xxx節點下所有的div元素

  • *:匹配任意節點元素

  • /html/body/div[1]:選取body下的第一個div節點

  • //div[@class="xxx"]:選取class屬性為xxx的div節點

  • //@attr:獲取attr對應的值

常用函式:

  • text():提取文字資訊,//*[@class='xxx']/text()

  • position():選取第幾個節點,//*[position()=1]

  • last():選取最後一個節點,//*[last()]

  • starts-with(@attr,substr):attr屬性值開頭substr的節點

  • contains(@attr,substr):attr屬性值是否包含substr

運算子:

  • |:計算兩個節點集合,比如//div|//li

  • 比較運算子=,!=,<=...,比如//price[text()>10]

  • 算數運算子:+,-,*,div,比如//price[text()+1]

  • 邏輯運算子:or,and,[@class='xxxx' or @class='yyyy']

4)xpath語法案例:以取a標籤為案例,div、span等都相同

  • xpath('//a'):找到全域性中所有的a標籤

  • xpath('//a/text()  '):獲取a標籤的文字值

  • xpath('//a/span'):a標籤物件兒子下的span標籤

  • xpath('//a[2]'):a標籤找到後,返回的是一個列表,[n]列表的n位置的值

  • xpath('//a[@id]'):找全域性中屬性為id的a標籤

  • xpath('//a[@id="i1"]'):找全域性中屬性id為i1的a標籤

  • xpath('//a/@id'):取a標籤的id的屬性值

  • xpath('//a[@href="link.html"][@id="i1"]'):兩個[]代表雙條件,需要href="link.html“,且id="i1"的a標籤

  • xpath('//a[contains(@href, "link")]'):a標籤的href需要包含"link"字元

  • xpath('//a[starts-with(@href, "link")]'):a標籤的href需要以"link"開頭

  • xpath('//a[re:test(@id, "i\d+")]'):正則表示式的寫法,需要有re:test

  • xpath('//a[re:test(@id, "i\d+")]/@href').extract(),正則表示式的寫法案例

  • xpath(.//)在當前基礎上往下找,需要加“.”,比如用在for迴圈中

  • obj.extract()#列表中的每一個物件轉化字串==>返回一個列表

  • obj.extract_first() #列表中的每一個物件轉化字元==>列表中的第一個元素

  • xpath('/html/body/ul/li/a/@href').extract():一層層去找標籤

5)獲取程式碼,需要在頁面,右鍵-----檢查元素---去看下html結構。

也可以直接選擇自己想要的內容,通過copy---copy xpath直接獲得原始碼,去貼上即可。

比如選取下面頁面的肖申克救贖的資訊:但一般這種路徑都會特別長。

//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a/span[1]

640?wx_fmt=png

6)css語法

基本語法:

  • .classvalue:選擇class屬性為classvalue的元素

  • #idvalue:選擇id值為idvalue的元素

  • p:選擇所有的p元素

  • div,p:選擇所有的div和p元素

  • div~p:前面有div元素的每個p元素,div和p為兄弟節點

  • li span :選擇li內部所有的span元素

  • div>p:選擇父結點為div的所有p元素

  • ::text/::attr(src):文字資訊/提取某個屬性的值,比如a::attr(href),提取超連結

屬性的語法:

  • [attr]選擇所有帶attr屬性的元素

  • [attr=val]選擇attr值為val的所有元素:[numvalue=num1]

  • [attr^=val]選擇attr屬性值以val開頭的元素:[src^=http]

  • [attr$=val]選擇attr屬性值以val結尾的元素:[src^=.jpg]

  • [attr*=val]選擇attr屬性值包含val的元素:[src*=douban]

ele語法

  • ele:first-of-type:所有子元素為ele的第一個元素

  • ele:first-child:第一個元素為ele的元素

  • ele:only-of-type:型別為ele的元素

  • ele:only-child:唯一子元素的每個ele元素

  • ele:nth-of-type(n):選擇第n個ele元素

  • ele:nth-child(n):第n個子元素為ele的元素

  • ele:last-child:最後一個為ele的子元素

2、Scrapy shell互動,測試網頁程式碼

    Scrapy終端是一個互動終端shell,我們可以在未啟動spider的情況下嘗試及除錯程式碼,也可以用來測試XPath或CSS表示式,檢視他們的工作方式,方便我們爬取的網頁中提取的資料。一旦你習慣使用了Scrapy shell,你將會發現Scrapy shell對於開發爬蟲是非常好用的一個測試工具。

1)啟動Scrapy shell,有三種方式請求url

1-1直接請求url:

    首先需要cmd-----進入專案根目錄,然後填寫"scrapy shell +url(你需要訪問的頁面地址) ",如下所示,即可返回資訊。

640?wx_fmt=png

會看到如下的資訊

640?wx_fmt=png

比如輸入:response.xpath('//title')可以看到如下資訊:

640?wx_fmt=png

    Scrapy Shell根據下載的頁面會自動建立一些方便使用的物件,例如Response物件,以及Selector物件(對HTML及XML內容)。當shell載入後,將得到一個包含response資料的本地 response 變數。

  • 輸入response.body將輸出response的包體。

  • 輸入response.headers可以看到response的包頭。

  • 輸入response.selector時,將獲取到一個response初始化的類Selector的物件,此時可以通過使用response.selector.xpath()或response.selector.css()來對response進行查詢。

  • Scrapy也提供了一些快捷方式, 例如 response.xpath()或response.css()同樣可以生效。

1-2請求url設定請求資訊,使用:scrapy shell -s name=value語句

比如直接設定代理USER_AGENT的語句:

640?wx_fmt=png

1-3 在命令列直接請求url,利用fetch。

req=Request(url,header={})

r=fetch(url)

640?wx_fmt=png

然後就可以看到response.url的資訊了。

2)測試xpath的使用語法:以豆瓣TOP250為例。

  • 比如選取html:response.xpath('/html')

640?wx_fmt=png

  • 提取網頁的title:response.xpath('/html/head/title')

640?wx_fmt=png

  • 找到所有的a標籤,或找到body下所有的a標籤:response.xpath('//body//a')

640?wx_fmt=png

640?wx_fmt=png

  • 找到body下所有的標籤

640?wx_fmt=png

  • text()獲取電影名稱:response.xpath('//div[@class="hd"]//a/span[1]/text()').extract()640?wx_fmt=png

640?wx_fmt=png

  • 獲取所有的電影的連線:response.xpath('//div[@class="hd"]//a/@href').extract()

640?wx_fmt=png

  • 提取所有的電影評分:response.xpath('//span[@class="rating_num"]/text()').extract()

640?wx_fmt=png

  • 提取所有的電影評分,且限制評分。response.xpath('//span[@class="rating_num" and text()>9.4]/text()').extract()

640?wx_fmt=png

  • 提取頁碼:response.xpath('//span[@class="next"]//a/@href').extract()

640?wx_fmt=png

3)測試CSS的使用案例:以豆瓣TOP250為例。

  • 比如選取html:response.css('html')

640?wx_fmt=png

  • 提取網頁的title:response.css('html head title')

640?wx_fmt=png

  • 獲取body下所有的a :response.css('body a')

640?wx_fmt=png

  • text()獲取所有的電影名稱:response.css('.title::text')

640?wx_fmt=png

根據文件結構,可以寫成如下格式:response.css('.title::text')

640?wx_fmt=png

  • 也可以使用ele語法獲取電影名稱:response.css("[class='hd'] a span:first-of-type").extract()

640?wx_fmt=png

  • 獲取所有的電影的連線:response.css('.hd a::attr(href)').extract()

640?wx_fmt=png

  • 提取所有的電影的評分:response.css('.rating_num::text').extract()

640?wx_fmt=png

  • 提取圖片:response.css("[src$=jpg]").extract()

640?wx_fmt=png

附錄::

附1、BeautifulSoup

    BeautifulSoup是一個模組,該模組用於接收一個HTML或XML字串,然後將其進行格式化,之後就可以使用他提供的方法進行快速查詢指定元素,從而使得在HTML或XML中查詢指定元素變得簡單。

四大物件種類

Beautiful Soup將複雜HTML文件轉換成一個複雜的樹形結構,每個節點都是Python物件,所有物件可以歸納為4種:

  •     Tag:HTML 中的一個個標籤,比如a,div等,有兩個屬性name和attrs

  •     NavigableString:標籤內部的文字,可以用 .string獲得

  •     BeautifulSoup:表示的是一個文件的全部內容

  •     Comment:註釋

1)模組的安裝(需要cmd,進入到python的工作目錄)

    pip3 install beautifulsoup4

2)匯入模組

    from bs4 import BeautifulSoup

3)建立物件

soup=BeautifulSoup(response.text,'html.parser')

4)使用語法:

  • soup.find('a'):獲取匹配的第一個a標籤,a可以改成span、div等

  • soup.find_all('a'):獲取所有匹配的a標籤,是列表形式

  • soup.find_all(id='xx'):查詢屬性id為xx的所有節點

  • soup.find_all(div,class_='xx'):查詢class屬性為xx的所有div節點

  • soup.find_all(a,text='xx'):查詢text值為xx的所有a節點

  • soup.find_all(a,text=re.complite('xx')):正則查詢text值為xx的所有a節點

  • soup.find('a').has_attr('id'),其中has_attr,檢查標籤是否具有該屬性

  • soup.find('body').children:找到所有子標籤

  • soup.find('body').descendants:找到所有的子子孫孫標籤

  • soup.find('body').clear(),將標籤的所有子標籤全部清空(保留標籤名)

  • soup.find('body').decompose():decompose,遞迴的刪除所有的標籤

  • soup.find('body')..extract():解析標籤

  • soup.find('body').decode(),soup.find('body').decode_contents(),decode,轉換為字串(含當前標籤);decode_contents(不含當前標籤)

  • soup.find_all('a',limit=1),只拿第一個a標籤

  • soup.find_all(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')限制屬性;等同於soup.find(name='a', class_='sister', recursive=True, text='Lacie');recursive=True代表查詢所有的節點,recursive=False代表只查詢本節點下的東西

  • soup.find_all(name=['a','div']),限制選取全部的a、div標籤

  • soup.find_all(href=['link1','link2']),選取href為link11、link2的標籤

  • soup.find_all(text=['Tillie']),text為Tillie的所有節點

  • soup.find_all(class_=['sister0', 'sister']),限制class=sister0、sister

  • 正則表示式rep = re.compile('sister.*') ,v = soup.find_all(class_=rep)

  • soup.find('a').get_text,其中get_text獲取標籤內部文字內容 

  • soup.find('br').is_empty_element,其中is_empty_element,是否是空標籤(是否可以是空)或者自閉合標籤,判斷是否是如下標籤:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame'。

  • soup.strings,獲取多個標籤內容

  • list(soup.stripped_strings),可以去除多餘空白內容

  • append在當前標籤內部追加一個標籤

5)使用soup.select()通過css進行篩選:

可參考css部分知識,僅舉部分案例

  • soup.select('a'):獲取匹配的a標籤

  • soup.select('div a'):按層級獲取div下的a標籤

  • soup.select('.css1'):查詢屬性值為css1的標籤

  • soup.select('#id1'):查詢id屬性值為#id1的標籤

  • soup.select('div[class]'):查詢存在某個屬性值的標籤

  • soup.select('div>p'):選擇父結點為div的所有p元素

  • soup.select('div~p'):前面有div元素的每個p元素,div和p為兄弟節點

  • soup.select('attr^=val'):屬性attr以val開頭的標籤

具體可參考:

http://www.cnblogs.com/wupeiqi/articles/6283017.html

5)使用案例:

from bs4 import BeautifulSoup

html_doc = """

<html><head><title>The Dormouse's story</title></head>
<body>
asdf
   <div class="title">
       <b>The Dormouse's story總共</b>
       <h1>f</h1>
   </div>

<div class="story">Once upon a time there were three little sisters; and their names were
   <a class="sister0" id="link1">Els<span>f</span>ie</a>,
   <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
   <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</div>
ad<br/>sf
<p class="story">...</p>
</body>
</html>

"""
#封裝使用案例:
soup = BeautifulSoup(html_doc, features="lxml")
tag1 = soup.find(name='a')
print('找到第一個a標籤',tag1)
tag2 = soup.find_all(name='a')
print('找到所有的a標籤',tag2)
tag3 = soup.select('#link2')
print('找到id=link2的標籤',tag3)

執行效果:

640?wx_fmt=png

附2、XPath

    XPath 是一門在 XML 文件中查詢資訊的語言。XPath用於在 XML文件中通過元素和屬性進行導航。

1、節點介紹:

    在 XPath 中,有七種型別的節點:元素、屬性、文字、名稱空間、處理指令、註釋以及文件節點(或稱為根節點)。

如下例:

640?wx_fmt=png

上面的XML文件中的節點例子:

<bookstore> (文件節點) 

<author>J K. Rowling</author> (元素節點) 

lang="en" (屬性節點)

2、節點關係:

  • 父(Parent):每個元素以及屬性都有一個父。

在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:

  • 子(Children):元素節點可有零個、一個或多個子。

在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:

  • 同胞(Sibling):擁有相同的父的節點

在下面的例子中,title、author、year 以及 price 元素都是同胞:

  • 先輩(Ancestor):某節點的父、父的父,等等。

在下面的例子中,title 元素的先輩是 book 元素和 bookstore 元素:

  • 後代(Descendant):某個節點的子,子的子,等等。

在下面的例子中,bookstore 的後代是 book、title、author、year 以及 price 元素:

3、XPath 語法

1)常用表示式:640?wx_fmt=png

640?wx_fmt=png

以下例看使用案例:

640?wx_fmt=png

  • xpath(‘bookstore’) 選取 bookstore元素的所有子節點。

  • xpath(‘/bookstore’),選取根元素

  • xpath(‘bookstore/book’),選取屬於 bookstore 的子元素的所有 book 元素。

  • xpath(‘//book’),全域性中找到所有的book元素,而不管它們在文件中的位置。 

  • xpath(‘bookstore//book’) 選擇屬於bookstore元素的後代的所有 book 元素,而不管它們位於bookstore之下的什麼位置。 

  • xpath(‘//@lang’)  選取名為 lang 的所有屬性。  

  • xpath(‘ /bookstore/*’) 選取 bookstore 元素的所有子元素。 

  • xpath(‘ //*’) 選取文件中的所有元素。 

  • xpath(‘ //title[@*]’) 選取所有帶有屬性的 title 元素。 

通過在路徑表示式中使用“|”運算子,您可以選取若干個路徑。

  • //book/title | //book/price 選取 book 元素的所有 title 和 price 元素。 

  • //title | //price 選取文件中的所有 title 和 price 元素。 

  • /bookstore/book/title | //price 選取屬於 bookstore元素的book元素的所有title元素,以及文件中所有的price元素。 

2)謂語

    謂語用來查詢某個特定的節點或者包含某個指定的值的節點。謂語被嵌在方括號中。

比如以下用法:

640?wx_fmt=png