python爬蟲:使用BeautifulSoup進行查找


 網頁中有用的信息通常存在於網頁中的文本或各種不同標簽的屬性值,為了獲得這些網頁信息,有必要有一些查找方法可以獲取這些文本值或標簽屬性。而Beautiful Soup中內置了一些查找方式:

  •            find()
  •            find_all()
  •            find_parent()
  •            find_parents()
  •            find_next_sibling()
  •            find_next_siblings()
  •            find_previous_sibling()
  •            find_previous_siblings()
  •            find_previous()
  •            find_all_previous()
  •            find_next()
  •            find_all_next()

        使用find()查找

以下這段HTML是例程要用到的參考網頁

  1. <html>  
  2.     <body>  
  3.         <div class="ecopyramid">  
  4.         <ul id="producers">  
  5.             <li class="producerlist">  
  6.                 <div class="name">plants</div>  
  7.                 <div class="number">100000</div>  
  8.             </li>  
  9.             <li class="producerlist">  
  10.                 <div class="name">algae</div>  
  11.                 <div class="number">100000</div>  
  12.             </li>  
  13.         </ul>  
  14.         <ul id="primaryconsumers">  
  15.             <li class="primaryconsumerlist">  
  16.                 <div class="name">deer</div>  
  17.                 <div class="number">1000</div>  
  18.             </li>  
  19.             <li class="primaryconsumerlist">  
  20.                 <div class="name">rabbit</div>  
  21.                 <div class="number">2000</div>  
  22.             </li>  
  23.         <ul>  
  24.         <ul id="secondaryconsumers">  
  25.             <li class="secondaryconsumerlist">  
  26.                 <div class="name">fox</div>  
  27.                 <div class="number">100</div>  
  28.             </li>  
  29.             <li class="secondaryconsumerlist">  
  30.                 <div class="name">bear</div>  
  31.                 <div class="number">100</div>  
  32.             </li>  
  33.         </ul>  
  34.         <ul id="tertiaryconsumers">  
  35.             <li class="tertiaryconsumerlist">  
  36.                 <div class="name">lion</div>  
  37.                 <div class="number">80</div>  
  38.             </li>  
  39.             <li class="tertiaryconsumerlist">  
  40.                 <div class="name">tiger</div>  
  41.                 <div class="number">50</div>  
  42.             </li>  
  43.         </ul>  
  44.     </body>  
  45. </html>  


以上代碼是一個生態金字塔的簡單展示,為了找到其中的第一生產者,第一消費者或第二消費者,我們可以使用Beautiful Soup的查找方法。一般來說,為了找到BeautifulSoup對象內任何第一個標簽入口,我們可以使用find()方法。

找到第一生產者

可以明顯看到,生產者在第一個<ul>標簽里,因為生產者是在整個HTML文檔中第一個<ul>標簽中出現,所以可以簡單的使用find()方法找到第一生產者。下圖HTML樹代表了第一個生產者所在位置。

然后在ecologicalpyramid.py中寫入下面一段代碼,使用ecologicalpyramid.html文件創建BeautifulSoup對象。

  1. from bs4 import BeautifulSoup  
  2. with open("ecologicalpyramid.html","r") as ecological_pyramid:  
  3. soup = BeautifulSoup(ecological_pyramid)  
  4. producer_entries = soup.find("ul")  
  5. print(producer_entries.li.div.string)  


 輸出得到:plants


find()說明

find()函數如下:

find(name,attrs,recursive,text,**wargs)

這些參數相當於過濾器一樣可以進行篩選處理。

不同的參數過濾可以應用到以下情況:

  • 查找標簽,基於name參數
  • 查找文本,基於text參數
  • 基於正則表達式的查找
  • 查找標簽的屬性,基於attrs參數
  • 基於函數的查找

 通過標簽查找

我們可以傳遞任何標簽的名字來查找到它第一次出現的地方。找到后,find函數返回一個BeautifulSoup的標簽對象。

  1. from bs4 import BeautifulSoup  
  2.   
  3. with open("ecologicalpyramid.html", "r") as ecological_pyramid:  
  4.     soup = BeautifulSoup(ecological_pyramid,"html")  
  5. producer_entries = soup.find("ul")  
  6. print(type(producer_entries))  


輸出的得到 <class 'bs4.element.Tag'>

通過文本查找

直接字符串的話,查找的是標簽。如果想要查找文本的話,則需要用到text參數。如下所示:

  1. from bs4 import BeautifulSoup  
  2.   
  3. with open("ecologicalpyramid.html", "r") as ecological_pyramid:  
  4.     soup = BeautifulSoup(ecological_pyramid,"html")  
  5. plants_string = soup.find(text="plants")  
  6. print(plants_string)  


輸出:plants

通過正則表達式查找

有以下HTML代碼:

  1. <br/>  
  2. <div>The below HTML has the information that has email ids.</div>   
  3. abc@example.com  
  4. <div>xyz@example.com</div>  
  5. <span>foo@example.com</span>  


