BeautifulSoup 解析網頁
BeautifulSoup解析網頁
前言
BeautifulSoup 模組是爬蟲程式設計師非常常用工具,本文使用許多實例,從最基礎開始講解,這將是未來進入更高深網路爬蟲的基石。
大綱
- 解析網頁使用BeautifulSoup 模組
- 建立 BeautifulSoup 物件
- 基本HTML文件解析 - 從簡單開始
- 去除標籤回傳文字屬性 text
- 傳回找尋的第一個符合的標籤 find()
- 傳回找尋所有符合的標籤 find_all()
- 認識 HTML 元素內容屬性與 getText()
- HTML 屬性的搜尋
- select()
- 標籤字串的 get()
- 其它 HTML 文件解析
- 爬取項目清單文件
- 爬取自定義清單文件
- 爬取表格文件
- find_next_sibling() 和 find_previous_sibling()
- parent()
- parent() 搭配使用 find_next_sibling()、find_next_siblings()、find_previous_sibling()、find_previous_siblings()
- 網路爬蟲實戰 圖片下載
- 網路爬蟲實戰 找出台灣彩券公司最新一期威力彩開獎結果
- 網路爬蟲實戰 列出Yahoo 焦點新聞標題和超連結
- IP 偵測網站FileFab
解析網頁使用 BeautifulSoup 模組
從前面章節,讀者應該已經了解如何下載網頁 HTML 原始檔案,也應該對網頁的基本架構有基本的認識,本節要介紹的是使用 BeautifulSoup4 模組解析 HTML 文件。目前這個模組是第四版,模組名稱是 beautifulsoup4,以下列方式安裝:
pip install beautifulsoup4
雖然安裝是 beautifulsoup4 但是導入模組時,用下列方式:
import bs4
建立 BeautifulSoup 物件
可使用下列語法建立 BeautifulSoup 物件
htmlFile = requests.get('https://icook.tw/') # 下載愛料理網頁內容
objSoup = bs4.BeautifulSoup(htmlFile.text, 'lxml') # lxml 是解析HTML文件方式
上述是下載愛料理網頁內容為例,當網頁下載後,將網頁內容的 Response 物件傳送給 bs4.BeautifulSoup() 方法,就可以建立 BeautifulSoup 物件了。至於另一個參數 "lxml",目的是註明解析HTML文件的方法,常用有下列方法:
- 'html.parser' : 這是老舊的方法(3.2.2 版本以前),相容性比較不好。
- 'lxml' : 速度快,相容性佳,本樹採用此方法。使用時需要安裝 lxml。
- pip install lxml
- 'html5lib' : 速度較慢,但解析能力強,需另外安裝 html5lib。
- pip install html5lib
範例 pythonBS4-01.py : 解析 https://icook.tw/ 網頁,主要是列出資料型態。
# pythonBS4-01.py
import requests, bs4
htmlFile = requests.get('https://icook.tw/') # 下載愛料理網頁內容
objSoup = bs4.BeautifulSoup(htmlFile.text, 'lxml') # lxml 是解析HTML文件方式
print("列印BeautifulSoup物件資料型態 : ", type(objSoup)) # 列印串列方法
執行結果
列印 BeautifulSoup 物件資料型態 : <class 'bs4.BeautifulSoup'>
從上述我們獲得了 BeautifulSoup 的資料型態,表示我們獲得初步的成果。
基本HTML文件解析 - 從簡單開始
真實世界的網頁是很複雜的,所以我們先從一個簡單的 HTML 文件開始解析網頁。在程式 pythonBS4-01.py 第 5 行的第一個參數 htmlFile.txt 是網頁內容的 Response 物件,我們可以在工作目錄中放置一個簡單的 HTML 文件,然後先學習使用 BeautifulSoup 解析此文件。
範例 htmlExampleBS4-02.html : 簡單的 HTML 文件。
<!doctype html>
<html lang="zh-Hant-TW">
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>HTML 文件基礎架構</title>
<style>
header {
background-color: rgb(218, 180, 231);
min-height: 100px;
}
main {
background-color: rgb(239, 238, 238);
min-height: 300px;
}
footer {
min-height: 80px;
}
</style>
</head>
<body>
<header class="d-flex justify-content-center align-items-center">
<h1 class="text-center" id='header'>這是表頭區塊</h1>
</header>
<main>
<h1 class="text-center">這是主要內容區</h1>
<section class="d-flex justify-content-center align-items-center">
<div class="card" style="width: 18rem;">
<img src="media/components/user-1.jpg" class="card-img-top" alt="card-1">
<div class="card-body">
<p class="card-text text-center" data-author='author1'>人物介紹-1</p>
</div>
</div>
<div class="card" style="width: 18rem;">
<img src="media/components/user-2.jpg" class="card-img-top" alt="card-2">
<div class="card-body">
<p class="card-text text-center" data-author='author2'>人物介紹-2</p>
</div>
</div>
<div class="card" style="width: 18rem;">
<img src="media/components/user-3.jpg" class="card-img-top" alt="card-2">
<div class="card-body">
<p class="card-text text-center" data-author='author3'>人物介紹-2</p>
</div>
</div>
</section>
</main>
<footer class="bg-dark text-white d-flex justify-content-center align-items-center">
<h3 class="text-center">這是表尾</h3>
</footer>
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
</body>
</html>
執行結果
範例 htmlExample-1.html 之 Tag 標籤的節點圖,依順序由上到下,由左到右,如下所示 :
範例 pythonBS4-02.py : 分析 htmlExample-1.html 文件,列出物件類型。
# pythonBS4-02.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
print("列印BeautifulSoup物件資料型態 : ", type(objSoup))
執行結果
列印BeautifulSoup物件資料型態 : <class 'bs4.BeautifulSoup'>
去除標籤回傳文字屬性 text
範例 pythonBS4-03.py : 分析 htmlExample-1.html 文件,列出標籤 <title> 內容。
# pythonBS4-03.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
print("列印title = ", objSoup.title)
print("title內容 = ", objSoup.title.text)
執行結果
列印title = <title>HTML 文件基礎架構</title>
title內容 = HTML 文件基礎架構
傳回找尋的第一個符合的標籤 find()
這個方法可以找尋 HTML 文件內第一個符合的標籤內容,例如 find('h1') 是找到第一個 h1 標籤,如果找到了,就傳回該標籤字串,我們可以使用 text 或是 string 屬性獲得內容,如果沒找到,就傳回 None。
範例 pythonBS4-04.py : 傳回第一個 <h1> 內容。
# pythonBS4-04.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find('h1')
print("資料型態 = ", type(objTag))
print("列印Tag = ", objTag)
print("Text屬性內容 = ", objTag.text)
print("String屬性內容 = ", objTag.string)
執行結果
資料型態 = <class 'bs4.element.Tag'> 列印Tag = <h1 class="text-center">這是表頭區塊</h1> Text屬性內容 = 這是表頭區塊 String屬性內容 = 這是表頭區塊
傳回找尋所有符合的標籤 find_all()
這個方法可以尋找HTML文件內所有符合的標籤內容,例如 : find_all('h1') 是找所有 h1 標籤,如果找到就回傳所有 h1 的標籤串列,如果沒找到就傳回空串列。
範例 pythonBS4-05.py : 傳回所有 <h1> 內容。
# pythonBS4-05.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all('h1')
print("資料型態 = ", type(objTag)) # 列印資料型態
print("列印Tag串列 = ", objTag) # 列印串列
print("以下是列印串列元素 : ")
for data in objTag: # 列印串列元素內容
print(data.text)
執行結果
資料型態 = <class 'bs4.element.ResultSet'> 列印Tag串列 = [<h1 class="text-center">這是表頭區塊</h1>, <h1 class="text-center">這是主要內容區</h1>] 以下是列印串列元素 : 這是表頭區塊 這是主要內容區
find_all() 基本上是使用迴圈方式尋找所有符合的標籤節點,我們可以使用參數限制尋找的節點數:
- limit = n #限制尋找最多 n 個標籤。
- recursive = False #限制尋找次一層次的標籤。
範例 pythonBS4-05-1.py : 傳回所有 <h1> 內容,限制尋找最多 1 個標籤。
# pythonBS4-05-1.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all('h1', limit=1)
print("資料型態 = ", type(objTag)) # 列印資料型態
print("列印Tag串列 = ", objTag) # 列印串列
print("以下是列印串列元素 : ")
for data in objTag: # 列印串列元素內容
print(data.text)
執行結果
資料型態 = <class 'bs4.element.ResultSet'> 列印Tag串列 = [<h1 class="text-center">這是表頭區塊</h1>] 以下是列印串列元素 : 這是表頭區塊
認識 HTML 元素內容屬性與 getText()
HTML 元素內容的屬性有三種
- textContent : 元素內容,不含任何標籤。
- innerHTML : 元素內容,包含內容與標籤,但不含本身的標籤碼。
- outerHTML : 元素內容,包含內容與標籤,也包含本身的標籤碼。
例如,有個元素如下:
<p>尋找HTML文件內所有符合 h1 標籤<b>find_all('h1')</b></p>
則上述3個屬性的觀念與內容如下 :
- textContent : find_all('h1')。
- innerHTML : 尋找HTML文件內所有符合 h1 標籤<b>find_all('h1')</b>。
- outerHTML : <p>尋找HTML文件內所有符合 h1 標籤<b>find_all('h1')</b></p>。
當使用 BeautifulSoup 模組解析 HTML 文件,如果傳回是串列時,也可以配合索引應用 getText() 取得串列元素內容,所取得的內容是 textContent。 意義與 "回傳文字屬性 text" 之屬性相同。
範例 pythonBS4-06.py : 使用 getText(),擴充設計 範例 pythonBS4-03.py。
# pythonBS4-06.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all('h1')
print("資料型態 = ", type(objTag)) # 列印資料型態
print("列印Tag串列 = ", objTag) # 列印串列
print("\n使用Text屬性列印串列元素 : ")
for data in objTag: # 列印串列元素內容
print(data.text)
print("\n使用getText()方法列印串列元素 : ")
for data in objTag:
print(data.getText())
執行結果
資料型態 = <class 'bs4.element.ResultSet'> 列印Tag串列 = [<h1 class="text-center">這是表頭區塊</h1>, <h1 class="text-center">這是主要內容區</h1>] 使用Text屬性列印串列元素 : 這是表頭區塊 這是主要內容區 使用getText()方法列印串列元素 : 這是表頭區塊 這是主要內容區
HTML 屬性的搜尋
我們可以根據 HTML 標籤屬性搜尋,可以參考以下範例 :
範例 pythonBS4-06-1.py : 搜尋第一個含有 id='header' 的標籤節點。
# pythonBS4-06-1.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find(id='header')
print(objTag)
print(objTag.text)
執行結果
<h1 class="text-center" id="header">這是表頭區塊</h1> 這是表頭區塊
範例 pythonBS4-06-2.py : 搜尋所有含有 class_='card-text' 的標籤節點。
# pythonBS4-06-2.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all(class_='card-text')
for tag in objTag:
print(tag)
print(tag.text)
執行結果
<p class="card-text text-center">人物介紹-1</p> 人物介紹-1 <p class="card-text text-center">人物介紹-2</p> 人物介紹-2 <p class="card-text text-center">人物介紹-2</p> 人物介紹-2
使用 attrs 參數, 搜尋 自訂義 屬性 data-* 之類的標籤,這相當於將屬性用字典方式傳遞給 attrs 參數。
範例 pythonBS4-06-3.py : 使用 attrs 參數, 搜尋 自訂義 屬性 data-* 之類的標籤。
# pythonBS4-06-3.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
tag = objSoup.find(attrs={'data-author':'author1'})
print(tag)
print(tag.text)
執行結果
<p class="card-text text-center" data-author="author1">人物介紹-1</p> 人物介紹-1
使用 find() 或 find_all() 執行 CSS 搜尋
class 由於是 Python 的保留字,所以用 find() 或 find_all() 搜尋 CSS 類別時,可以使用 class_ 代表節點或是直接省略。
範例 pythonBS4-06-4.py : 使用 class_和直接省略方式搜尋 CSS 類別的標籤。
# pythonBS4-06-4.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
tag = objSoup.find('p', class_='card-text')
print(tag)
print(tag.getText())
print('-'*70)
tag = objSoup.find('p', 'card-text')
print(tag)
print(tag.text)
執行結果
<p class="card-text text-center" data-author="author1">人物介紹-1</p> 人物介紹-1 ---------------------------------------------------------------------- <p class="card-text text-center" data-author="author1">人物介紹-1</p> 人物介紹-1
使用正則表達式搜尋部分字串符合的標籤
範例 pythonBS4-06-5.py : 使用正則表達式搜尋部分字串符合的標籤。
# pythonBS4-06-5.py
import bs4
import re
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
tag = objSoup.find('h1', class_=re.compile('text'))
print(tag)
print(tag.text)
執行結果
<h1 class="text-center" id="header">這是表頭區塊</h1> 這是表頭區塊
select()
select() 主要是以 CSS 選擇器( selector )的觀念尋找元素,如果找到回傳的是串列 list ,select() 的特色是一次可以尋找所有相符的元素,如果找不到則傳回空串列。下列是使用實例 :
- objSoup.select('p') : 尋找所有 <p> 標籤的元素。
- objSoup.select('img') : 尋找所有 <img> 標籤的元素。
- objSoup.select('.card') : 尋找所有 CSS class 屬性為 card 的元素。
- objSoup.select('.#author') : 尋找所有 CSS id 屬性為 author 的元素。
- objSoup.select('p .card') : 尋找所有<p> 標籤且 CSS class 屬性為 card 的元素。
- objSoup.select('p #author') : 尋找所有<p> 標籤且 CSS id 屬性為 author 的元素。
- objSoup.select('p b') : 尋找所有<p> 標籤內 的 <b> 元素。
- objSoup.select('p>b') : 尋找所有<p> 標籤內 的直接 <b> 元素,中間沒有其他元素。
- objSoup.select('input[name]') : 尋找所有<input> 標籤且有屬性為 name 的元素。
- objSoup.select('a{link1}') : 尋找所有<a> 標籤內容為 link1 的元素。
範例 pythonBS4-07.py : 使用正則表達式搜尋部分字串符合的標籤。
# pythonBS4-07.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#header')
print("資料型態 = ", type(objTag)) # 列印資料型態
print("串列長度 = ", len(objTag)) # 列印串列長度
print("元素資料型態 = ", type(objTag[0])) # 列印元素資料型態
print("元素內容 = ", objTag[0].getText()) # 列印元素內容
執行結果
資料型態 = <class 'bs4.element.ResultSet'> 串列長度 = 1 元素資料型態 = <class 'bs4.element.Tag'> 元素內容 = 這是表頭區塊
如果將元素傳給 str() ,則回傳的是含開始與結束的標籤字串。
範例 pythonBS4-08.py : 將解析的串列傳給 str() 並列印出來。
# pythonBS4-08.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#header')
print("列出串列元素的資料型態 = ", type(objTag[0]))
print(objTag[0])
print("列出str()轉換過的資料型態 = ", type(str(objTag[0])))
print(str(objTag[0]))
執行結果
列出串列元素的資料型態 = <class 'bs4.element.Tag'> <h1 class="text-center" id="header">這是表頭區塊</h1> 列出str()轉換過的資料型態 = <class 'str'> <h1 class="text-center" id="header">這是表頭區塊</h1>
範例 pythonBS4-09.py : 將解析的串列應用 attrs 列印出所有屬性的字典資料型態。
# pythonBS4-09.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#header')
print(type(objTag[0].attrs))
print(str(objTag[0].attrs))
執行結果
<class 'dict'>
{'class': ['text-center'], 'id': 'header'}
標籤字串的 get()
假設我們要搜尋 <img> 標籤,請參考以下範例。
範例 pythonBS4-10.py : 搜尋 <img> 標籤,並列印出結果。
# pythonBS4-10.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
imgTag = objSoup.select('img')
print("含<img>標籤的串列長度 = ", len(imgTag))
for img in imgTag:
print(img)
執行結果
含<img>標籤的串列長度 = 3 <img alt="card-1" class="card-img-top" src="media/components/user-1.jpg"/> <img alt="card-2" class="card-img-top" src="media/components/user-2.jpg"/> <img alt="card-2" class="card-img-top" src="media/components/user-3.jpg"/>
<img> 這是一個插入圖片的標籤,沒有結束標籤,所以沒有內文,如果讀者嘗試使用 text 屬性列印內容,將看不到任何結果。<img> 這對網路爬蟲設計是很重要的,因為可以由此獲得網頁的圖檔資料,從上述執行結果可以看到,對我們而言很重要的是 <img> 標籤內的屬性 src,這個屬性設定的圖片的路徑。這個時候我們可以使用標籤字串的 img.get() 取得或是使用 img[‘src’] 方式取得路徑。
範例 pythonBS4-11.py : 擴充範例 pythonBS4-10.py,取得所有圖檔的路徑。
# pythonBS4-11.py
import bs4
htmlFile = open('htmlExampleBS4-02.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
imgTag = objSoup.select('img')
print("含<img>標籤的串列長度 = ", len(imgTag))
for img in imgTag:
print("列印標籤串列 = ", img)
print("列印圖檔 = ", img.get('src'))
print("列印圖檔 = ", img['src'])
執行結果
列印標籤串列 = <img alt="card-1" class="card-img-top" src="media/components/user-1.jpg"/> 列印圖檔 = media/components/user-1.jpg 列印圖檔 = media/components/user-1.jpg 列印標籤串列 = <img alt="card-2" class="card-img-top" src="media/components/user-2.jpg"/> 列印圖檔 = media/components/user-2.jpg 列印圖檔 = media/components/user-2.jpg 列印標籤串列 = <img alt="card-2" class="card-img-top" src="media/components/user-3.jpg"/> 列印圖檔 = media/components/user-3.jpg 列印圖檔 = media/components/user-3.jpg
上述程式最重要的是 第10行 img.get(‘src’) 和 第11行 img[‘src’] 這兩個方法,可以取得標籤字串的 src 屬性內容。在程式範例 pythonBS4-08.py 中,曾經說明標籤字串與純字串不同,就是在這裡,純字串無法呼叫 get() 方法執行上述將圖檔字串取出 。
其他 HTML 文件解析
爬取項目清單文件
範例 htmlExampleBS4-03.html : 有關項目清單之簡單 HTML 文件。
<!doctype html>
<html lang="zh-Hant-TW">
<html>
<head>
<meta charset="utf-8">
<title>htmlExampleBS4-03.html</title>
</head>
<body>
<h1>台灣旅遊景點排名</h1>
<ol type="a">
<li>故宮博物院</li>
<li>日月潭</li>
<li>阿里山</li>
</ol>
<h2>台灣夜市排名</h2>
<ol type="A">
<li>士林夜市</li>
<li>永康夜市</li>
<li>逢甲夜市</li>
</ol>
<h2>台灣人口排名</h2>
<ol type="i">
<li>新北市</li>
<li>台北市</li>
<li>桃園市</li>
</ol>
<h2>台灣最健康大學排名</h2>
<ol type="I">
<li>明志科大</li>
<li>台灣體院</li>
<li>台北體院</li>
</ol>
</body>
</html>
執行結果
範例 pythonBS4-11-1.py : 爬取台灣最健康大學排名,這程式會列印出標題和最健康學校清單。
# pythonBS4-11-1.py
import bs4
htmlFile = open('htmlExampleBS4-03.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
titleobj = objSoup.find_all('h2') # h2標題
print(titleobj[2].text)
itemobj = objSoup.find('ol', type='I') # type='I'
items = itemobj.find_all('li')
for item in items:
print(item.text)
執行結果
台灣最健康大學排名 明志科大 台灣體院 台北體院
爬取自定義清單文件
範例 htmlExampleBS4-04.html : 有關自定義清單簡單 HTML 文件。
<!doctype html>
<html lang="zh-Hant-TW">
<html>
<head>
<meta charset="utf-8">
<title>htmlExampleBS4-04.html</title>
</head>
<body>
<h1>國家首都資料表</h1>
<dl>
<dt>Washington</dt>
<dd>美國首都</dd>
<dt>Tokyo</dt>
<dd>日本首都</dd>
<dt>Paris</dt>
<dd>法國首都</dd>
</dl>
</body>
</html>
執行結果
範例 pythonBS4-11-2.py : 爬取國家首都資料,這程式會以字典方式列印出國家、首都資料。
# pythonBS4-11-2.py
import requests, bs4
url = 'htmlExampleBS4-04.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
mycity = []
cityobj = objSoup.find('dl')
cities = cityobj.find_all('dt')
for city in cities:
mycity.append(city.text) # mycity串列
mycountry = []
countryobj = objSoup.find('dl')
countries = countryobj.find_all('dd')
for country in countries:
mycountry.append(country.text) # mycountry串列
print("國家 = ", mycountry)
print("首都 = ", mycity)
data = dict(zip(mycountry, mycity))
print(data) # 字典顯示結果
執行結果
國家 = ['美國首都', '日本首都', '法國首都']
首都 = ['Washington', 'Tokyo', 'Paris']
{'美國首都': 'Washington', '日本首都': 'Tokyo', '法國首都': 'Paris'}
爬取表格文件
範例 htmlExampleBS4-05.html : 簡單的 HTML 表格文件。
<!doctype html>
<html lang="zh-Hant-TW">
<html>
<head>
<meta charset="utf-8">
<title>htmlExampleBS4-05.html</title>
<style>
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<table>
<thead>
<!--建立表頭 -->
<tr>
<th colspan="3">聯合國水資源中心</th>
</tr>
<tr>
<th>河流名稱</th>
<th>國家</th>
<th>洲名</th>
</tr>
</thead>
<tbody>
<!-- 建立表格本體 -->
<tr>
<td>長江</td>
<td>中國</td>
<td>亞洲</td>
</tr>
<tr>
<td>尼羅河</td>
<td>埃及</td>
<td>非洲</td>
</tr>
<tr>
<td>亞馬遜河</td>
<td>巴西</td>
<td>南美洲</td>
</tr>
</tbody>
<tfoot>
<!-- 建立表尾 -->
<tr>
<td colspan="3">製表2017年5月30日</td>
</tr>
</tfoot>
</table>
</body>
</html>
執行結果
範例 pythonBS4-11-3.py : 爬取河川資料,這程式會以字典方式列印出國家、河川資料。
# pythonBS4-11-3.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRiver = [] # 河川
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
for table in tables:
river = table.find('td')
myRiver.append(river.text)
myCountry = [] # 國家
for table in tables:
countries = table.find_all('td')
country = countries[1]
myCountry.append(country.text)
print("國家 = ", myCountry)
print("河川 = ", myRiver)
data = dict(zip(myCountry, myRiver))
print(data) # 字典顯示結果
執行結果
國家 = ['中國', '埃及', '巴西']
河川 = ['長江', '尼羅河', '亞馬遜河']
{'中國': '長江', '埃及': '尼羅河', '巴西': '亞馬遜河'}
find_next_sibling() 和 find_previous_sibling()
在範例 pythonBS4-11-3.py 中,我們使用索引方式取得表格本體的國家資訊,然後儲存在 myCountry[] 串列中,其實爬蟲處理中,我們可以使用以下兩種方式:
- find_next_sibling() # 同父節點的下一個同階層的節點。
- find_previous_sibling() # 同父節點的上一個同階層的節點
範例 pythonBS4-11-4.py : 使用 find_next_sibling() 修改 範例 pythonBS4-11-3.py。
# pythonBS4-11-4.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRiver = [] # 河川
myCountry = [] # 國家
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
for table in tables:
river = table.find('td')
myRiver.append(river.text)
country = river.find_next_sibling('td') # 下一個節點
myCountry.append(country.text)
print("國家 = ", myCountry)
print("河川 = ", myRiver)
data = dict(zip(myCountry, myRiver))
print(data) # 字典顯示結果
執行結果
國家 = ['中國', '埃及', '巴西']
河川 = ['長江', '尼羅河', '亞馬遜河']
{'中國': '長江', '埃及': '尼羅河', '巴西': '亞馬遜河'}
範例 pythonBS4-11-5.py : 使用 find_next_sibling() 和 find_previous_sibling() 方法,建立洲別和河川的字典。
# pythonBS4-11-5.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRiver = [] # 河川
myState = [] # 洲名
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
for table in tables:
countries = table.find_all('td')
country = countries[1] # 國家節點
river = country.find_previous_sibling('td') # 前一個節點
myRiver.append(river.text)
state = country.find_next_sibling('td') # 下一個節點
myState.append(state.text)
print("洲名 = ", myState)
print("河川 = ", myRiver)
data = dict(zip(myState, myRiver))
print(data)
執行結果
洲名 = ['亞洲', '非洲', '南美洲']
河川 = ['長江', '尼羅河', '亞馬遜河']
{'亞洲': '長江', '非洲': '尼羅河', '南美洲': '亞馬遜河'}
find_next_siblings() 和 find_previous_siblings()
- find_next_siblings() # 同父節點的下一個同階層的所有節點。
- find_previous_siblings() # 同父節點的上一個同階層的所有節點
範例 pythonBS4-11-6.py : 使用 find_next_siblings() 和 find_previous_siblings() 方法,爬取同階層的以下所有節點,然後再爬取第3個節點的前面所有的2個節點。
# pythonBS4-11-6.py
import requests, bs4
url = 'htmlExampleBS4-03.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
titleObj = objSoup.find('h2') # h2標題
title = titleObj.find_next_siblings('h2') # 下一系列節點
print('find_next_siblings = ', title)
titleObj = objSoup.find_all('h2')
title = titleObj[2].find_previous_siblings('h2') # 前一系列節點
print('find_previous_siblings = ', title)
執行結果
find_next_siblings = [<h2>台灣人口排名</h2>, <h2>台灣最健康大學排名</h2>] find_previous_siblings = [<h2>台灣人口排名</h2>, <h2>台灣夜市排名</h2>]
parent()
目前階層的節點,移至上層的復節點。
範例 pythonBS4-11-7.py : 先列出尼羅河,再爬取並印出父節點。
# pythonBS4-11-7.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRiver = [] # 河川
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
river = tables[1].find('td')
print(river.text)
river_parent = river.parent()
print(river_parent)
執行結果
尼羅河 [<td>尼羅河</td>, <td>埃及</td>, <td>非洲</td>]
parent() 搭配使用 find_next_sibling()、find_next_siblings()、find_previous_sibling()、find_previous_siblings()
- parent.find_next_sibling() # 上移至父節點爬取父階層的前一個節點
- parent.find_next_siblings() # 上移至父節點爬取父階層之前所有的節點
- parent.find_previous_sibling() # 上移至父節點爬取父階層的後一個節點
- parent.find_previous_siblings() # 上移至父節點爬取父階層之後所有的節點
範例 pythonBS4-11-8.py : 先列出尼羅河,再爬取並印出父節點的前一行節點,然後後一行節點。
# pythonBS4-11-8.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRiver = [] # 河川
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
river = tables[1].find('td')
print(river.text)
previous_row = river.parent.find_previous_sibling()
print(previous_row)
next_row = river.parent.find_next_sibling()
print(next_row)
執行結果
尼羅河 <tr> <td>長江</td> <td>中國</td> <td>亞洲</td> </tr> <tr> <td>亞馬遜河</td> <td>巴西</td> <td>南美洲</td> </tr>
範例 pythonBS4-11-9.py : 先到長江,先到父階層,然後印出之後的所有節點。再到亞馬遜河,然後印出之前的所有節點。
# pythonBS4-11-9.py
import requests, bs4
url = 'htmlExampleBS4-05.html'
htmlFile = open(url, encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
myRver = [] # 河川
tableObj = objSoup.find('table').find('tbody')
tables = tableObj.find_all('tr')
river = tables[0].find('td')
print(river.text)
previous_rows = river.parent.find_next_siblings()
print(previous_rows)
print('-'*70)
river = tables[2].find('td')
print(river.text)
next_rows = river.parent.find_previous_siblings()
print(next_rows)
執行結果
江 [<tr> <td>尼羅河</td> <td>埃及</td> <td>非洲</td> </tr>, <tr> <td>亞馬遜河</td> <td>巴西</td> <td>南美洲</td> </tr>] ---------------------------------------------------------------------- 亞馬遜河 [<tr> <td>尼羅河</td> <td>埃及</td> <td>非洲</td> </tr>, <tr> <td>長江</td> <td>中國</td> <td>亞洲</td> </tr>]
網路爬蟲實戰 圖片下載
我們已經用 HTML 文件來解說網路爬蟲的基本原理了,其實在真實的網路世界一切比上述複雜多了。
範例 pythonBS4-12.py : 到上奇資訊的網站下載所有的圖片,並放到目錄 "outputPythonBS4" 內。
# pythonBS4-12.py
import bs4, requests, os
url = 'https://www.grandtech.com/' # 上奇資訊網頁
html = requests.get(url)
print("網頁下載中 ...")
html.raise_for_status() # 驗證網頁是否下載成功
print("網頁下載完成")
destDir = 'outputPythonBS4' # 設定未來儲存圖片的資料夾
if os.path.exists(destDir) == False:
os.mkdir(destDir) # 建立資料夾供未來儲存圖片
objSoup = bs4.BeautifulSoup(html.text, 'lxml') # 建立BeautifulSoup物件
imgTag = objSoup.select('img') # 搜尋所有圖片檔案
print("搜尋到的圖片數量 = ", len(imgTag)) # 列出搜尋到的圖片數量
if len(imgTag) > 0: # 如果有找到圖片則執行下載與儲存
for i in range(len(imgTag)): # 迴圈下載圖片與儲存
imgUrl = imgTag[i].get('src') # 取得圖片的路徑
print("%s 圖片下載中 ... " % imgUrl)
finUrl = url + imgUrl # 取得圖片在Internet上的路徑
print("%s 圖片下載中 ... " % finUrl)
picture = requests.get(finUrl) # 下載圖片
picture.raise_for_status() # 驗證圖片是否下載成功
print("%s 圖片下載成功" % finUrl)
# 先開啟檔案, 再儲存圖片
pictFile = open(os.path.join(destDir, os.path.basename(imgUrl)), 'wb')
for diskStorage in picture.iter_content(10240):
pictFile.write(diskStorage)
pictFile.close()
範例 pythonBS4-13.py : 到網站下載所有的圖片,並放到目錄 "outputPythonBS4" 內,使用偽裝伺服器的 header 宣告。
# pythonBS4-13.py
import bs4, requests, os
headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
Safari/537.36', }
url = 'http://aaa.24ht.com.tw/' # 這個伺服器會擋住網頁
html = requests.get(url, headers=headers)
print("網頁下載中 ...")
html.raise_for_status() # 驗證網頁是否下載成功
print("網頁下載完成")
destDir = 'outputPythonBS4' # 設定儲存資料夾
if os.path.exists(destDir) == False:
os.mkdir(destDir) # 建立目錄供未來儲存圖片
objSoup = bs4.BeautifulSoup(html.text, 'lxml') # 建立BeautifulSoup物件
imgTag = objSoup.select('img') # 搜尋所有圖片檔案
print("搜尋到的圖片數量 = ", len(imgTag)) # 列出搜尋到的圖片數量
if len(imgTag) > 0: # 如果有找到圖片則執行下載與儲存
for i in range(len(imgTag)): # 迴圈下載圖片與儲存
imgUrl = imgTag[i].get('src') # 取得圖片的路徑
print("%s 圖片下載中 ... " % imgUrl)
finUrl = url + imgUrl # 取得圖片在Internet上的路徑
print("%s 圖片下載中 ... " % finUrl)
picture = requests.get(finUrl, headers=headers) # 下載圖片
picture.raise_for_status() # 驗證圖片是否下載成功
print("%s 圖片下載成功" % finUrl)
# 先開啟檔案, 再儲存圖片
pictFile = open(os.path.join(destDir, os.path.basename(imgUrl)), 'wb')
for diskStorage in picture.iter_content(10240):
pictFile.write(diskStorage)
pictFile.close() # 關閉檔案
網路爬蟲實戰 找出台灣彩券公司最新一期威力彩開獎結果
範例 pythonBS4-14.py : 找出威力彩最新一期開獎結果,在程式第12行先找出 Class 是 "contents_box02" ,因為我們發現這裡面有包含最新一期開獎結果。程式第13行發現總共有4組串列,程式第14-15行則列出此4組串列。觀察這4組串列,發現威力彩是在第1組串列中,所以程式第18行 :
- dataTag[0] 即要找出第1串列
- find_all : 找出 所有 div 標籤,並且屬性的 class 是 ball_tx ball_green。
# pythonBS4-14.py
import bs4, requests
url = 'http://www.taiwanlottery.com.tw'
html = requests.get(url)
print("網頁下載中 ...")
html.raise_for_status() # 驗證網頁是否下載成功
print("網頁下載完成")
objSoup = bs4.BeautifulSoup(html.text, 'lxml') # 建立BeautifulSoup物件
dataTag = objSoup.select('.contents_box02') # 尋找class是contents_box02
print("串列長度", len(dataTag))
for i in range(len(dataTag)): # 列出含contents_box02的串列
print(dataTag[i])
# 找尋開出順序與大小順序的球
balls = dataTag[0].find_all('div', {'class':'ball_tx ball_green'})
print("開出順序 : ", end='')
for i in range(6): # 前6球是開出順序
print(balls[i].text, end=' ')
print("\n大小順序 : ", end='')
for i in range(6,len(balls)): # 第7球以後是大小順序
print(balls[i].text, end=' ')
# 找出第二區的紅球
redball = dataTag[0].find_all('div', {'class':'ball_red'})
print("\n第二區 :", redball[0].text)
執行結果
網頁下載中 ... 網頁下載完成 串列長度 4 <div class="contents_box02"> <div id="contents_logo_02"></div><div class="contents_mine_tx02"><span class="font_black15">110/10/11 第110000081 期 </span><span class="font_red14"><a href="Result_all.aspx#01">開獎結果</a></span></div><div class="contents_mine_tx04">開出順序<br/>大小順序<br/>第二區</div><div class="ball_tx ball_green">24 </div><div class="ball_tx ball_green">33 </div><div class="ball_tx ball_green">15 </div><div class="ball_tx ball_green">12 </div><div class="ball_tx ball_green">38 </div><div class="ball_tx ball_green">05 </div><div class="ball_tx ball_green">05 </div><div class="ball_tx ball_green">12 </div><div class="ball_tx ball_green">15 </div><div class="ball_tx ball_green">24 </div><div class="ball_tx ball_green">33 </div><div class="ball_tx ball_green">38 </div><div class="ball_red">02 </div> </div> <div class="contents_box02"> .....中間省略..... </div> 開出順序 : 24 33 15 12 38 05 大小順序 : 05 12 15 24 33 38 第二區 : 02
網路爬蟲實戰 列出Yahoo 焦點新聞標題和超連結
首先我們需要使用 Chrome 解析 Yahoo 的網頁,請進入 Yahoo 網站,點選焦點新聞,將滑鼠移至第一條焦點新聞,然後按一下滑鼠右鍵,執行檢查指令,可以進入 Chrome 開發工具模式,我們可以找出焦點新聞皆有CSS 內容為 story-title 的 class。有了上述的觀念,我們就可以設計爬蟲程式了。
請留意每一條焦點新聞皆是在 <a> 元素內,同時在 class 有許多屬性,其中一定有 story-title ,可以利用這個特性找出所有的焦點新聞。至於屬性內則是標題,如下所示
範例 pythonBS4-15.py : 列出 Yahoo 焦點新聞標題和超連結。
# pythonBS4-15.py
import requests, bs4,re
htmlFile = requests.get('https://tw.yahoo.com/')
objSoup = bs4.BeautifulSoup(htmlFile.text, 'lxml')
headline_news = objSoup.find_all('a', class_='story-title')
for h in headline_news:
print("焦點新聞 : " + h.text)
print("新聞網址 : " + h.get('href'))
IP 偵測網站FileFab
進入此網站後,點選滑鼠右鍵,選擇 "檢查",進入 Chrome 開發工具,發現以下資訊
有了上述資訊,我們就可以設計爬蟲程式了。
範例 pythonBS4-16.py : 列出自己的 IP,程式第13行 strip() 方法,會將字串頭尾的空白、換行符號去掉。
# pythonBS4-16.py
import requests
import bs4
# 使用自己的IP
headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
Safari/537.36', }
url = 'http://ip.filefab.com/index.php'
htmlFile = requests.get(url, headers=headers)
objSoup = bs4.BeautifulSoup(htmlFile.text, 'lxml')
ip = objSoup.find('h1', id='ipd')
print(ip.text.strip())
執行結果
Your IP address: 180.177.109.201
參考資料
特色、摘要,Feature、Summary:
關鍵字、標籤,Keyword、Tag:
- Web-Crawler,Data-Mining,Data-Science,Python,
留言
張貼留言
Aron阿龍,謝謝您的留言互動!