1 一、獲取BeautifulSoup文檔的對象 2 1.對象 3 Beautiful Soup將復雜HTML文檔轉換成一個復雜的樹形結構,每個節點都是Python對象, 4 所有對象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment . 5 6 Comment 對象是一個特殊類型的 NavigableString 對象,其輸出的內容不包括注釋符號。 7 8 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> 9 #a 標簽里的內容實際上是注釋,但是如果我們利用 .string 來輸出它的內容時,注釋符號已經去掉了。 10 type(soup.a.string) --> <class 'bs4.element.Comment'> 11 12 13 可以傳入一段字符串或一個文件句柄. 14 from bs4 import BeautifulSoup 15 soup = BeautifulSoup(open("index.html")) or soup = BeautifulSoup("<html>data</html>") 16 2.編碼 17 使用Beautiful Soup解析后,文檔都被轉換成了Unicode 18 Beautiful Soup用了 編碼自動檢測 子庫來識別當前文檔編碼並轉換成Unicode編碼. 19 BeautifulSoup 對象的 .original_encoding 屬性記錄了自動識別編碼的結果 20 21 #通過傳入 from_encoding 參數來指定編碼方式 22 soup = BeautifulSoup(markup, from_encoding="iso-8859-8") 23 24 #節點可通過encode() 方法來指定輸出編碼 25 soup.p.encode("utf-8") 26 27 #通過Beautiful Soup輸出文檔時,不管輸入文檔是什么編碼方式,輸出編碼均為UTF-8編碼 28 print(soup.prettify("latin-1")) #如果不想用UTF-8編碼輸出,可以將編碼方式傳入 prettify() 方法 29 prettify() #增加換行符,html格式打印
二、Tag對象屬性 name attrs 返回一個字典 例如:{'class':'sister'} #可以操作字段 def __getitem__(self, key): return self.attrs[key] def has_attr(self, key): key in self.attrs contents 注意 空格換行等 都包含在contents內 def __len__(self): return len(self.contents) def __contains__(self, x): return x in self.contents def __iter__(self): return iter(self.contents) string child = self.contents[0] if isinstance(child, NavigableString): #如果tag.contents[0] 為 NavigableString 類型子節點,直接得到子節點 return child return child.string #如果不是 則獲取子標簽的string屬性 children == iter(self.contents) next_element 和 previous_elemen 如同字面意思,前一個ele和后一個ele next_elements 和 previous_elements 當前Tag前面和后面ele的迭代器 descendants 迭代器,從contents[0] 然后.next_element直到.next_sibling.previous_element strings 和 stripped_strings 迭代器 for descendant in self.descendants: strings --> 遍歷descendants屬性,獲取所有NavigableString對象 stripped_strings --> 遍歷descendants屬性,獲取所有len(descendant.strip()) != 0 的 NavigableString對象 parent 和 parents parent:元素的父節點 parents:迭代器一直獲取.parent屬性 next_sibling 和 previous_sibling 查詢兄弟節點 實際文檔中的tag的 .next_sibling 和 .previous_sibling 屬性通常是字符串或空白 next_siblings 和 previous_siblings 屬性可以對當前節點的兄弟節點迭代輸出
三、通過API獲取Tag對象 一個Tag可能包含多個字符串或其它的Tag,這些都是這個Tag的子節點. 1.根據標簽查找(type:bs4_obj) tag_p = soup.p #返回HTML文檔第一個Tag p標簽 2.通過 find 和 find_all find(name=None, attrs={}, recursive=True, text=None, **kwargs) #內部調用find_all方法返回第一個Tag find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs) #recursive=True 默認會檢索當前tag的所有子孫節點,參數 recursive=False 只檢索子節點 #limit參數 限制返回數量 #名字為 name 的tag,如果name不是內置的參數名,搜索時會按tag的屬性來搜索注意class的寫法 soup.find_all("title") or soup.find_all(class_ = 'tb') or soup.find_all(id='link2') soup.find_all("a", class_="sister") or soup.find_all("a", attrs={"class": "sister"}) #class_ 參數同樣接受不同類型的 過濾器 ,字符串,正則表達式,方法或 True soup.find_all(class_=re.compile("itl")) def has_six_characters(css_class): return css_class is not None and len(css_class) == 6 soup.find_all(class_=has_six_characters) # 過濾其class屬性長度為6的標簽 # text 參數可以搜搜文檔中的字符串內容,接受 字符串 , 正則表達式 , 列表, True . soup.find_all(text=re.compile("Dormouse")) soup.find_all("a", text="Elsie") #包含“Elsie”的<a>標簽 soup.find_all(text="Elsie") #返回一個NavigableString對象 #實際上就是對 .children 屬性的迭代搜索 3.通過soup() 內部調用find_all方法 def __call__(self, *args, **kwargs): return self.find_all(*args, **kwargs) 4.通過find_parents() 和 find_parent() find_parent(self, name=None, attrs={}, **kwargs) #搜索當前節點的第一個父節點 find_parents(self, name=None, attrs={}, limit=None, **kwargs) #搜索當前節點的所有父節點,父父節點等 #實際上就是對 .parents 屬性的迭代搜索 5.通過find_previous_siblings() 和 find_previous_sibling(): #一個與多個(list) find_previous_siblings(self, name=None, attrs={}, text=None, limit=None, **kwargs) #實際上就是對 .previous_siblings 屬性的迭代搜索 6.通過find_all_next() 和 find_next(): find_all_next(self, name=None, attrs={}, text=None, limit=None, **kwargs) #實際上就是對 .next_elements 屬性的迭代搜索 7.通過find_all_previous() 和 find_previous(): find_all_previous(self, name=None, attrs={}, text=None, limit=None, **kwargs) #實際上就是對 .previous_elements 屬性的迭代搜索
四、通過CSS選擇器獲取Tag對象 (1)通過標簽名查找: soup.select('a') #返回所有的a標簽 list (2)通過類名查找: soup.select('.sister') #返回所有包含class="sister"的標簽 list soup.select('a.sister') (3)通過 id 名查找: soup.select('#link1') #返回所有包含 id="link1"的標簽 list soup.select("p#link1") (4)組合查找: soup.select('p #link1') #查找所有 p 標簽中,id 等於 link1的標簽 注意區分soup.select('p#link1') soup.select("html head title") or soup.select("body a") #逐層查找 soup.select("p > a") #直接子標簽查找 注意是子標簽不是子孫標簽 (5)通過是否存在某個屬性和屬性的值來查找: soup.select('a[href]') soup.select('a[href="http://example.com/elsie"]') soup.select('a[href^="http://example.com/"]') # ^= 開頭 soup.select('a[href$="tillie"]') # $= 結尾 soup.select('a[href*=".com/el"]') # *= 包含
五、解析部分文檔 將整片文檔進行解析,實在是浪費內存和時間.最快的方法是從一開始就把想要獲取以外的東西都忽略掉. SoupStrainer 類可以定義文檔的某段內容,這樣搜索文檔時就不必先解析整篇文檔。 三種SoupStrainer 對象: from bs4 import SoupStrainer only_a_tags = SoupStrainer("a") #把<a>標簽以外的東西都忽略掉 only_tags_with_id_link2 = SoupStrainer(id="link2") #把屬性id="link2"標簽以外的東西都忽略掉 def is_short_string(string): return len(string) < 10 only_short_strings = SoupStrainer(text=is_short_string) #把文本內容長度大於等於10的標簽都忽略掉 將 SoupStrainer 對象作為 parse_only 參數給 BeautifulSoup 的構造方法即可 print(BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags).prettify())