BeautifulSoup

      在〈BeautifulSoup〉中尚無留言

美麗的湯

BeautifulSoup 可以對 requests 下載下來的 html,css, javascript 進行樹狀節點化分析。

早期曾在 xml解析 一篇中介紹 xml.etree.ElementTree 對xml進行分析。那麼 html 也是屬於 xml 的一種,所以 html 是否也可以使用 ElementTree進行分析呢?答案是不行的,因為 html 內含 javascript,若使用 ElementTree 則會出現例外處理而閃退。

Beautiful Soup能將html結構化成樹狀節點不需多少代碼即可以寫出一個完整的應用程序。Beautiful Soup 自動將輸入文檔轉換為Unicode編碼,輸出文檔轉換為 utf-8 編碼, 所以無需要考慮編碼方式。

在正規表式示裏, 符號多且不好背也不好理解, 讓人非常煩雜, 所以很多人都不喜歡用正規表示式. 此時bs4就派上用場了.

安裝

pip install beautifulsoup4 lxml

若沒有安裝lxml的話, bs4會使用標準庫中的HTML解析器. 不過 lxml 解析器更強大,速度更快,推薦安裝。

使用方式

import requests, re
from bs4 import BeautifulSoup
page = requests.get("http://www.uuxs.tw/ls/22_22102/")
page.encoding="utf-8"
soup=BeautifulSoup(page.text, "html.parser")
print(soup.prettify())
結果: 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <title>
 天降巨富最新章节列表,全文阅读,UU小说网无弹窗广告
 </title>
 <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
 <meta content="小说天降巨富最新章节列表,全文阅读" name="keywords"/>
 <meta content="感谢您支持陆原居的小说天降巨富,UU小说网提供天降巨富最新章节连载和在线阅读,更新速度最快且无弹窗广告,7*24小时不间断更新天降巨富,请收藏以方便今后阅读。" name="description"/>
.................
 <div id="intro">
 <p>
 作为一个超级富二代装穷是一种什么体验?别拦着我,没有人比我更有资格回答这个问题!
 </p>
 <p>
 本站提示:各位书友要是觉得《天降巨富》还不错的话请不要忘记向您QQ群和微博里的朋友推荐哦!
 </p>
 </div>
 </div>
.........................

上述是使用requests取得網頁page後, 再將page.text傳入 bs4內, 最後使用prettify()將整個結構印出來.

ps : prettify [ˋprɪtɪ͵faɪ] : 美化

本地端檔案

requests的get, post都必需是遠端被在伺服器的檔案. 對於本地端無web server的網頁檔, 則無法開啟. 但bs4確可以輕易的打開, 如下

soup = BeautifulSoup(open('index.htm'))
print(soup.prettify())

請注意, 要開啟的檔案, 必需儲存為ANSI格式, 不可以使用utf-8. 所以請先用記事本重新檢查看看

遍訪整個樹狀結構

bs4會將html的每個標籤轉換成節點, 並以樹狀結構存放在記憶体裏

soup.head : 傳回head節點
soup.title : 傳回title節點
soup.body : 傳回body節點
soup.a : 傳回第一個標籤為<a>的節點
soup.h1 : 傳回第出第一個標籤為<h1>的節點

soup.body.contents : 將 body 內的子節點以list傳回, 可以使用len()查得裏面有幾個節點數
soup.body.children : 將 body 內的子節點以list產生器傳回, 需使用 foreach 列印出來

strings

具有多個節點的物件, 就可以使用strings將所有的子節點的內容(text)印出, 比如soup.body.strings. 此法亦需使用foreach印出

ls=soup.body.strings
for l in ls:
    print(l)

搜尋整顆樹狀結構

soup.a : 列印第一個<a>標籤, 若加string, 則會去除標籤(僅適用於單一節點)

node = soup.find(‘a’, href=’/ls/84_84080/’) : 搜尋第一筆 節點為 <a>, 屬性為 href=’/ls/84_84080/’
node.get(“href”) : 取得節點的 href 屬性
ls=soup.find_all(‘a’, href=’/ls/84_84080/’, recursive=True) : 同上, 但搜尋所有的文件, 並將結果以List傳回
ls=soup.find_all([‘a’, ‘b’], limit=2) : 搜尋多個條件, 並限制傳回的筆數
ls=soup.find_all(“b”,string=”小影后她又奶又萌”) : 搜尋特定節點中,text 為 “小影后她又奶又萌”的節點,結果以list傳回。

底下的代碼,則是利用上述的方法,將天降巨富小說中的文章超連節印出

import requests, re
from bs4 import BeautifulSoup
page = requests.get("http://www.uuxs.tw/ls/22_22102/")
page.encoding="utf-8"
soup=BeautifulSoup(page.text, "html.parser")
dds=soup.find_all('dd')
for node in dds:
    link=node.find('a')
    print(node, link.get('href'), link.get('title'))
結果 :
<dd><a href="22145254.html" title="第五百五十章 打开后备厢看看">第五百五十章 打开后备厢看看</a></dd> 22145254.html 第五百五十章 打开后备厢看看
<dd><a href="22274451.html" title="第五百五十一章 赵思思何去何从">第五百五十一章 赵思思何去何从</a></dd> 22274451.html 第五百五十一章 赵思思何去何从
<dd><a href="22416022.html" title="第五百五十二章 外婆舅舅这一家">第五百五十二章 外婆舅舅这一家</a></dd> 22416022.html 第五百五十二章 外婆舅舅这一家
<dd><a href="23488023.html" title="第五百五十三章 把亲生母亲接来">第五百五十三章 把亲生母亲接来</a></dd> 23488023.html 第五百五十三章 把亲生母亲接来
<dd><a href="23509205.html" title="第五百五十四章 去别墅接回曹凤">第五百五十四章 去别墅接回曹凤</a></dd> 23509205.html 第五百五十四章 去别墅接回曹凤

