Beautiful Soup是一個能夠從HTML或XML文件中提取數據的Python庫。能將即將要進行解析的源碼加載到bs對象,調用bs對象中相關的方法或屬性進行源碼中的相關標籤的定位,並獲取定位到的標籤之間存在的文本或者屬性值。html
它可以經過你喜歡的轉換器實現慣用的文檔導航、查找、修改文檔的方式。Beautiful Soup會幫你節省數小時甚至數天的工做時間。python
pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com正則表達式
from bs4 import BeautifulSoup soup = BeautifulSoup("<html>A Html Text</html>", "html.parser")
兩個參數:第一個參數是要解析的html文本,第二個參數是使用那種解析器,對於HTML來說就是html.parser,這個是bs4自帶的解析器。express
若是一段HTML或XML文檔格式不正確的話,那麼在不一樣的解析器中返回的結果多是不同的。url
格式化輸出:spa
soup.prettify()
Beautfiful Soup將複雜HTML文檔轉換成一個複雜的樹形結構,每一個節點都是Python對象,全部對象能夠概括爲4種:tag,NavigableString,BeautifulSoup,Comment。code
Tag對象與xml或html原生文檔中的tag相同。orm
from bs4 import BeautifulSoup soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','xml') tag = soup.b print(type(tag))# <class 'bs4.element.Tag'>
若是不存在,則返回 None,若是存在多個,則返回第一個。xml
每一個tag都有本身的名字。htm
tag = soup.b print(tag.name)# b
tag的屬性是一個字典。
tag = soup.b print(tag['class'])# boldest print(tag.attrs)# {'class': 'boldest'} print(type(tag.attrs))# <class 'dict'>
多值屬性,最多見的多值屬性是class,多值屬性的返回list。
soup = BeautifulSoup('<p class="body strikeout"></p>','xml')
print(soup.p['class']) # ['body', 'strikeout'] print(soup.p.attrs) # {'class': ['body', 'strikeout']}
若是某個屬性看起來好像有多個值,但在任何版本的HTML定義中都沒有被定義爲多值屬性,那麼Beautiful Soup會將這個屬性做爲字符串返回。
soup = BeautifulSoup('<p id="my id"></p>', 'html.parser') print(soup.p['id']) # 'my id'
text屬性返回tag的全部字符串連成的字符串。
字符串常被包含在tag內,BeautifulSoup用NavigableString類來包裝tag中的字符串。可是字符串中不能包含其餘tag。
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','xml') s = soup.b.string print(s) # Extremely bold print(type(s)) # <class 'bs4.element.NavigableString'>
BeautifulSoup 對象表示的是一個文檔的所有內容。大部分時候,能夠把它看成Tag對象。可是BeautifulSoup對象並非真正的HTML或XML的 tag,它沒有attribute屬性,name屬性是一個值爲「[document]」的特殊屬性。
Comment通常表示文檔的註釋部分。
oup = BeautifulSoup("<b><!--This is a comment--></b>",'xml') comment = soup.b.string print(comment) # This is a comment print(type(comment)) # <class 'bs4.element.Comment'>
contents屬性返回全部子節點的列表,包括NavigableString類型節點。若是節點當中有換行符,會被當作是NavigableString類型節點而做爲一個子節點。
NavigableString 類型節點沒有contents屬性,由於沒有子節點。
soup = BeautifulSoup("""<div> <span>test</span> </div> """,'xml') element = soup.div.contents print(element) # ['\n', <span>test</span>, '\n']
children屬性跟contents屬性基本同樣,只不過返回的不是子節點列表,而是子節點的可迭代對象。
descendants屬性返回tag的全部子孫節點。
若是tag只有一個NavigableString類型子節點,那麼這個tag可使用.string獲得子節點。
若是一個tag僅有一個子節點,那麼這個tag也可使用.string方法,輸出結果與當前惟一子節點的.string結果相同。
若是tag包含了多個子節點,tag就沒法肯定.string方法應該調用哪一個子節點的內容,.string 的輸出結果是None。
soup = BeautifulSoup("""<div> <p><span><b>test</b></span></p> </div> """,'xml') element = soup.p.string print(element) # test print(type(element)) # <class 'bs4.element.NavigableString'>
特別注意,爲了清楚顯示,通常咱們會將html節點換行縮進顯示,而在BeautifulSoup中會被認爲是一個NavigableString類型子節點,致使出錯。上例中,若是改爲element = soup.div.string就會出錯。
若是tag中包含多個字符串,能夠用strings屬性來獲取。若是返回結果中要去除空行,則能夠用stripped_strings屬性。
soup = BeautifulSoup("""<div> <p> </p> <p>test 1</p> <p>test 2</p> </div> """, 'html.parser') element = soup.div.stripped_strings print(list(element)) # ['test 1', 'test 2']
parent屬性返回某個元素(tag、NavigableString)的父節點,文檔的頂層節點的父節點是BeautifulSoup對象,BeautifulSoup對象的父節點是None。
parents屬性遞歸獲得元素的全部父輩節點,包括BeautifulSoup對象。
next_sibling返回後一個兄弟節點,previous_sibling返回前一個兄弟節點。直接看個例子,注意別被換行縮進攪了局。
soup = BeautifulSoup("""<div> <p>test 1</p><b>test 2</b><h>test 3</h></div> """, 'html.parser') print(soup.b.next_sibling) # <h>test 3</h> print(soup.b.previous_sibling) # <p>test 1</p> print(soup.h.next_sibling) # None
next_siblings返回後面的兄弟節點
previous_siblings返回前面的兄弟節點
把html解析當作依次解析標籤的一連串事件,BeautifulSoup提供了重現解析器初始化過程的方法。
next_element屬性指向解析過程當中下一個被解析的對象(tag或NavigableString)。
previous_element屬性指向解析過程當中前一個被解析的對象。
另外還有next_elements和previous_elements屬性,不贅述了。
介紹find_all()方法前,先介紹一下過濾器的類型,這些過濾器貫穿整個搜索的API。過濾器能夠被用在tag的name中,節點的屬性中,字符串中或他們的混合中。
html = """ <div> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</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></p> </div> """ soup = BeautifulSoup(html, 'html.parser')
查找全部的<b>標籤
print(soup.find_all('b')) # [<b>The Dormouse's story</b>]
傳入正則表達式做爲參數,返回知足正則表達式的標籤。下面例子中找出全部以b開頭的標籤。
print(soup.find_all(re.compile("^b"))) # [<b>The Dormouse's story</b>]
傳入列表參數,將返回與列表中任一元素匹配的內容。下面例子中找出全部<a>標籤和<b>標籤。
print(soup.find_all(["a", "b"])) # [<b>The Dormouse's story</b>, <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
True能夠匹配任何值,下面的代碼查找到全部的tag,可是不會返回字符串節點。
print(soup.find_all(True))
若是沒有合適的過濾器,還能夠自定義一個方法,方法只接收一個元素參數,若是這個方法返回True表示當前元素匹配被找到。
例子,返回全部包含class屬性但不包含id屬性的標籤:
def has_class_but_no_id(tag): return tag.has_attr('class') and not tag.has_attr('id') print(soup.find_all(has_class_but_no_id))
結果:
結果怎麼不對呢,<a>標籤含有id屬性。其實返回的list中只有2個元素,都是<p>標籤,<a>標籤是<p>標籤的子節點。
搜索當前tag的全部tag子節點,並判斷是否符合過濾器的條件。
語法:
find(name=None, attrs={}, recursive=True, text=None, **kwargs)
find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
參數:
name:查找全部名字爲name的tag,字符串對象會被自動忽略掉。上面過濾器示例中的參數都是name參數。固然,其餘參數中也可使用過濾器。
attrs:按屬性名和值查找。傳入字典,key爲屬性名,value爲屬性值。
recursive:是否遞歸遍歷全部子孫節點,默認True。
text:用於搜索字符串,會找到 .string方法與text參數值相符的tag,一般配合正則表達式使用。也就是說,雖然參數名是 text,但實際上搜索的是string屬性。
limit:限定返回列表的最大個數。
kwargs:若是一個指定名字的參數不是搜索內置的參數名,搜索時會把該參數看成tag的屬性來搜索。這裏注意,若是要按class屬性搜索,由於class是python的保留字,須要寫做class_。
Tag的有些屬性在搜索中不能做爲kwargs參數使用,好比H5中的data-*屬性。
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>') print(data_soup.find_all(data-foo="value")) # SyntaxError: keyword can't be an expression
可是能夠經過attrs參數傳遞
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','xml') print(data_soup.find_all(attrs={"data-foo": "value"})) # [<div data-foo="value">foo!</div>]
BeautifulSoup支持大部分的CSS選擇器,這裏直接用代碼來演示。
from bs4 import BeautifulSoup html = """ <html> <head><title>標題</title></head> <body> <p class="title" name="dromouse"><b>標題</b></p> <div name="divlink"> <p> <a href="http://example.com/1" class="sister" id="link1">連接1</a> <a href="http://example.com/2" class="sister" id="link2">連接2</a> <a href="http://example.com/3" class="sister" id="link3">連接3</a> </p> </div> <p></p> <div name='dv2'></div> </body> </html> """ soup = BeautifulSoup(html, 'lxml') # 經過tag查找 print(soup.select('title')) # [<title>標題</title>] # 經過tag逐層查找 print(soup.select("html head title")) # [<title>標題</title>] # 經過class查找 print(soup.select('.sister')) # [<a class="sister" href="http://example.com/1" id="link1">連接1</a>, # <a class="sister" href="http://example.com/2" id="link2">連接2</a>, # <a class="sister" href="http://example.com/3" id="link3">連接3</a>] # 經過id查找 print(soup.select('#link1, #link2')) # [<a class="sister" href="http://example.com/1" id="link1">連接1</a>, # <a class="sister" href="http://example.com/2" id="link2">連接2</a>] # 組合查找 print(soup.select('p #link1'))# [<a class="sister" href="http://example.com/1" id="link1">連接1</a>] # 查找直接子標籤 print(soup.select("head > title"))# [<title>標題</title>] print(soup.select("p > #link1"))# [<a class="sister" href="http://example.com/1" id="link1">連接1</a>] print(soup.select("p > a:nth-of-type(2)")) # [<a class="sister" href="http://example.com/2" id="link2">連接2</a>] # nth-of-type 是CSS選擇器 # 查找兄弟節點(向後查找) print(soup.select("#link1 ~ .sister")) # [<a class="sister" href="http://example.com/2" id="link2">連接2</a>, # <a class="sister" href="http://example.com/3" id="link3">連接3</a>] print(soup.select("#link1 + .sister")) # [<a class="sister" href="http://example.com/2" id="link2">連接2</a>] # 經過屬性查找 print(soup.select('a[href="http://example.com/1"]')) # ^ 以XX開頭 print(soup.select('a[href^="http://example.com/"]')) # * 包含 print(soup.select('a[href*=".com/"]')) # 查找包含指定屬性的標籤 print(soup.select('[name]')) # 查找第一個元素 print(soup.select_one(".sister"))
咱們來爬取三國演義的內容,我甚至有個想法,去那些小說網裏爬小說去。
import requests from bs4 import BeautifulSoup url = 'http://www.shicimingju.com/book/sanguoyanyi.html' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' } page_text = requests.get(url=url,headers=headers).text soup = BeautifulSoup(page_text,'lxml') a_list = soup.select('.book-mulu > ul > li > a') fp = open('sanguo.txt','w',encoding='utf-8') for a in a_list: title = a.string detail_url = 'http://www.shicimingju.com'+a['href'] detail_page_text = requests.get(url=detail_url,headers=headers).text soup = BeautifulSoup(detail_page_text,'lxml') content = soup.find('div',class_='chapter_content').text fp.write(title+'\n'+content) print(title,'下載完畢') print('over')