如果想找出第一個郵箱地址,但是由於第一個郵箱地址沒有標簽包含,所以通過其他方式很難找到。但是我們可以把郵箱地址進行正則表達式處理,這樣就容易多了。

參考如下代碼:

  1. import re  
  2. from bs4 import BeautifulSoup  
  3.   
  4. email_id_example = """<br/> 
  5. <div>The below HTML has the information that has email ids.</div>  
  6. abc@example.com 
  7. <div>xyz@example.com</div> 
  8. <span>foo@example.com</span> 
  9. """  
  10.   
  11. soup = BeautifulSoup(email_id_example)  
  12. emailid_regexp = re.compile("\w+@\w+\.\w+")  
  13. first_email_id = soup.find(text=emailid_regexp)  
  14. print(first_email_id)  


輸出:abc@example.com

通過標簽屬性進行查找

觀看例程HTML代碼,其中第一消費者在ul標簽里面且id屬性為priaryconsumers.

因為第一消費者出現的ul不是文檔中第一個ul,所以通過前面查找標簽的辦法就行不通了。現在通過標簽屬性進行查找,參考代碼如下:

  1. from bs4 import BeautifulSoup  
  2.   
  3. with open("ecologicalpyramid.html", "r") as ecological_pyramid:  
  4.     soup = BeautifulSoup(ecological_pyramid,"html")  
  5. primary_consumer = soup.find(id="primaryconsumers")  
  6. print(primary_consumer.li.div.string)  


輸出:deer

通過標簽屬性查找的方式適用於大多數標簽屬性,包括id,style,title,但是有一組標簽屬性例外。

  • Custom attrbutes
  • Class
此時,我們需要借助attrs參數來進行傳遞。

基於定制屬性的查找


