在前面的章節中,我們介紹了如何爬取單個網頁和多個網頁,所提取頁面標簽內容基本都是使用find方法,
在本章節,我們將對訪問頁面標簽、屬性和值進行一個系統的介紹,使用的url是https://www.ppzuowen.com/book/antushengtonghua/7403.html
1.使用絕對路徑訪問標簽
在前面我們已經介紹過,HTML頁面本質上是一棵DOM樹,形狀結構如下圖所示:

使用絕對路徑訪問,就是從HTML頁面的根結點html開始,按照順序訪問對應的標簽位置,比如,我們需要訪問上圖中的h1標簽,那么我們設定的絕對訪問路徑就為html.body.div.h1,
根據上面的介紹,我們來做一個實際的練習,

比如我們要訪問目標頁面中的h1標簽,那么我們需要設定的訪問路徑就是html.body.div.div.h1,獲取標簽的文本內容使用get_text方法,獲取屬性的值使用xxTag.attrs[xx],
我們可以使用urllib包或requests包來抓包,
urllib抓包代碼實作如下:
# 請求庫
from urllib.request import urlopen
import requests
# 決議庫
from bs4 import BeautifulSoup
# 爬取的網頁鏈接
url=r"https://www.ppzuowen.com/book/antushengtonghua/7403.html"
html=urlopen(url)
html.encoding=None
bs=BeautifulSoup(html.read(),'html.parser')
# print(bs.html.title.get_text())
print('標簽:',bs.html.div.div.h1)
print('class屬性:',bs.html.div.div.h1.attrs['class'])
print('標簽值:',bs.html.div.div.h1.get_text())
requests抓包代碼實作如下:
# 請求庫
from urllib.request import urlopen
import requests
# 決議庫
from bs4 import BeautifulSoup
# 爬取的網頁鏈接
url=r"https://www.ppzuowen.com/book/antushengtonghua/7403.html"
html=requests.get(url)
html.encoding=None
bs=BeautifulSoup(html.text,'html.parser')
# print(bs.html.title.get_text())
print('標簽:',bs.html.div.div.h1)
print('class屬性:',bs.html.div.div.h1.attrs['class'])
print('標簽值:',bs.html.div.div.h1.get_text())
得到的結果如下:

