導入所需包
from bs4 import BeautifulSoup
soup = BeautifulSoup(html)
解析順序
'lxml'->'html5lib'->'html.parser'
對象種類
有四種類型:Tag,NavigableString,BeautifulSoup,Comment。
BeautifulSoup將文檔轉化為樹形結構,每個節點都是上述四種類型的Python對象。
遍歷文檔樹
BeautifulSoup對象作為一棵樹,有多個節點。對于一個節點,相對于它所在的位置,有子節點、父節點、兄弟節點。
1. 子節點
* 一個Tag可包含多個Tag以及字符串,這些都是這個Tag的子節點。而NavigableString不會有子節點。
* 如果想要獲得某個Tag:`soup.tag_name`
* 通過點取屬性,只能獲得當前名字的第一個tag,若要獲取所有,需要使用搜索文檔樹中的方法:`soup.find_all('tag_name')`
* tag的.contents屬性可將所有子節點以列表的方式輸出。可通過tag的.children生成器,對所有子節點遍歷。
* `.contents`和`.children`只對獲取Tag的直接子節點,`.descendants`可對Tag的所有子孫節點遍歷。
* 如果tag只有一個NavigableString類型子節點,則可用`.string`獲取。如果包含多個,使用`.strings`遍歷。若輸出的字符串中包含空格或空行,使用`.stripped_strings`去除。
```python
res = soup.stripped_strings
print(list(res))
```
2. 父節點
.parent
-> 當前節點的父節點
.parents
-> 當前節點的所有父輩節點
3. 兄弟節點
擁有同一父節點的節點之間:
.next_sibling
.previous_sibling
所有兄弟節點:
`.next_siblings`
`.previous_siblings`
指向下一個或上一個解析對象:
`.next_element`
`.previous_element`
`.next_elements`
`.previous_elements`
搜索文檔樹:find(str)和find_all(str)
其中的str,代表了tag的name。可以是純字符串、正則表達式、列表(任一匹配就滿足條件,是或運算)、True(返回所有Tag節點不返回字符串節點)。
另一種入參不是str,而是method。此方法是一個函數,只接受一個元素入參,若此函數返回True表示入參匹配要求。例如:
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
綜上,過濾器包括:純字符串、正則表達式、列表、True、方法這幾種。
1. find_all(name,attrs,recursive,text,**kwargs)
該方法搜索當前節點的所有tag子節點。
-
name
參數:
指的是tag的name屬性,字符串對象自動忽略。
過濾器可以使用全部種類。 -
keyword
參數:
如果一個入參指定了名字,但是并不是上述提到的入參名字,搜索時會把該入參當做是tag的屬性來搜索。
例如:soup.find_all(id='link2')
會返回tag中存在屬性id,并且id對應的值是link2的tag。
以上方法可使用除方法之外的所有過濾器。
某些特殊屬性不能這樣直接使用,則使用如下方法:
soup.find_all(attrs={"key":"value"})
例如要使用class屬性進行搜索,由于class是python中的保留字,不能直接寫成入參,目前有兩種方法:
soup.find_all('tag.name',class_='class_value')
soup.find_all('tag.name',attrs={'class':'class_value'})
class_方法可以使用全部過濾器。
另外,因為class是一個多值屬性,所以只需要匹配一個值,就可以得到結果,所謂的不完全匹配。
使用完全匹配時,過濾器中的字符順序需要和實際相符合才能得到對應結果。 -
text
參數:
搜索的是Tag中的字符串內容,可使用全部過濾器。 -
limit
參數:
限制返回數量。 -
recursive
參數:
find_all()默認是搜索當前節點的所有子孫節點,若只需要搜索直接的子節點,則設置recursive=False。
find_all()是實際當中用的最廣泛的。因此有了等價的簡化版:
soup.find_all('a')
或soup('a')
2. find(name,attrs,recursive,text,**kwargs)
find()方法等價于find_all(limit=1),返回符合條件的第一個對象。
區別在于,前者直接返回結果,后者返回只有一個元素的列表。若沒有對象符合條件,前者返回None,后者返回空列表。
簡化版:soup.find('head').find('title')
或soup.head.title
除了find()和find_all()之外還有一些搜索的方法:
find_parent()
find_next_sibling()
find_previous_sibling()
上面三種可以在后面加's'表示所有。
find_next()
find_previous()
find_all_next()
find_all_previous()
3. CSS選擇器(.select()方法)
Tag或BeautifulSoup對象的.select()方法。
res = soup.select('#wrapperto')
-> tag's id
res = soup.select('img[src]')
-> 'img' tags有'src' attributes
res = soup.select('img[src=...]')
-> 'src' attributes是...
輸出
-
soup.prettify()
將文檔樹格式化之后輸出。 - 若不注重格式,則可使用python的
str()
或unicode()
。 - 如果想得到tag中包含的文本內容,使用
get_text()
,可獲取到當前節點的文本,以及子孫節點中的文本。返回的是Unicode。 - 可以指定參數設置分隔符如
get_text("|")
是以“|”作為分隔符。 -
get_text(strip=True)
可去除文本前后的空白。 - 或者用
.stripped_strings
進行遍歷。獲得父級標簽下的所有子標簽內的文本信息,相當于處理多個文本的高級的get_text()方法
編碼
1.soup使用Unicode編碼。
2.BeautifulSoup對象的.original_encoding屬性來獲取自動識別編碼的結果。
3.在創建BeautifulSoup對象時,指定入參from_encoding
來告知文檔的編碼方式。
4.有時轉碼時有些特殊字符替換成了特殊的Unicode,可通過BeautifulSoup對象的.contains_repalcement_characters
屬性來判斷是否有此情況,為True即為有特殊替換。
5.輸出編碼統一為UTF8,若想要其他的編碼,則和一般的python字符串相同,需要進行手動設置。
完整實例代碼
import requests,urllib.request
from bs4 import BeautifulSoup
source_code = requests.get(url,headers=header)
wb_data = source_code.text
soup = BeautifulSoup(wb_data,'lxml')
titles = soup.select('body > div.main-content > ul > li > div.article-info > h3 > a')
images = soup.select('body > div.main-content > ul > li > img')
#獲取網頁信息
for title,image in zip(titles,images):
data = {
'title':title.get_text(),
'image':image.get('src')
}
print(data)
#下載
download_links = []
folder_path = "C:/Users/asus-pc/Desktop"
for pic_tag in soup.find_all('img'):
pic_link = pic_tag.get('src')
download_links.append(pic_link)
for item in download_links:
urllib.request.urlretrieve(item,folder_path+item[-5:])
print("done!")
使用示例
capurl = "https://pixabay.com/"
headers = {"Host":"pixabay.com","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",}
session = requests.Session()
res = session.get(capurl,headers=headers)
# print(res.text)
soup = BeautifulSoup(res.text,'lxml')
imgs = soup.find_all("div",class_="item")
for i in range(len(imgs)):
print(imgs[i].attrs["style"])