比如我們HTML5標簽中的data-custom屬性,如果我們這樣
  1. customattr = ""'<p data-custom="custom">custom attribute   
  2. example</p>"""  
  3. customsoup = BeautifulSoup(customattr,'lxml')  
  4. customSoup.find(data-custom="custom")  

那么則會報錯。原因是在Python中變量不能呢含有-這個字符,而我們傳遞的data-custom有-這個字符。
解決的辦法是在attrs屬性用字典進行傳遞參數。
  1. using_attrs = customsoup.find(attrs={'data-custom':'custom'})  
  2. print(using_attrs)  

基於CSS類的查找


因為class是Python的保留關鍵字,所以無法使用class這個關鍵字。所以解決辦法類似上面。
  1. css_class = soup.find(attrs={'class':'primaryconsumerlist'})  
  2. print(css_class)  

還有另一個辦法。BeautifulSoup有一個特別的關鍵字參數class_。示例:
方法1:
  1. css_class = soup.find(class_ = "primaryconsumers" )   

方法2:
  1. css_class = soup.find(attrs={'class':'primaryconsumers'})  

基於定義的函數進行查找

可以傳遞函數到find()來基於函數定義的條件進行查找。函數值必須返回true或者false。
例子:
  1. def is_secondary_consumers(tag):  
  2. return tag.has_attr('id') and tag.get('id') ==   
  3. 'secondaryconsumers'  
  1. secondary_consumer = soup.find(is_secondary_consumers)  
  2. print(secondary_consumer.li.div.string)  

輸出:fox

把方法進行組合后進行查找


可以用其中任何方法進行組合來進行查找,比如同時基於標簽名和id號。

使用find_all查找

find()用來查找第一個匹配結果出現的地方,而find_all()正如名字所示,將會找到所有匹配結果出現的地方。應用到find()中的不同過濾參數同理可以用到find_all()中,實際上,過濾參數可以用於任何查找函數,如find_parents()或和find_siblings()。

查找所有三級消費者

  1. all_tertiaryconsumers =   
  2. soup.find_all(class_="tertiaryconsumerslist")  

其all_tertiaryconsumers的類型是列表。
所以我們對其列表進行迭代,循環輸出三級消費者的名字。
  1. for tertiaryconsumer in all_tertiaryconsumers:  
  2. print(tertiaryconsumer.div.string)  

輸出:
lion
tiger

理解用於find_all()的參數


相比find(),find_all()有個額外的參數limit,如下所示:
find_all(name,attrs,recursive,text,limit,**kwargs)
limit參數可以限制我們想要得到結果的數目。參照前面的郵件地址例子,我們可以得到所有右鍵地址通過:
  1. email_ids = soup.find_all(text=emailid_regexp)  
  2. print(email_ids)  

輸出:[u'abc@example.com',u'xyz@example.com',u'foo@example.com']

當我們使用limit參數,效果如下:
  1. email_ids_limited = soup.find_all(text=emailid_regexp,limit=2)  
  2. print(email_ids_limited)  

限制得到兩個結果,所以輸出為:
[u'abc@example.com',u'xyz@example.com']

說白了,find()也就是當limit=1時的find_all()。

可以向find函數傳遞True或False參數,如果我們傳遞True給find_all(),則返回所有soup對象的標簽。對於find()來說,則返回第一個標簽。
舉例查找文本,傳遞True將會返回所有文本。
  1. all_texts = soup.find_all(text=True)  
  2. print(all_texts)  

輸出:
[u'\n', u'\n', u'\n', u'\n', u'\n', u'plants', u'\n', u'100000', 
u'\n', u'\n', u'\n', u'algae', u'\n', u'100000', u'\n', u'\n', 
u'\n', u'\n', u'\n', u'deer', u'\n', u'1000', u'\n', u'\n', 
u'\n', u'rabbit', u'\n', u'2000', u'\n', u'\n', u'\n', 
u'\n', u'\n', u'fox', u'\n', u'100', u'\n', u'\n', u'\n', 
u'bear', u'\n', u'100', u'\n', u'\n', u'\n', u'\n', 
u'\n', u'lion', u'\n', u'80', u'\n', u'\n', u'\n', 
u'tiger', u'\n', u'50', u'\n', u'\n', u'\n', u'\n', 
u'\n'] 

同樣的,我們可以在傳遞text參數時傳遞一個字符串列表,那么find_all()會找到誒個在列表中定義過的字符串。
  1. all_texts_in_list = soup.find_all(text=["plants","algae"])  
  2. print(all_texts_in_list)  

輸出:
  1. [u'plants', u'algae']  

這個同樣適用於查找標簽,標簽屬性,定制屬性和CSS類。如:
  1. div_li_tags = soup.find_all(["div","li"])  

find()和find_all()都會查找一個對象所有后輩們,不過我們可以控制它通過recursive參數。如果recursive=False,那么超找只會找到該對象的最近后代。

通過標簽之間的關系進行查找

我們可以通過find()和find_all()來查找到想要內容。但有時候,我們需要查看的與之內容相關先前的標簽或者后面的標簽來獲取額外的信息。比如方法find_parents()和find_next_siblings()等等。一般的,在find()和find_all()方法后使用上述方法,因為find()和find_all()可以找到特殊的一個標簽,然后我們可以通過這個特殊的標簽找到其他的想要的與之有關系的標簽。

查找父標簽

通過find_parents()或find_parent()。它們之間的不同就類似於find()和find_all()的區別。find_parents()返回全部的相匹配的父標簽,而find_paret()返回最近的一個父標簽。適用於find()的方法同樣也使用於這兩個方法。

在前面的第一消費者例子中,我們可以找到離Primaryconsumer最近的ul父標簽。
  1. primaryconsumers = soup.find_all(class_="primaryconsumerlist")  
  2. primaryconsumer = primaryconsumers[0]  
  3. parent_ul = primaryconsumer.find_parents('ul')  
  4. print(parent_ul)  

一個簡單的找到一個標簽的父標簽的方法就是使用find_parent()卻不帶任何參數。
  1. immediateprimary_consumer_parent = primary_consumer.find_parent()  

查找同胞


如果標簽在同一個等級的話,我們可以說這些標簽是同胞的關系,比如參照上面金字塔例子,所有ul標簽就是同胞的關系。


上圖ul標簽下的producers,primaryconsumers,secondaryconsumers,teriaryconsumers就是同胞關系。
再看下面這個圖:


div下的plants和algae不是同胞關系,但是plants和臨近的number是同胞關系。
Beautiful Soup自帶有查找同胞的方法。
比如find_next_siblings()和find_next_sibling()查找對象下面的同胞。舉例:
  1. producers= soup.find(id='producers')  
  2. next_siblings = producers.find_next_siblings()  
  3. print(next_siblings)  

將會輸出與之臨近的下面的所有同胞HTML代碼。

查找下一個

對每一個標簽來說,下一個元素可能會是定位字符串,標簽對象或者其他BeautifulSoup對象。我們定義下一個元素為與當前元素最靠近的元素。這個不同於同胞定義。我們有方法可以找到我們想要標簽的下一個其他元素對象。find_all_next()找到與當前元素靠近的所有對象。而find_next()找到離當前元素最接近的對象。

比如,找到在第一個div標簽后的所有li標簽
  1. first_div = soup.div  
  2. all_li_tags = first_div.find_all_next("li")  

輸出“:
[<li class="producerlist">
<div class="name">plants</div>
<div class="number">100000</div>
</li>, <li class="producerlist">
<div class="name">algae</div>
<div class="number">100000</div>
</li>, <li class="primaryconsumerlist">
<div class="name">deer</div>
<div class="number">1000</div>
</li>, <li class="primaryconsumerlist">
<div class="name">rabbit</div>
<div class="number">2000</div>
</li>, <li class="secondaryconsumerlist">
<div class="name">fox</div>
<div class="number">100</div>
</li>, <li class="secondaryconsumerlist">
<div class="name">bear</div>
<div class="number">100</div>
</li>, <li class="tertiaryconsumerlist">
<div class="name">lion</div>
<div class="number">80</div>
</li>, <li class="tertiaryconsumerlist">
<div class="name">tiger</div>
<div class="number">50</div>
</li>]

查找上一個

與查找下一個相反的是查找前一個。同理用於find_all_previous()和find_all_previous()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM