眾多語言都能進行爬蟲,但基於python的爬蟲顯得更簡潔,方便。爬蟲也成了python語言中不可或缺的一部分。爬蟲的解析方式也是多樣化。
Requests函式庫的用法大家一定已經熟練了,但是當我們使用Requests取得到網頁的 HTML 程式碼資訊後,我們要如何才能抓取到我們想要的資訊呢?我相信大家肯定嘗試過很多辦法,例如字串的 find 方法,還有高階點的正規表示式。雖然正規可以匹配到我們需要的信息,但是我相信大家在匹配某個字符串一次一次嘗試著正則匹配的規則時,一定很鬱悶。
那麼,我們就會想有沒有方便點的工具呢。答案是肯定的,我們還有一個強大的工具,叫做BeautifulSoup。有了它我們可以很方便地提取出HTML或XML標籤中的內容,這篇文章就讓我們了解下BeautifulSoup的常用方法吧。
上一篇為大家講解的是爬蟲的解析方式一:JOSN解析,本篇帶給大家Beautifulsoup解析。
#什麼是BeautifulSoup?
Python的網頁解析可以用正規表示式去完成,那麼我們在寫的時候,要挨個的去把程式碼拿出來匹配,而且還要寫匹配的規則,整體實現起來就很複雜。 BeautifulSoup呢,它是一個方便的網頁解析庫,處理高效,支援多種解析器。大部分情況下,利用它我們不在需要編寫正規表示式就可以方便的實現網頁資訊的提取。
官方文件
安裝:$ pip install beautifulsoup4
BeautifulSoup是一個網頁解析庫,它支援很多解析器,不過最主流的有兩個。一個是Python標準函式庫,一個是lxml HTML 解析器。兩者的使用方法相似:
from bs4 import BeautifulSoup # Python的标准库 BeautifulSoup(html, 'html.parser') # lxml BeautifulSoup(html, 'lxml')
Python內建標準函式庫的執行速度一般,但在低版的Python中,中文的容錯能力比較差。 lxmlHTML 解析器的執行速度快,但需要安裝 C語言的依賴函式庫。
lxml的安裝
由於lxml安裝需要依賴C語言庫,所以當lxml在Windows上安裝時,我們會發現各種奇怪的報錯,當然臉好的使用pip install lxml
安裝也是可以成功的。不過大部分人都是會倒在這裡。
這裡推薦大家使用lxml的.whl檔來安裝。首先我們要安裝一下wheel函式庫,有了這個函式庫我們才可以正常安裝.whl檔。 pip install wheel
從官方網站下載與系統,Python版本相符的lxml檔。
另外,不知道自己系統和python版本資訊的夥伴。需要進入系統管理員工具(CMD)或python的 IDLE,輸入以下程式碼:
import pip print(pip.pep425tags.get_supported())
這時我們就可以看到印出來的Python版本資訊了。
下載好lxml的檔案後,我們需要找到檔案的位置,然後進入管理員工具,使用pip安裝:pip install whl檔案的全名
安裝完成後,可以進入Python,import一下,如果沒有報錯,那麼恭喜你安裝成功。
如果有的夥伴覺得麻煩,那我推薦大家安裝anaconda 下載地址(如果安裝速度慢,可以找國內鏡像),不知道是什麼的小伙伴可以穀歌一下,有了他,那些在windows上pip安裝出錯的問題將不再存在。
BeautifulSoup的基本標籤選擇方法
雖然Python內建的標準庫解析器還不錯,但我還是推薦大家使用lxml,因為它夠快。那麼後面的程式碼我們都是用lxml解析器來進行示範。
我們先導入官方文件的例子:
html_doc = """ <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> """
HTML 程式碼,我們能夠得到一個BeautifulSoup的物件,並且能按照標準的縮排格式的結構輸出:
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc, 'lxml')
我們可以看到上面的HTML 程式碼並不完整,接下來我們使用prettify()方法來進行自動補全,註解部分就是運行的輸出:
print(soup.prettify()) # <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 class="sister" href="http://example.com/elsie" id="link1"> # Elsie # </a> # , # <a class="sister" href="http://example.com/lacie" id="link2"> # Lacie # </a> # and # <a class="sister" href="http://example.com/tillie" id="link2"> # Tillie # </a> # ; and they lived at the bottom of a well. # </p> # <p class="story"> # ... # </p> # </body> # </html>
取得標籤
print(soup.title) # <title>The Dormouse's story</title>
透過輸出結果,我們可以看到取得內容的屬性,其實就是HTML 程式碼裡的一個title標籤。
取得名稱
print(soup.title.name) # 'title'
其實就是標籤的名稱。
取得屬性
print(soup.p.attrs['class']) # 'title' print(soup.p['class']) # 'title'
取得標籤的屬性我們可以使用attrs方法,傳給他屬性名,就可以得到標籤的屬性。透過結果我們可以看到,直接傳給p標籤屬性名,一樣可以取得到標籤屬性。
取得內容
#print(soup.title.string) # 'The Dormouse's story'
我们还可以使用嵌套的选择,比如我们获得body标签里面p标签的内容:
print(soup.body.p.string) # 'The Dormouse's story'
常见用法
标准选择器
虽然BeautifulSoup的基本用法,标签获取,内容获取,可以解析一些 html代码。但是在遇到很多复杂的页面时,上面的方法是完全不足的,或者是很繁琐的,因为有时候有的标签会有几个属性(class、id等)。
索性BeautifulSoup给我们提供了很方便的标准选择器,也就是 API 方法,这里着重介绍2个: find() 和 find_all() 。其它方法的参数和用法类似,大家举一反三吧。
find_all()
find_all(name, attrs, recursive, text, **kwargs)可以根据标签,属性,内容查找文档。
find_all()其实和正则表达式的原理很相似,他能找出所有能满足匹配模式的结果,在把结果以列表的形式返回。
仍然是文档的例子:
html_doc = """ <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> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc, 'lxml')
过滤器
文档参考
介绍 find_all() 方法前,大家可以参考一下过滤器的类型。过滤器只能作为搜索文档的参数,或者说应该叫参数类型更为贴切。这些过滤器贯穿整个搜索的API。过滤器可以被用在 tag 的name中,节点的属性中,字符串中或他们的混合中。
find_all() 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件。这里有几个例子:
soup.find_all("title") # [<title>The Dormouse's story</title>] soup.find_all("p", "title") # [<p class="title"><b>The Dormouse's story</b></p>] soup.find_all("a") # [<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>] soup.find_all(id="link2") # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
有几个方法很相似,还有几个方法是新的,参数中的 string 和id是什么含义? 为什么 find_all("p", "title") 返回的是CSS Class为”title”的标签? 我们来仔细看一下find_all()的参数:
name参数
name 参数可以查找所有名字为 name 的 tag,字符串对象会被自动忽略掉。
soup.find_all("title") # [The Dormouse's story]
搜索 name 参数的值可以使任一类型的过滤器,字符窜,正则表达式,列表,方法或是True 。
我们常用的 name 参数是搜索文档的标签名。
keyword参数
如果我们的 HTML代码中有几个div标签,但是我们只想获取到class属性为top的div标签,我们怎么出来呢。
soup.find_all('div', class_='top')
# 这里注意下,class是Python的内部关键词,我们需要在css属性class后面加一个下划线'_',不然会报错。
仍然以上面的代码实例:
soup.find_all('a', id='link2') # [<a id="link2" href="http://example.com/lacie">Lacie</a>]
这样我们就只获取到id为link2的a标签。
limit参数
find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢。如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量。效果与 SQL 中的limit关键字类似,当搜索到的结果数量达到limit的限制时,就停止搜索返回结果。
比如我们要搜索出a标签,但是满足的有3个,我们只想要得到2个:
soup.find_all("a", limit=2) # [<a id="link1" class="sister" href="http://example.com/elsie">Elsie</a>, # <a id="link2" class="sister" href="http://example.com/lacie">Lacie</a>]
其他的参数,不是经常用到,大家如需了解可以参考官方文档。
find()
find_all()返回的是所有元素列表,find()返回单个元素。
find( name , attrs , recursive , string , **kwargs )
find_all()方法将返回文档中符合条件的所有 tag,尽管有时候我们只想得到一个结果。比如文档中只有一个标签,那么使用find_all()方法来查找标签就不太合适, 使用find_all方法并设置limit=1参数不如直接使用find()方法。下面两行代码是等价的:
soup.find_all('title', limit=1) # [The Dormouse's story] soup.find('title') #The Dormouse's story
唯一的区别是find_all()方法的返回结果是值包含一个元素的列表,而find()方法直接返回结果。find_all()方法没有找到目标是返回空列表, find()方法找不到目标时,返回None。
CSS选择器
Beautiful Soup支持大部分的 CSS选择器。在Tag或BeautifulSoup对象的.select()方法中传入字符串参数, 即可使用 CSS选择器的语法找到 tag。我们在写 css 时,标签 class类名加”.“,id属性加”#“。
soup.select("title") # [The Dormouse's story]
通过 tag标签逐层查找:
soup.select("body a") # [<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>] soup.select("html head title") # [<title>The Dormouse's story</title>]
找到某个 tag标签下的直接子标签:
soup.select("head > title") # [<title>The Dormouse's story</title>] soup.select("p > a") # [<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>] soup.select("p > #link1") # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>] soup.select("body > a") # []
通过 CSS 的 class类名查找:
soup.select(".sister") # [<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>]
通过 tag 的 id 查找:
soup.select("#link1") # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>] soup.select("a#link2") # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
同时用多种 CSS选择器查询元素,使用逗号隔开:
soup.select("#link1,#link2") # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
提取标签内容
如果我们得到了几个标签:
list = [<a href="http://www.baidu.com/">百度</a>, <a href="http://www.163.com/">网易</a>, <a href="http://www.sina.com/"新浪</a>]
我们要怎样提取他里面的内容呢。我们开始的时候有提及。
for i in list: print(i.get_text()) # 我们使用get_text()方法获得标签内容 print(i.get['href'] # get['attrs']方法获得标签属性 print(i['href']) # 简写结果一样
结果:
百度 网易 新浪 http://www.baidu.com/ http://www.163.com/ http://www.sina.com/ http://www.baidu.com/ http://www.163.com/ http://www.sina.com/
总结
BeautifulSoup的解析庫,建議使用lxml,如果出現亂碼的情況下,可以使用html.parser;BeautifulSoup的標籤選擇篩選方法,雖然弱但是速度快;建議使用find_all(),find()方法搜尋標籤,當然如果對css選擇器熟悉,推薦使用.select()方法;get_text()方法取得標籤文字內容,get[attrs]方法取得標籤屬性值。
以上是爬蟲類的解析方式二:Beautifulsoup的詳細內容。更多資訊請關注PHP中文網其他相關文章!