今年四月因應 LINE Notify 終止服務, 圖書館爬蟲程式改版為 v9, 改用 Telegram 取代 LINE Notify 來傳送訊息, 運作均正常 (但是不太習慣). 之前的相關筆記參考 :
前三篇是 LINE Notify 版; 第四篇是 Telegram 版.
最近完成 render.com 雲端平台測試後打算將此爬蟲程式改版, 目標如下 :
- 近一周將到期的書, 增加抓 "典藏地" 資訊
- 放棄用迴圈查詢全部借書證做法, 改為執行時帶入帳密參數, 一次只查一張借書證
- 借書資訊存入 Mapleboard 的 serverless 平台資料庫 ksml_books 供 LINE Bot 查詢
- 樹莓派定時爬取圖書館網頁更新 serverless 資料庫
本篇先來處理如何抓典藏地資訊, 登入市圖網站的個人書房, 按 F12 開啟開發者頁面, 按 Element 左上角的箭頭可輕易找到書籍典藏地所對應的 HTML 位置 :
按右鍵選擇 Copy/Copy Xpath 即可印證典藏地的位置是 ul[3]li[1] :
//*[@id="center"]/div/div/div[2]/div/div[6]/form/div/div[2]/div[2]/div[1]/ul[3]/li[1]/text()[2]
因此只要在借閱書籍 books 迴圈中增加 book_site 即可 :
book_site=book.find_element(By.XPATH, './ul[3]/li[1]').text
reg=r'典藏地:(\S+)'
item['book_site']=re.findall(reg, book_site)[0]
此處使用正規式 (\S+) 來取出 '典藏地:' 後面的字串, 然後存入 item 字典的 book_site 鍵中. 然後在主程式的 borrow_books 迴圈裡取出 book_site 資訊, 加入借閱書籍資訊中 :
book_site=book['book_site']
... (略) ...
if delta < 0: # 負數=已逾期
msg=f'🅧 {book_name} (逾期 {abs(delta)} 天{state}, {book_site})'
borrow.append(msg)
elif delta == 0: # 0=今天到期
msg=f'⓿ {book_name} (今日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 1: # 1=明天到期
msg=f'❶ {book_name} (明日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 2: # 2=後天到期
msg=f'❷ {book_name} (後天到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif 2 < delta < 8: # 3 天以上一周內到期
msg=f'✦ {book_name} ({book["due_date"]} 到期, '\
f'續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
修改後的完整程式碼如下 :
# ksml_personal_10.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
import re
from datetime import datetime
import time
import requests
import asyncio
from telegram import Bot
import sys
async def telegram_send_text(text):
bot=Bot(token=token)
try:
await bot.send_message(
chat_id=chat_id,
text=text
)
return True
except Exception as e:
print(f'Error sending text: {e}')
return False
def get_books(account, password):
try:
# 登入我的書房
options=Options()
options.add_argument("--headless")
driverpath='geckodriver.exe'
browser=webdriver.Firefox(options=options)
browser.implicitly_wait(60)
browser.get('https://webpacx.ksml.edu.tw/personal/')
loginid=browser.find_element(By.ID, 'logxinid')
loginid.send_keys(account)
pincode=browser.find_element(By.ID, 'pincode')
pincode.send_keys(password)
div_btn_grp=browser.find_element(By.CLASS_NAME, 'btn_grp')
login_btn=div_btn_grp.find_element(By.TAG_NAME, 'input')
login_btn.click()
# 擷取借閱紀錄
div_redblock=browser.find_element(By.CLASS_NAME, 'redblock')
div_redblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
borrow_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
book_site=book.find_element(By.XPATH, './ul[3]/li[1]').text
reg=r'典藏地:(\S+)'
item['book_site']=re.findall(reg, book_site)[0]
reg=r'\d{4}-\d{2}-\d{2}'
due_date=book.find_element(By.XPATH, './ul[4]/li[2]').text
item['due_date']=re.findall(reg, due_date)[0]
due_times=book.find_element(By.XPATH, './ul[5]/li[1]').text
item['due_times']=re.findall(r'\d{1}', due_times)[0]
try:
state=book.find_element(By.XPATH, './ul[6]/li[1]').text
except:
state=''
finally:
if '有人預約' in state:
item['state']=', 有人預約'
else:
item['state']=''
borrow_books.append(item)
browser.back() # 回上一頁
# 擷取預約紀錄
div_blueblock=browser.find_element(By.CLASS_NAME, 'blueblock')
div_blueblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
reserve_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
sequence=book.find_element(By.XPATH, './ul[7]/li[1]').text
if '預約待取' in sequence: # 已到館
item['ready_for_pickup']=True
reg=r'\d{4}-\d{2}-\d{2}'
item['expiration']=re.findall(reg, sequence)[0]
item['sequence']='0'
else: # 預約中
item['ready_for_pickup']=False
item['expiration']=''
item['sequence']=re.findall(r'\d+', sequence)[0]
reserve_books.append(item)
browser.close()
return (borrow_books, reserve_books)
except Exception as e:
print(e)
return None, None
if __name__ == '__main__':
start=time.time()
token='Telegram 權杖'
chat_id='Telegram 聊天室 ID'
if len(sys.argv) != 3:
print('用法: python3 ksml_personal_10.py 帳號 密碼')
sys.exit(1)
# 取得傳入的帳密參數
account=sys.argv[1]
password=sys.argv[2]
# 呼叫 get_books() 取得借書與預約書
borrow_books, reserve_books=get_books(account, password)
# 處理借書
if borrow_books:
borrow=[]
for book in borrow_books:
book_name=book['book_name']
book_site=book['book_site']
due_times=book['due_times']
due_date=book['due_date']
state=book['state']
due_date=datetime.strptime(due_date, '%Y-%m-%d') # 到期日
today_str=datetime.today().strftime('%Y-%m-%d')
today=datetime.strptime(today_str, "%Y-%m-%d")
delta=(due_date-today).days # 計算離到期日還有幾天
if delta < 0: # 負數=已逾期
msg=f'🅧 {book_name} (逾期 {abs(delta)} 天{state}, {book_site})'
borrow.append(msg)
elif delta == 0: # 0=今天到期
msg=f'⓿ {book_name} (今日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 1: # 1=明天到期
msg=f'❶ {book_name} (明日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 2: # 2=後天到期
msg=f'❷ {book_name} (後天到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif 2 < delta < 8: # 3 天以上一周內到期
msg=f'✦ {book_name} ({book["due_date"]} 到期, '\
f'續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
# 傳送借閱書到期摘要至 Telegram
if len(borrow) != 0:
borrow.insert(0, f'\n❖ {account} 的借閱 :')
msg='\n'.join(borrow)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
# 處理預約書
if reserve_books:
reserve=[]
i=0
j=['①', '②', '③', '④', '⑤']
k=['❶', '❷', '❸', '❹', '❺']
# 預約狀態
for book in reserve_books:
book_name=book['book_name']
sequence=book['sequence']
ready_for_pickup=book['ready_for_pickup'] # 已到館
expiration=book['expiration'] # 取書截止日
if ready_for_pickup:
msg=f'{k[i]} {book_name} (已到館, 保留期限 {expiration})'
else:
msg=f'{j[i]} {book_name} (順位 {sequence})'
reserve.append(msg)
i += 1
# 傳送預約書狀態摘要至 Telegram
if len(reserve) != 0:
reserve.insert(0, f'\n❖ {account} 的預約 :')
msg='\n'.join(reserve)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
else:
print('無資料')
end=time.time()
print(f'執行時間:{end-start}')
此程式使用 Selenium 透過 Firefox 瀏覽器以無頭模式爬取圖書館借閱資訊, Firefox 要
更新到最新版, 同時到下列網址下載最新的 Firefox webdriver (x86 64 位元) geckodriver :
執行時要傳入帳密參數 (程式比舊版多了 import sys 來取得參數), 結果如下 :
D:\python\test>python ksml_personal_10.py myhome 123456
訊息傳送成功!
訊息傳送成功!
執行時間:82.54115891456604
可見新版程式已成功地爬取到典藏地資訊了.
2025-10-24 補充 :
雖然市圖爬蟲最新改版目標 (參考 v11) 是改為每日多次爬取後儲存最新資訊於 serverless 平台的資料表 ksml_books, 然後由樹莓派觸發 serverless 每日傳送兩次從資料表取得知最新訊息到 Telegram, 此模式需本地樹莓派與雲端 serverless 平台搭配才能運作, 好處是可以用在 LINE Bot 操作. 但我還是保留全部由樹莓派一手包辦爬取+發送 Telegram 的模式, 將上面於 Windows 上用 Firefox 測試的 v10 程式改為用 Chromnium
# ksml_personal_10_deploy.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import re
from datetime import datetime
import time
import requests
import asyncio
from telegram import Bot
import sys
async def telegram_send_text(text):
bot=Bot(token=token)
try:
await bot.send_message(
chat_id=chat_id,
text=text
)
return True
except Exception as e:
print(f'Error sending text: {e}')
return False
def get_books(account, password):
try:
# 登入我的書房
options=Options()
options.add_argument("--headless")
driverpath='/usr/lib/chromium-browser/chromedriver'
service=Service(driverpath)
browser=webdriver.Chrome(options=options, service=service)
browser.implicitly_wait(60)
browser.get('https://webpacx.ksml.edu.tw/personal/')
loginid=browser.find_element(By.ID, 'logxinid')
loginid.send_keys(account)
pincode=browser.find_element(By.ID, 'pincode')
pincode.send_keys(password)
div_btn_grp=browser.find_element(By.CLASS_NAME, 'btn_grp')
login_btn=div_btn_grp.find_element(By.TAG_NAME, 'input')
login_btn.click()
# 擷取借閱紀錄
div_redblock=browser.find_element(By.CLASS_NAME, 'redblock')
div_redblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
borrow_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
book_site=book.find_element(By.XPATH, './ul[3]/li[1]').text
reg=r'典藏地:(\S+)'
item['book_site']=re.findall(reg, book_site)[0]
reg=r'\d{4}-\d{2}-\d{2}'
due_date=book.find_element(By.XPATH, './ul[4]/li[2]').text
item['due_date']=re.findall(reg, due_date)[0]
due_times=book.find_element(By.XPATH, './ul[5]/li[1]').text
item['due_times']=re.findall(r'\d{1}', due_times)[0]
try:
state=book.find_element(By.XPATH, './ul[6]/li[1]').text
except:
state=''
finally:
if '有人預約' in state:
item['state']=', 有人預約'
else:
item['state']=''
borrow_books.append(item)
browser.back() # 回上一頁
# 擷取預約紀錄
div_blueblock=browser.find_element(By.CLASS_NAME, 'blueblock')
div_blueblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
reserve_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
sequence=book.find_element(By.XPATH, './ul[7]/li[1]').text
if '預約待取' in sequence: # 已到館
item['ready_for_pickup']=True
reg=r'\d{4}-\d{2}-\d{2}'
item['expiration']=re.findall(reg, sequence)[0]
item['sequence']='0'
else: # 預約中
item['ready_for_pickup']=False
item['expiration']=''
item['sequence']=re.findall(r'\d+', sequence)[0]
reserve_books.append(item)
browser.close()
return (borrow_books, reserve_books)
except Exception as e:
print(e)
return None, None
if __name__ == '__main__':
start=time.time()
token='我的 Telegram 權杖'
chat_id='Telegram 聊天室 ID'
if len(sys.argv) != 3:
print('用法: python3 ksml_personal_10_deploy.py 帳號 密碼')
sys.exit(1)
# 取得傳入的帳密參數
account=sys.argv[1]
password=sys.argv[2]
# 呼叫 get_books() 取得借書與預約書
borrow_books, reserve_books=get_books(account, password)
# 處理借書
if borrow_books:
borrow=[]
for book in borrow_books:
book_name=book['book_name']
book_site=book['book_site']
due_times=book['due_times']
due_date=book['due_date']
state=book['state']
due_date=datetime.strptime(due_date, '%Y-%m-%d') # 到期日
today_str=datetime.today().strftime('%Y-%m-%d')
today=datetime.strptime(today_str, "%Y-%m-%d")
delta=(due_date-today).days # 計算離到期日還有幾天
if delta < 0: # 負數=已逾期
msg=f'🅧 {book_name} (逾期 {abs(delta)} 天{state}, {book_site})'
borrow.append(msg)
elif delta == 0: # 0=今天到期
msg=f'⓿ {book_name} (今日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 1: # 1=明天到期
msg=f'❶ {book_name} (明日到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif delta == 2: # 2=後天到期
msg=f'❷ {book_name} (後天到期, 續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
elif 2 < delta < 8: # 3 天以上一周內到期
msg=f'✦ {book_name} ({book["due_date"]} 到期, '\
f'續借次數 {due_times}{state}, {book_site})'
borrow.append(msg)
# 傳送借閱書到期摘要至 Telegram
if len(borrow) != 0:
borrow.insert(0, f'\n❖ {account} 的借閱 :')
msg='\n'.join(borrow)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
# 處理預約書
if reserve_books:
reserve=[]
i=0
j=['①', '②', '③', '④', '⑤']
k=['❶', '❷', '❸', '❹', '❺']
# 預約狀態
for book in reserve_books:
book_name=book['book_name']
sequence=book['sequence']
ready_for_pickup=book['ready_for_pickup'] # 已到館
expiration=book['expiration'] # 取書截止日
if ready_for_pickup:
msg=f'{k[i]} {book_name} (已到館, 保留期限 {expiration})'
else:
msg=f'{j[i]} {book_name} (順位 {sequence})'
reserve.append(msg)
i += 1
# 傳送預約書狀態摘要至 Telegram
if len(reserve) != 0:
reserve.insert(0, f'\n❖ {account} 的預約 :')
msg='\n'.join(reserve)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
else:
print('無資料')
end=time.time()
print(f'執行時間:{end-start}')
然後 crontab 修改為如下格式 :
0 12, 21 * * * /usr/bin/python3 /home/pi/ksml_personal_10_deploy.py 帳號 密碼
每個帳號間隔約 4 分鐘即可.
我將上面程式佈署至鄉下的 Pi 3 發現執行錯誤, 因為 OS 是 Raspbian Strecth 的 Python 是 3.5 版, 不支援 f 字串, 所以我改成用 format() :
# ksml_personal_10_deploy.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import re
from datetime import datetime
import time
import requests
import asyncio
from telegram import Bot
import sys
async def telegram_send_text(text):
bot=Bot(token=token)
try:
await bot.send_message(
chat_id=chat_id,
text=text
)
return True
except Exception as e:
print('Error sending text: '.format(e))
return False
def get_books(account, password):
try:
# 登入我的書房
options=Options()
options.add_argument("--headless")
driverpath='/usr/lib/chromium-browser/chromedriver'
service=Service(driverpath)
browser=webdriver.Chrome(options=options, service=service)
browser.implicitly_wait(60)
browser.get('https://webpacx.ksml.edu.tw/personal/')
loginid=browser.find_element(By.ID, 'logxinid')
loginid.send_keys(account)
pincode=browser.find_element(By.ID, 'pincode')
pincode.send_keys(password)
div_btn_grp=browser.find_element(By.CLASS_NAME, 'btn_grp')
login_btn=div_btn_grp.find_element(By.TAG_NAME, 'input')
login_btn.click()
# 擷取借閱紀錄
div_redblock=browser.find_element(By.CLASS_NAME, 'redblock')
div_redblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
borrow_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
book_site=book.find_element(By.XPATH, './ul[3]/li[1]').text
reg=r'典藏地:(\S+)'
item['book_site']=re.findall(reg, book_site)[0]
reg=r'\d{4}-\d{2}-\d{2}'
due_date=book.find_element(By.XPATH, './ul[4]/li[2]').text
item['due_date']=re.findall(reg, due_date)[0]
due_times=book.find_element(By.XPATH, './ul[5]/li[1]').text
item['due_times']=re.findall(r'\d{1}', due_times)[0]
try:
state=book.find_element(By.XPATH, './ul[6]/li[1]').text
except:
state=''
finally:
if '有人預約' in state:
item['state']=', 有人預約'
else:
item['state']=''
borrow_books.append(item)
browser.back() # 回上一頁
# 擷取預約紀錄
div_blueblock=browser.find_element(By.CLASS_NAME, 'blueblock')
div_blueblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
reserve_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
sequence=book.find_element(By.XPATH, './ul[7]/li[1]').text
if '預約待取' in sequence: # 已到館
item['ready_for_pickup']=True
reg=r'\d{4}-\d{2}-\d{2}'
item['expiration']=re.findall(reg, sequence)[0]
item['sequence']='0'
else: # 預約中
item['ready_for_pickup']=False
item['expiration']=''
item['sequence']=re.findall(r'\d+', sequence)[0]
reserve_books.append(item)
browser.close()
return (borrow_books, reserve_books)
except Exception as e:
print(e)
return None, None
if __name__ == '__main__':
start=time.time()
token='7938146214:AAGoV71AVEygN3e-j-ZfOWUcWSHG1qBp7KE'
chat_id='7742981072'
if len(sys.argv) != 3:
print('用法: python3 ksml_personal_10.py 帳號 密碼')
sys.exit(1)
# 取得傳入的帳密參數
account=sys.argv[1]
password=sys.argv[2]
# 呼叫 get_books() 取得借書與預約書
borrow_books, reserve_books=get_books(account, password)
# 處理借書
if borrow_books:
borrow=[]
for book in borrow_books:
book_name=book['book_name']
book_site=book['book_site']
due_times=book['due_times']
due_date=book['due_date']
state=book['state']
due_date=datetime.strptime(due_date, '%Y-%m-%d') # 到期日
today_str=datetime.today().strftime('%Y-%m-%d')
today=datetime.strptime(today_str, "%Y-%m-%d")
delta=(due_date-today).days # 計算離到期日還有幾天
if delta < 0: # 負數=已逾期
msg='🅧 {} (逾期 {} 天{}, {})'.format(book_name, abs(delta), state, book_site)
borrow.append(msg)
elif delta == 0: # 0=今天到期
msg='⓿ {} (今日到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif delta == 1: # 1=明天到期
msg='❶ {} (明日到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif delta == 2: # 2=後天到期
msg='❷ {} (後天到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif 2 < delta < 8: # 3 天以上一周內到期
msg='✦ {} ({} 到期, '.format(book_name, book["due_date"]) +\
'續借次數 {}{}, {})'.format(due_times, state, book_site)
borrow.append(msg)
# 傳送借閱書到期摘要至 Telegram
if len(borrow) != 0:
borrow.insert(0, '\n❖ {} 的借閱 :'.format(account))
msg='\n'.join(borrow)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
# 處理預約書
if reserve_books:
reserve=[]
i=0
j=['①', '②', '③', '④', '⑤']
k=['❶', '❷', '❸', '❹', '❺']
# 預約狀態
for book in reserve_books:
book_name=book['book_name']
sequence=book['sequence']
ready_for_pickup=book['ready_for_pickup'] # 已到館
expiration=book['expiration'] # 取書截止日
if ready_for_pickup:
msg='{} {} (已到館, 保留期限 {})'.format(k[i], book_name, expiration)
else:
msg='{} {} (順位 {})'.format(j[i], book_name, sequence)
reserve.append(msg)
i += 1
# 傳送預約書狀態摘要至 Telegram
if len(reserve) != 0:
reserve.insert(0, '\n❖ {} 的預約 :'.format(account))
msg='\n'.join(reserve)
if asyncio.run(telegram_send_text(msg)):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
else:
print('無資料')
end=time.time()
print('執行時間:{}'.format(end-start))
執行出現另一個錯誤 :
Traceback (most recent call last):
File "ksml_personal_10_deploy.py", line 11, in <module>
from telegram import Bot
File "/home/pi/.local/lib/python3.5/site-packages/telegram/__init__.py", line 21, in <module>
from .base import TelegramObject
File "/home/pi/.local/lib/python3.5/site-packages/telegram/base.py", line 42
_id_attrs: Tuple[Any, ...] = ()
^
SyntaxError: invalid syntax
詢問 ChatGPT 才知道是因為 Python 3.5 太舊了, python-telegram-bot 雖然能安裝, 但如果使用 Bot 物件傳訊息執行時會出錯, 須改用 HTTP 呼叫; 而且 Cromium driver 也必須用舊版寫法 (不能用 Service), 我重新安裝了 chromium-chromedriver 套件 :
sudo apt-get install -y chromium-chromedriver
然後將程式改為舊版 driver 語法之後就可以在這台 Pi 3 的 Raspbian Stretch 上順利用 Selenium 爬取資料並將結果傳到 Telegram, 程式碼如下 :
# ksml_personal_10_deploy.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import re
from datetime import datetime
import time
import requests
import sys
def telegram_send_text(text):
try:
data={'chat_id': chat_id, 'text': text}
url='https://api.telegram.org/bot{}/sendMessage'.format(token)
r=requests.post(url, data=data)
return True
except Exception as e:
print('Error sending text: '.format(e))
return False
def get_books(account, password):
try:
# 登入我的書房
chrome_options=Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
# 舊版 selenium 用 executable_path
browser=webdriver.Chrome(executable_path="/usr/bin/chromedriver", options=chrome_options)
browser.implicitly_wait(60)
browser.get('https://webpacx.ksml.edu.tw/personal/')
loginid=browser.find_element(By.ID, 'logxinid')
loginid.send_keys(account)
pincode=browser.find_element(By.ID, 'pincode')
pincode.send_keys(password)
div_btn_grp=browser.find_element(By.CLASS_NAME, 'btn_grp')
login_btn=div_btn_grp.find_element(By.TAG_NAME, 'input')
login_btn.click()
# 擷取借閱紀錄
div_redblock=browser.find_element(By.CLASS_NAME, 'redblock')
div_redblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
borrow_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
book_site=book.find_element(By.XPATH, './ul[3]/li[1]').text
reg=r'典藏地:(\S+)'
item['book_site']=re.findall(reg, book_site)[0]
reg=r'\d{4}-\d{2}-\d{2}'
due_date=book.find_element(By.XPATH, './ul[4]/li[2]').text
item['due_date']=re.findall(reg, due_date)[0]
due_times=book.find_element(By.XPATH, './ul[5]/li[1]').text
item['due_times']=re.findall(r'\d{1}', due_times)[0]
try:
state=book.find_element(By.XPATH, './ul[6]/li[1]').text
except:
state=''
finally:
if '有人預約' in state:
item['state']=', 有人預約'
else:
item['state']=''
borrow_books.append(item)
browser.back() # 回上一頁
# 擷取預約紀錄
div_blueblock=browser.find_element(By.CLASS_NAME, 'blueblock')
div_blueblock.click()
books=browser.find_elements(By.CLASS_NAME, 'bookdata')
reserve_books=[]
for book in books:
item=dict()
book_name=book.find_element(By.XPATH, './h2/a').text
item['book_name']=book_name.replace('/', '').strip()
sequence=book.find_element(By.XPATH, './ul[7]/li[1]').text
if '預約待取' in sequence: # 已到館
item['ready_for_pickup']=True
reg=r'\d{4}-\d{2}-\d{2}'
item['expiration']=re.findall(reg, sequence)[0]
item['sequence']='0'
else: # 預約中
item['ready_for_pickup']=False
item['expiration']=''
item['sequence']=re.findall(r'\d+', sequence)[0]
reserve_books.append(item)
browser.close()
return (borrow_books, reserve_books)
except Exception as e:
print(e)
return None, None
if __name__ == '__main__':
start=time.time()
token='7938146214:AAGoV71AVEygN3e-j-ZfOWUcWSHG1qBp7KE'
chat_id='7742981072'
if len(sys.argv) != 3:
print('用法: python3 ksml_personal_10.py 帳號 密碼')
sys.exit(1)
# 取得傳入的帳密參數
account=sys.argv[1]
password=sys.argv[2]
# 呼叫 get_books() 取得借書與預約書
borrow_books, reserve_books=get_books(account, password)
# 處理借書
if borrow_books:
borrow=[]
for book in borrow_books:
book_name=book['book_name']
book_site=book['book_site']
due_times=book['due_times']
due_date=book['due_date']
state=book['state']
due_date=datetime.strptime(due_date, '%Y-%m-%d') # 到期日
today_str=datetime.today().strftime('%Y-%m-%d')
today=datetime.strptime(today_str, "%Y-%m-%d")
delta=(due_date-today).days # 計算離到期日還有幾天
if delta < 0: # 負數=已逾期
msg='🅧 {} (逾期 {} 天{}, {})'.format(book_name, abs(delta), state, book_site)
borrow.append(msg)
elif delta == 0: # 0=今天到期
msg='⓿ {} (今日到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif delta == 1: # 1=明天到期
msg='❶ {} (明日到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif delta == 2: # 2=後天到期
msg='❷ {} (後天到期, 續借次數 {}{}, {})'.format(book_name, due_times, state, book_site)
borrow.append(msg)
elif 2 < delta < 8: # 3 天以上一周內到期
msg=('✦ {} ({} 到期, '.format(book_name, book["due_date"]) +
'續借次數 {}{}, {})'.format(due_times, state, book_site))
borrow.append(msg)
# 傳送借閱書到期摘要至 Telegram
if len(borrow) != 0:
borrow.insert(0, '\n❖ {} 的借閱 :'.format(account))
msg='\n'.join(borrow)
if telegram_send_text(msg):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
# 處理預約書
if reserve_books:
reserve=[]
i=0
j=['①', '②', '③', '④', '⑤']
k=['❶', '❷', '❸', '❹', '❺']
# 預約狀態
for book in reserve_books:
book_name=book['book_name']
sequence=book['sequence']
ready_for_pickup=book['ready_for_pickup'] # 已到館
expiration=book['expiration'] # 取書截止日
if ready_for_pickup:
msg='{} {} (已到館, 保留期限 {})'.format(k[i], book_name, expiration)
else:
msg='{} {} (順位 {})'.format(j[i], book_name, sequence)
reserve.append(msg)
i += 1
# 傳送預約書狀態摘要至 Telegram
if len(reserve) != 0:
reserve.insert(0, '\n❖ {} 的預約 :'.format(account))
msg='\n'.join(reserve)
if telegram_send_text(msg):
print('訊息傳送成功!')
else:
print('訊息傳送失敗!')
else:
print('無資料')
end=time.time()
print('執行時間:{}'.format(end-start))
其中黃底色部分即 Chromium driver 舊版寫法.




沒有留言 :
張貼留言