網路爬蟲問題解析 --- CSS select 出現 list index out of range


在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碼


以上是我的心得筆記, 希望對大家學習爬蟲有幫助, 有任何意見, 歡迎留言

#css #爬蟲 #Python






你可能感興趣的文章

實作 Redux(二):抽離 store 以及監控數據的變化

實作 Redux(二):抽離 store 以及監控數據的變化

[MTR04] W2 D1 JavaScript 基礎

[MTR04] W2 D1 JavaScript 基礎

DAY34:Human readable duration format

DAY34:Human readable duration format






留言討論