正規表示法搜尋

條件內可以擺放正規表示法, 首先必需先import re

import re
s='123456789'
p=re.compile('[a,5].*')
a=p.findall(s)
print(a)

“.”  : 任何字元
“*” : 至少匹配 0 次
“^” : 整行的開頭

‘^1’ :  1
‘^1.*’ : 123456789

“$” : 整行的結尾

‘[]’ : 匹配到[]內的任一字元

‘[a,5]’ : 5
‘[a,5].*’ : 56789

‘\d’ : 任何數字

‘\D’ : 任何非數字

ls=soup.find_all('a', href=re.compile(("^https:.*")))

節點種類

bs4將html轉成樹狀結構後, 每個節點的種類有四種
Tag, NavigableString, BeautifulSoup, Comment

Tag

就是html中的標籤, 如<head>, <body>, <a href=”xxx”>, 標籤加上裏面的內容,  就形成了Tag

print(soup.title)
print(soup.a)
結果 :
<title>天降巨富最新章节列表,全文阅读,UU小说网无弹窗广告</title>
<a href="#" onclick="this.style.behavior='url(#default#homepage)';this.setHomePage('http://www.uuxs.tw/');">将UU小说网设为首页</a>

上面<a>標籤有上百個, 但使用 soup.a只會抓到第一個

每個標籤都有其屬性, 使用下列方式可取得屬性值
soup.a.name : 傳回節點的標籤名稱, 即 a
soup.a.attrs : 傳回節點中所有屬性的list
soup.a[‘class’] : 傳回屬性為 ‘class’ 的值
soup.a[‘class’]=”newClass’ : 設定新的class名
del soup.a[‘class’] : 刪除屬性名

NavigableString

取得每個標籤的內容, 可以使用
soup.a.string

BeautifulSoup

最上面的根節點, 即為整個文件檔的內容

Comment

comment為註解的意思. 比如
<a class=”pokemon” href=”http://abc.com.tw”><!– Super –> </a>
使用soup.a.string, 則會取出去除註解後的字串 “Super”

若不想把註解印出, 需使用如下方式

if type(soup.a.string)!=bs4.element.Comment:
    print(soup.a.string)

爬取天降巨富

下列代碼可將天降巨富小說全數爬回

import codecs
import os
import requests, re
from bs4 import BeautifulSoup
url="http://www.uuxs.tw/ls/22_22102/" page = requests.get(url) page.encoding="utf-8" soup=BeautifulSoup(page.text, "html.parser") nodes=soup.find_all('a') links={} for node in nodes: if node.get('title') is not None: links[node.text]=f'{url}/{node.get("href")}' path='c:/thomas/天降巨富' if not os.path.exists(path): os.mkdir(path) for key in links.keys(): filename=os.path.join(path, f'{key}.txt') r=requests.get(links[key]) r.encoding='utf-8' soup=BeautifulSoup(r.text,'html.parser') content=soup.find(id='content') file=codecs.open(filename, 'w', 'utf-8') file.write(content.text) file.close()

補充說明

開啟文字檔方式, 可以使用open(‘檔名’,’模式’, -1), -1表示緩衝大小採預設值, 常用模式如下
r : 只用於讀取
w : 只用於寫入, 會刪除舊檔案
w+ : 用於讀及寫
a : 附加檔尾寫入

file=open(r'd:\pytest.txt','a',-1)
file.write("第一行\n")
file.write("第二行\n")
file.write("第三行\n")
file.close()

open()並不處理編碼, 字串是什麼碼, 它就寫入什麼碼. 所以如果想要統一使用utf-8, 就要使用codecs

遞迴列印所有節點的內容

下面的方式, 是練習上述的觀念, 使用自已的方式將每個節點的內容印出來

import requests
from bs4 import BeautifulSoup
def analysis(nodes):
    if len(nodes)==1:
        try:
            print("attrs: %s : " % (nodes[0].attrs), end="")
        except:
            print("error:", end="")
        print("%s, %s" % (nodes[0].name, nodes[0].string))
    else:
        for n in nodes:
            if n.name !=None:
                print("--------------------------%s------------------" % (n.name))
                analysis(n.contents)
page = requests.get("http://www.uuxs.tw/ls/22_22102/")
soup=BeautifulSoup(page.text, "html.parser")
analysis(soup.contents)

Yahoo 頭條新聞

Yahoo 新聞超連結都有 class=story-title , 所以我們只要找出網頁中所有符合此條件的標籤,就可以把頭條新聞的資訊抓出來了。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://tw.yahoo.com/')
page.encoding="utf-8"
if page.status_code == requests.codes.ok:#確認是否下載成功
soup = BeautifulSoup(page.text, 'html.parser')
#ls = soup.find_all('a', class='story-title')
ls = soup.find_all('a')
for l in ls:
print("標題:" + l.text)
print("網址:" + l.get('href'))
else:
print("下載失敗")

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *