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阿龍,謝謝您的留言互動!