雖然在實際應用中使用絕對路徑訪問任意標簽都是可以做到的,但是萬一碰到的頁面非常復雜,那么通過這種絕對路徑訪問標簽就顯得非常笨重與麻煩,影響作業效率,這是一般會采用相對路徑的方式進行訪問,
2.相對路徑訪問
2.1 find() 和 findAll() 訪問
在相對路徑訪問中,BeautifulSoup 里的 find() 和 findAll() 可能是你最常用的兩個函式,
借助它們,你可以通過標簽的不同屬性輕松地過濾 HTML 頁面,查找需要的標簽組或單個標簽,這兩個函式非常相似,BeautifulSoup 檔案里兩者的定義就是這樣:
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
- 標簽引數 tag:可以傳一個標簽的名稱或多個標簽名稱組成的 Python串列做標簽引數,例如,下面的代碼將回傳一個包含 HTML 檔案中所有標題標簽的串列:
.findAll({"h1","h2","h3","h4","h5","h6"}) - 屬性引數 attributes: 是用一個 Python 字典封裝一個標簽的若干屬性和對應的屬性值,例如,下面這個函式會回傳 HTML 檔案里紅色與綠色兩種顏色的 span 標簽:
.findAll("span", {"class":{"green", "red"}}) - 遞回引數 recursive: 是一個布爾變數,你想抓取 HTML 檔案標簽結構里多少層的資訊?如果recursive 設定為 True,findAll 就會根據你的要求去查找標簽引數的所有子標簽,以及子標簽的子標簽,如果 recursive 設定為 False,findAll 就只查找檔案的一級標簽,findAll默認是支持遞回查找的(recursive 默認值是 True);一般情況下這個引數不需要設定,除非你真正了解自己需要哪些資訊,而且抓取速度非常重要,那時你可以設定遞回引數,
- 文本引數 text :它是用標簽的文本內容去匹配,而不是用標簽的屬性,假如想查找前面網頁中包含“丑小鴨”內容的標簽數量,可以把之前的 findAll 方法換成下面的代碼:
name=bs.findAll(text='丑小鴨')
print(len(name))
- 范圍限制引數 limit,顯然只用于 findAll 方法,find 其實等價于 findAll 的 limit 等于1 時的情形,如果你只對網頁中獲取的前 n 項結果感興趣,就可以設定它,但是要注意,這個引數設定之后,獲得的前幾項結果是按照網頁上的順序排序的,未必是你想要的那前幾項,
關鍵詞引數 keyword:可以讓你選擇那些具有指定屬性的標簽,例如:
bs.findAll(href="https://www.cnblogs.com/book/")
雖然關鍵詞引數 keyword 在一些場景中很有用,但是,它是 BeautifulSoup 在技術上做的一個冗余功能,任何用關鍵詞引數能夠完成的任務,同樣可以用其他技術解決,
例如,下面兩行代碼是完全一樣的:
bs.findAll(href="https://www.cnblogs.com/book/")
bs.findAll("", {"href":"/book/"})
另外,用 keyword 偶爾會出現問題,尤其是在用 class 屬性查找標簽的時候,因為 class 是 Python 中受保護的關鍵字,不過,你可以在 class 后面增加一個下劃線解決:
bs.findAll(class_="subNav")
2.2 Tag訪問
除了通過find() 和 findAll() 方法查找,還可以通過標簽查找,正如前面反復強調的,HTML本質是一棵DOM樹,因此可以通過某個tag去訪問它的父節點、兄弟節點和子節點,
- 處理子標簽和其他后代標簽
在 BeautifulSoup 庫里,孩子(child)和后代(descendant)有顯著的不同:子標簽就是一個父標簽的下一級,而后代標簽是指一個父標簽下面所有級別的標簽,
例如,tr 標簽是 table標簽的子標簽,而 tr、th、td標簽都是 table 標簽的后代標簽,所有的子標簽都是后代標簽,但不是所有的后代標簽都是子標簽,
一般情況下,BeautifulSoup 函式總是處理當前標簽的后代標簽,
bs.p
如果你只想找出子標簽,可以用 .children 標簽,例如:
ls=bs.find('ul',{'class':'globeNav'}).children
for child in ls:
print(child)
- 處理兄弟標簽
next_sibling 和 previous_sibling 函式可以用來回傳當前節點的后一個弟節點和前一個兄節點,與 next_siblings 和 previous_siblings的作用類似,只是它們回傳的是一組標簽,而不是單個標簽,例如:
brother=bs.find('div',{'class':'nav mt10'}).div.previous_siblings
for bro in brother:
print('兄節點:',bro)
brother=bs.find('div',{'class':'nav mt10'}).ul.next_siblings
for bro in brother:
print('弟節點:',bro)
- 父標簽處理
BeautifulSoup 的父標簽查找函式,parent 和 parents,例如:
parents=bs.find('div',{'class':'nav mt10'}).parents
for parent in parents:
print('父節點:',parent)
2.3 通過CSS選擇器獲取Tag物件
- 通過標簽名查找:
bs.select('a') #回傳所有的a標簽 list - 通過類名查找:
bs.select('.sister') #回傳所有包含的標簽 list
bs.select('a.sister')
- 通過 id 名查找:
bs.select('#link1') #回傳所有包含 id="link1"的標簽 list
bs.select("p#link1")
- 組合查找:
bs.select('p #link1') #查找所有 p 標簽中,id 等于 link1的標簽 注意區分soup.select('p#link1')
bs.select("html head title") or bs.select("body a") #逐層查找
bs.select("p > a") #直接子標簽查找 注意是子標簽不是子孫標簽
- 通過是否存在某個屬性和屬性的值來查找:
bs.select('a[href]')
bs.select('a[href="http://example.com/elsie"]')
bs.select('a[href^="Example Domain"]') # ^= 開頭
bs.select('a[href$="tillie"]') # $= 結尾
bs.select('a[href*=".com/el"]') # *= 包含
除此之外,有時查找的物件只需要滿足某一類條件即可,這時可以結合正則運算式進行模糊查找,
關于正則運算式的撰寫,可以參照前面python基礎部分的內容,這里不再贅述,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/116873.html
標籤:Python
