一:beautifulsoup簡介
beautifulsoup是一個非常強大的工具,爬蟲利器。
beautifulSoup “美味的湯,綠色的濃湯”
一個靈活又方便的網頁解析庫,處理高效,支持多種解析器。
利用它就不用編寫正則表達式也能方便的實現網頁信息的抓取。
二:常用解析庫
Beautiful Soup支持Python標准庫中的HTML解析器,還支持一些第三方的解析器,如果我們不安裝它,則 Python 會使用 Python默認的解析器,lxml 解析器更加強大,速度更快,推薦安裝。
下面是常見解析器:
解析器 | 使用方法 | 優勢 | 劣勢 |
---|---|---|---|
Python標准庫 | BeautifulSoup(markup, "html.parser") | Python的內置標准庫、執行速度適中 、文檔容錯能力強 | Python 2.7.3 or 3.2.2)前的版本中文容錯能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快、文檔容錯能力強 | 需要安裝C語言庫 |
lxml XML 解析器 | BeautifulSoup(markup, "xml") | 速度快、唯一支持XML的解析器 | 需要安裝C語言庫 |
html5lib | BeautifulSoup(markup, "html5lib") | 最好的容錯性、以瀏覽器的方式解析文檔、生成HTML5格式的文檔 | 速度慢、不依賴外部擴展 |
三:基本使用
# BeautifulSoup入門 from bs4 import BeautifulSoup html = ''' <html><head><title>The Dormouse's story</title></head> <body> <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>; and they lived at the bottom of a well.</p> <p class="story">...</p> ''' soup = BeautifulSoup(html,'lxml') # 創建BeautifulSoup對象 print(soup.prettify()) # 格式化輸出 print(soup.title) # 打印標簽中的所有內容 print(soup.title.name) # 獲取標簽對象的名字 print(soup.title.string) # 獲取標簽中的文本內容 == soup.title.text print(soup.title.parent.name) # 獲取父級標簽的名字 print(soup.p) # 獲取第一個p標簽的內容 print(soup.p["class"]) # 獲取第一個p標簽的class屬性 print(soup.a) # 獲取第一個a標簽 print(soup.find_all('a')) # 獲取所有的a標簽 print(soup.find(id='link3')) # 獲取id為link3的標簽 print(soup.p.attrs) # 獲取第一個p標簽的所有屬性 print(soup.p.attrs['class']) # 獲取第一個p標簽的class屬性 print(soup.find_all('p',class_='title')) # 查找屬性為title的p # 通過下面代碼可以分別獲取所有的鏈接以及文字內容 for link in soup.find_all('a'): print(link.get('href')) # 獲取鏈接 print(soup.get_text())獲取文本
(1):標簽選擇器
在快速使用中我們添加如下代碼:
print(soup.title)
print(type(soup.title))
print(soup.head)
print(soup.p)
通過這種soup.標簽名 我們就可以獲得這個標簽的內容
這里有個問題需要注意,通過這種方式獲取標簽,如果文檔中有多個這樣的標簽,返回的結果是第一個標簽的內容,如我們通過soup.p獲取p標簽,而文檔中有多個p標簽,但是只返回了第一個p標簽內容。
(2):獲取名稱
當我們通過soup.title.name的時候就可以獲得該title標簽的名稱,即title。
(3):獲取屬性
print(soup.p.attrs['name'])
print(soup.p['name'])
上面兩種方式都可以獲取p標簽的name屬性值
(4):獲取內容
print(soup.p.string)
結果就可以獲取第一個p標簽的內容。
(5):嵌套選擇
我們直接可以通過下面嵌套的方式獲取
print(soup.head.title.string)
(6):子節點和子孫節點
a、contents的使用
html = """ <html> <head> <title>The Dormouse's story</title> </head> <body> <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"> <span>Elsie</span> </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> and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html,'lxml') print(soup.p.contents) # 獲取p標簽中的所有內容,各部分存入一個列表
################################ 運行結果
['\n Once upon a time there were three little sisters; and their names were\n ', <a class="sister" href="http://example.com/elsie" id="link1"> <span>Elsie</span> </a>, '\n', <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, '\n and\n ', <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>, '\n and they lived at the bottom of a well.\n ']
#################################
b、children的使用
print(soup.p.children) for i,child in enumerate(soup.p.children): print(i,child)
# 通過children也可以獲取內容,和contents獲取的結果是一樣的,但是children是一個迭代對象,而不是列表,只能通過循環的方式獲取信息
print(soup.descendants)# 獲取子孫節點
(7):父節點和祖父節點
通過soup.a.parent就可以獲取父節點的信息
通過list(enumerate(soup.a.parents))可以獲取祖先節點,這個方法返回的結果是一個列表,會分別將a標簽的父節點的信息存放到列表中,以及父節點的父節點也放到列表中,並且最后還會講整個文檔放到列表中,所有列表的最后一個元素以及倒數第二個元素都是存的整個文檔的信息
(8):兄弟節點
soup.a.next_siblings 獲取后面的兄弟節點
soup.a.previous_siblings 獲取前面的兄弟節點
soup.a.next_sibling 獲取下一個兄弟標簽
souo.a.previous_sinbling 獲取上一個兄弟標簽
四:標准選擇器
(1):find_all
find_all(name,attrs,recursive,text,**kwargs)
可以根據標簽名,屬性,內容查找文檔
a、name
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all('ul')) # 找到所有ul標簽 print(type(soup.find_all('ul')[0])) # 拿到第一個ul標簽 # find_all可以多次嵌套,如拿到ul中的所有li標簽 for ul in soup.find_all('ul'): print(ul.find_all('li'))
b、attrs
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1" name="elements"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(attrs={'id': 'list-1'})) # 找到id為ilist-1的標簽 print(soup.find_all(attrs={'name': 'elements'})) # 找到name屬性為elements的標簽
注意:attrs可以傳入字典的方式來查找標簽,但是這里有個特殊的就是class,因為class在python中是特殊的字段,所以如果想要查找class相關的可以更改attrs={'class_':'element'}或者soup.find_all('',{"class":"element}),特殊的標簽屬性可以不寫attrs,例如id
c、text
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(text='Foo')) # 查到所有text="Foo"的文本
(2)find
find(name,attrs,recursive,text,**kwargs)
find返回的匹配結果的第一個元素
其他一些類似的用法:
find_parents()返回所有祖先節點,find_parent()返回直接父節點。
find_next_siblings()返回后面所有兄弟節點,find_next_sibling()返回后面第一個兄弟節點。
find_previous_siblings()返回前面所有兄弟節點,find_previous_sibling()返回前面第一個兄弟節點。
find_all_next()返回節點后所有符合條件的節點, find_next()返回第一個符合條件的節點
find_all_previous()返回節點后所有符合條件的節點, find_previous()返回第一個符合條件的節點
五:CSS選擇器
通過select()直接傳入CSS選擇器就可以完成選擇
熟悉前端的人對CSS可能更加了解,其實用法也是一樣的
.表示class #表示id
標簽1,標簽2 找到所有的標簽1和標簽2
標簽1 標簽2 找到標簽1內部的所有的標簽2
[attr] 可以通過這種方法找到具有某個屬性的所有標簽
[atrr=value] 例子[target=_blank]表示查找所有target=_blank的標簽
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.select('.panel .panel-heading')) print(soup.select('ul li')) print(soup.select('#list-2 .element')) print(type(soup.select('ul')[0]))
a、獲取內容
通過get_text()就可以獲取文本內容
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') for li in soup.select('li'): print(li.get_text())
b、獲取屬性
獲取屬性的時候可以通過[屬性名]或者attrs[屬性名]
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') for ul in soup.select('ul'): print(ul['id']) print(ul.attrs['id'])
六:總結
- 推薦使用lxml解析庫,必要時使用html.parser
- 標簽選擇篩選功能弱但是速度快
- 建議使用find()、find_all() 查詢匹配單個結果或者多個結果
- 如果對CSS選擇器熟悉建議使用select()
- 記住常用的獲取屬性和文本值的方法