在Python學習網路的過程中, 發現使用 CSS select時, 常會發生 list index out of range 的錯誤訊息, 這裡將我解析的過程記錄下來, 供大家參考:
1. 爬蟲目標
台灣股市資訊網 的各股的股利政策
股利政策的表格拆成二部份
表格一
表格二
2.觀察網頁
利用Chrome瀏覽器的檢查功能, 來觀察網頁內容
滑鼠游標停在表格 2020的位置, 按滑鼠的功能鍵, 出現選單, 點選 檢查
, 會出現右邊的視窗, 可以觀察網頁的內容
3.發出請求
分析內容發現是一個 html 格式的靜態網頁, 我們利用 Python 的套件來發出請求, 程式片段如下:
import requests
from bs4 import BeautifulSoup
url = 'https://goodinfo.tw/StockInfo/StockDividendPolicy.asp?STOCK_ID=2884'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}
resp = requests.get(url, headers=headers)
# 設定編碼為 utf-8 避免中文亂碼問題
resp.encoding = 'utf-8'
# 根據 HTTP header 的編碼解碼後的內容資料(ex. UTF-8),若該網站沒設定可能會有中文亂碼問題。所以通常會使用 resp.encoding 設定
raw_html = resp.text
# 將 HTML 轉成 BeautifulSoup 物件
soup = BeautifulSoup(raw_html, 'html.parser')
4.解析內容
soup內容是一整串的 html 語法的資料, 需要透過解析網頁的語法, 並使用 CSS select 來分離出所需的表格內容
這裡使用Chrome瀏覽器的檢查功能, 在右邊的檢查視窗發現表格 2000 的 html 語法位置, 按滑鼠的功能鍵, 出現選單, 點選
Copy
, 在點選Copy selector
我們可以得到一串
#divDetail > table > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(1) > nobr > b
的CSS select語法再將這一段 CSS select語法, 用Python套件中的功能來看是否有取得我們要的資料
print(soup.select('#divDetail > table > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(1) > nobr > b'))
- 會發現結果是 [] , 為一個空的list, 如果沒有檢查, 而直接用語法取出內容(如下語法)
print(soup.select('#divDetail > table > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(1) > nobr > b')[0].text)
- 此時就會出現
list index out of range
的錯誤訊息 (因為是空的 list)
- 那麼再取出 2019, 2018, 2017...2004..1999 這些格子的位置呢?
2020:#divDetail > table > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(1) > nobr > b
2019:#divDetail > table > tbody:nth-child(2) > tr:nth-child(2) > td:nth-child(1) > nobr > b
2018:#divDetail > table > tbody:nth-child(2) > tr:nth-child(3) > td:nth-child(1) > nobr > b
2017:#divDetail > table > tbody:nth-child(2) > tr:nth-child(4) > td:nth-child(1) > nobr > b
2004:#divDetail > table > tbody:nth-child(4) > tr:nth-child(1) > td:nth-child(1) > nobr > b
1999:#divDetail > table > tbody:nth-child(4) > tr:nth-child(6) > td:nth-child(1) > nobr > b
- 查驗的結果都是空的list []
- 再觀察 股利合計那一個欄位的變化
2020:#divDetail > table > tbody:nth-child(2) > tr:nth-child(1) > td:nth-child(8)
2019:#divDetail > table > tbody:nth-child(2) > tr:nth-child(2) > td:nth-child(8)
2018:#divDetail > table > tbody:nth-child(2) > tr:nth-child(3) > td:nth-child(8)
- 查驗的結果都是空的list []
為何直接 Copy selector 不能用?
經過測試後發現改寫如下即可:
2020: #divDetail > table > tr:nth-child(2) > td:nth-child(1) > nobr > b
2019: #divDetail > table > tr:nth-child(3) > td:nth-child(1) > nobr > b
2018: #divDetail > table > tr:nth-child(4) > td:nth-child(1) > nobr > b
2017: #divDetail > table > tr:nth-child(5) > td:nth-child(1) > nobr > b
2004: #divDetail > table > tr:nth-child(19) > td:nth-child(1) > nobr > b
1999: #divDetail > table > tr:nth-child(24) > td:nth-child(1) > nobr > b
完成測試如下:
推論: thead,tbody 的 html語法 無法適用於 Python CSS select 的程式寫法, 去掉即可, 因為少了一層, 故 tr:nth-child(數字) 內的數字就需重新排列
可以不要每去都去數表格內年份在那一個tr:nth-child(數字)嗎?
語法如下:
soup.select('#divDetail > table > tr > td:nth-child(1) > nobr > b')
就會發現 年份形成一個連續的list
[<b>2020</b>, <b>2019</b>, <b>2018</b>, <b>2017</b>, <b>2016</b>, <b>2015</b>,
<b>2014</b>, <b>2013</b>, <b>2012</b>, <b>2011</b>, <b>2010</b>, <b>2009</b>,
<b>2008</b>, <b>2007</b>, <b>2006</b>, <b>2005</b>, <b>2004</b>, <b>2003</b>,
<b>2002</b>, <b>2001</b>, <b>2000</b>, <b>1999</b>, <b>1998</b>, <b>1997</b>,
<b>1996</b>, <b>1995</b>]
# 要取出 2020時
soup.select('#divDetail > table > tr > td:nth-child(1) > nobr > b')[0].text
# 要取出 2019時
soup.select('#divDetail > table > tr > td:nth-child(1) > nobr > b')[1].text
# 其餘年份以此類推, 或者用公式來計算
同樣的股利合計的欄位, 可以改寫成
soup.select('#divDetail > table > tr > td:nth-child(8)')
2020年3月12日補充:
利用Google瀏覽器Chrome的CSS select copy時, 會自動加上 tbody 等語法, 所以要仔細核對原始的html碼
以上是我的心得筆記, 希望對大家學習爬蟲有幫助, 有任何意見, 歡迎留言