Beautiful Soup是python的另一個HTML或XML解析庫,可以很友善的提取網頁中的資料,利用它可以省去很多繁瑣的提取工作,提高了解析效率。
首先從一個執行個體出發:
html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse'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" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" class="sister" id="link1"><!--- Elsie --></a>,
<a href="http://example.com/lacie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" 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.prettify())
print(soup.title.string)
運作結果:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9sGROdXT61EMJRVT3V1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0UDN5AjNwETM2ETOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
代碼分析:這裡首先聲明了html,并不是一個完整的HTML字元串,然後調用BeautifulSoup并初始化對象,調用prettify()方法把要解析的字元串以标準的縮進格式輸出。然後調用soup.title.string輸出HTML中title節點的文本内容。
節點選擇器
直接調用節點的名稱就可以選擇節點元素,在調用string屬性就可以得到節點内的文本了。
- 選擇元素
運作結果如下:當有多個節點時,列印第一個節點。print(soup.title) print(type(soup.title)) print(soup.title.string) print(soup.head) print(soup.p)
2. 提取資訊
# 提取資訊\
#節點名稱
print(soup.title.name)
#節點屬性
print(soup.p.attrs)
print(soup.p["name"])
print(soup.p["class"])
# 節點内容
print(soup.p.string)
運作結果如下:
3. 嵌套選擇
上面的例子中,每一個傳回結果都是bs4.element.Tag類型,它同樣可以繼續調用節點進行下一步的選擇。
# 嵌套選擇
print(soup.head.title)
print(soup.head.title.string)
4. 關聯選擇
在做選擇的時候不能一步就選到想要的節點,需要選擇一個節點,然後以他為基準再選擇它的子節點、父節點、兄弟節點等。
(1) 子節點和子孫節點
選擇節點元素之後,調用contents屬性擷取它的直接子節點。
html = """
<html><head><title>The Dormoues'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" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" class="sister" id="link1"><!--- Elsie --></a>,
<a href="http://example.com/lacie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" 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節點直接字典帶你既包含文本,又包含節點。可以調用children屬性得到響應的結果:
for i,child in enumerate(soup.p.children):
print(i,child)
運作結果如下:
調用descendants屬性擷取所有子孫節點:
for i,child in enumerate(soup.p.descendants):
print(i,child)
運作結果如下:
(2) 父節點和祖先節點
parent屬性可以擷取節點的父節點。
print(soup.a.parent)
運作結果如下:
擷取第一個a節點的父節點p。
parents屬性可以擷取節點的祖先節點。
print(type(soup.a.parents))
print(list(enumerate(soup.a.parents)))
運作結果如下:
(3) 兄弟節點
next_sibling和previous_sibling屬性擷取節點的下一個和上一個兄弟元素,next_siblings和previous_siblings屬性傳回節點所有前面的和後面的兄弟節點的生成器。
5. 提取資訊
文本string,屬性attrs。
html = """
<html><head><title>The Dormoues'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/lacie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" class="sister" id="link2">Lacie</a><a href="http://example.com/tillie" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" 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('-'*15,'擷取資訊','-'*15)
# 擷取資訊
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print(type(soup.a.parents))
print(list(soup.a.parents)[0])
print(list(soup.a.parents)[0].attrs['class'])
運作結果如下:
如果傳回的是單個節點,可以直接調用string、attrs等屬性擷取文本和屬性值;如果傳回結果是多個節點的生成器,則可以轉為清單取出某個元素,再調用string、attrs等屬性。
方法選擇器
前面所講的選擇方法都是通過屬性來選擇的,這種方法非常快,但是如果進行比較複雜的選擇就比較繁瑣,find_all()、find()方法可以靈活查詢。
1. find_all( )
查詢所有符合條件的元素,傳入一些屬性或文本,就可以的東岸符合條件的元素。
API:find_all(name,attrs,recursive,text,**kwargs)
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>
"""
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(name="ul"))
print(type(soup.find_all(name="ul")[0]))
運作結果如下:
# 同樣可以嵌套選擇
for ul in soup.find_all(name="ul"):
print(ul.find_all(name="li"))
for li in ul.find_all(name="li"):
print(li.string)
print(li.attrs)
運作結果如下:
也可以通過屬性來選擇:
print(soup.find_all(attrs={'id':'list-1'}))
print(soup.find_all(attrs={'name':'elements'}))
對于一些常用屬性,如id和class等,我們可以不用attrs來傳遞。
print(soup.find_all(id="list-1"))
print(soup.find_all(class_="element"))
Text參數可用來比對節點的文本,傳入的形式可以是字元串,也可以是正規表達式,
html = """
<div class="panel">
<div class="panel-body">
<a>Hello, this is a link</a>
<a>Hello, this is a link too</a>
"""
import re
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))
2. find( )
傳回第一個元素。
CSS選擇器
BeautifulSoup提供了CSS選擇器,隻需要調用select()方法傳入相應的CSS選擇器即可。
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>
"""
print('-'*15,'CSS選擇器','-'*15)
soup = BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select("#list-2 .element"))
print(soup.select('ul')[0])
運作結果如下:
這裡調用了3次CSS選擇器,傳回的結果是符合CSS選擇器節點組成的清單。#表示id選擇器,.表示class選擇器。
1. 嵌套選擇
for ul in soup.select('ul'):
print(ul.select('li'))
2. 擷取屬性
for ul in soup.select('ul'):
print(ul['id'])
print(ul.attrs['id'])
3. 擷取文本
for li in ul.select('li'):
print(li.string)
print(li.get_text())
以上就是BeautifulSoup的學習,接下來可以通過一個作業來實踐一下:
利用requests和BeautifulSoup爬取某個靜态網頁的内容,這裡以上面那個例子為例子(上面是XPath解析庫解析)。(全部代碼見news.py)
網頁位址為:https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8
第一步和第二步檢視網頁和網頁源碼見上篇部落格。
第三步,完整代碼
import requests
from lxml import etree
def get_html(url):
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"}
response = requests.get(url,headers=headers)
if response.status_code==200:
return response.text
return None
from bs4 import BeautifulSoup
def main1():
url = 'https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8'
html = get_html(url)
soup = BeautifulSoup(html,'lxml')
#标題
print(soup.select('h1.a-title strong')[0].string)
#時間
print(soup.select('.a-info span.time')[0].string)
#責編
print(soup.select('.a-edit')[0].string)
#内容
#html
print(soup.select('.content-a')[0].select('.a-con')[0])
# text
for p in soup.select('.content-a')[0].select('.a-con')[0].select('p'):
print(p.string)
if __name__=='__main__':
main1()
如果對你有用,點個贊 手動笑臉(*_*)