台彩於 2024/01 月 開始更改網址,並改版網頁內容,本例適用於新版網頁。
簡介
本範例可以爬取台灣大樂透每一期的開獎號碼, 並將日期, 期別及 7 組號碼存入資料庫。
大樂透網址如下
https://www.taiwanlottery.com/lotto/result/lotto649
資料庫結構
大樂透649 資料表格式如下
use cloud; CREATE TABLE `大樂透649` ( `id` int NOT NULL AUTO_INCREMENT, `日期` date NOT NULL, `期數` varchar(9) COLLATE utf8_unicode_ci NOT NULL, `n1` int NOT NULL, `n2` int NOT NULL, `n3` int NOT NULL, `n4` int NOT NULL, `n5` int NOT NULL, `n6` int NOT NULL, `n7` int NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `日期_UNIQUE` (`日期`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci
今彩539 資料表格式如下
use cloud; CREATE TABLE `今彩539` ( `id` int(11) NOT NULL AUTO_INCREMENT, `日期` date DEFAULT NULL, `期數` varchar(9) DEFAULT NULL, `n1` int(11) DEFAULT NULL, `n2` int(11) DEFAULT NULL, `n3` int(11) DEFAULT NULL, `n4` int(11) DEFAULT NULL, `n5` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `日期_UNIQUE` (`日期`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
大樂透649 – Selenium
本代碼適用於台彩 2024/01 新版網頁,可以正常爬取每期的開獎資料。本例從民國 112 年開始爬取,直到目前的時間。
查詢按鈕本身沒有 id,但由 Selenium IDE 操作查詢後,看到裏面使用 css 選擇器名稱定義,所以可以由下代碼抓取。
btn = browser.find_element(By.CSS_SELECTOR,".el-button--primary")
完整代碼如下
import random
import time
from datetime import datetime
import mysql.connector as mysql
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions as EC
def getData(period):
try:
y = browser.find_element(By.ID, "el-id-1024-2")
y.clear()
y.send_keys(period)
btn.click()
WebDriverWait(browser, 20, 0.2).until(
EC.presence_of_element_located(
(By.CLASS_NAME,"period-title")
)
)
soup=BeautifulSoup(browser.page_source, "html.parser")
nodes=soup.find_all("div", class_="special-number")
div_day = browser.find_element(By.CLASS_NAME, "period-date")
div_title = browser.find_element(By.CLASS_NAME, "period-title")
ds=div_day.text.replace("開獎日期:","").split("/")
date=f'{int(ds[0])+1911}-{ds[1]}-{ds[2]}'
title=div_title.text.replace("第","").replace("期","")
data=[date,title]+[int(node.text) for node in nodes]
return data
except Exception as e:
print(e)
return None
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
#options.add_experimental_option('detach', True)
service=Service(ChromeDriverManager().install())
browser = webdriver.Chrome(service=service, options=options)
browser.get("https://www.taiwanlottery.com/lotto/result/lotto649")
WebDriverWait(browser, 20, 0.2).until(
EC.presence_of_element_located(
(By.CLASS_NAME, 'el-button--primary')
)
)
#按鈕在下方,所以目前看不到按鈕,所以也按不到
#最大化才能看到按鈕
browser.maximize_window()
btn=browser.find_element(By.CLASS_NAME, "el-button--primary")
#往下捲到按鈕的位置
#browser.execute_script("arguments[0].scrollIntoView();", btn)
conn=mysql.connect(host="localhost", user="帳號", password="密碼", database="資料庫")
cursor=conn.cursor()
current_year=datetime.now().year-1911
for year in range(112,current_year+1):
index=1
while True:
data=getData(f"{year}000{index:03d}")
if data==None:break
print(data)
cmd = f"insert into 大樂透649 (日期, 期數, n1, n2, n3, n4, n5, n6, n7) values ('{data[0]}','{data[1]}',{data[2]},{data[3]},{data[4]},{data[5]},{data[6]},{data[7]},{data[8]})"
cursor.execute(cmd)
conn.commit()
#time.sleep(random.random())#不睡好像也不會被鎖 ip
index+=1
conn.close()
使用 taiwanlottery 套件
網路上有人開發可以爬取台彩多種資料的套件,請參考 https://github.com/stu01509/TaiwanLotteryCrawler/tree/master?tab=readme-ov-file github說明
首先在專案下新增 taiwanlottery 套件
pip install taiwanlottery
然後新增檔案,撰寫如下代碼即可爬取指定的年月資料
from TaiwanLottery import TaiwanLotteryCrawler
lottery = TaiwanLotteryCrawler()
result = lottery.lotto649(['2023', '06'])
print(result)
結果:
[{'期別': 112000064, '開獎日期': '2023-06-30T00:00:00', '獎號': [6, 22, 26, 29, 32, 43], '特別號': 38}, {'期別': 112000063, '開獎日期': '2023-06-27T00:00:00',....]
大樂透649 及 今彩539 完整代碼
底下是抓取當月份大樂透 649 及今彩539 的完整代碼,並按期別排序後,再寫入資料庫。寫入前會先比對資料庫是否已存在相同的期別,如果期別已存在就會跳過去不儲存。
#!/data/server/auto/crawler/.venv/bin/python3
import datetime
from TaiwanLottery import TaiwanLotteryCrawler
import mysql.connector as mysql
from G import G
lottery = TaiwanLotteryCrawler()
current=datetime.datetime.now()
current_year=current.year
current_month=current.month
conn=mysql.connect(
host=G.ip,
user=G.account,
password=G.password,
database=G.db
)
cursor=conn.cursor()
#大樂透649
cmd=f"select * from 大樂透649 where 日期 like '{current_year}-{current_month:02d}%'"
cursor.execute(cmd)
db_periods=set([r[2] for r in cursor.fetchall()])
data = []
rs = lottery.lotto649([f'{current_year}', f'{current_month:02d}'])
rs.sort(key=lambda d: d['期別'])
for r in rs:
period=str(r['期別'])
date=r['開獎日期'].split('T')[0]
n=r['獎號']
s=r['特別號']
if period not in db_periods:
cmd = f"insert into 大樂透649 (日期, 期數, n1, n2, n3, n4, n5, n6, n7) values ('{date}','{period}',{n[0]},{n[1]},{n[2]},{n[3]},{n[4]},{n[5]},{s})"
print(cmd)
cursor.execute(cmd)
conn.commit()
#今彩539
cmd=f"select * from 今彩539 where 日期 like '{current_year}-{current_month:02d}%'"
cursor.execute(cmd)
db_periods=set([r[2] for r in cursor.fetchall()])
data = []
rs = lottery.daily_cash([f'{current_year}', f'{current_month:02d}'])
rs.sort(key=lambda d: d['期別'])
for r in rs:
period=str(r['期別'])
date=r['開獎日期'].split('T')[0]
n=r['獎號']
if period not in db_periods:
cmd = f"insert into 今彩539 (日期, 期數, n1, n2, n3, n4, n5) values ('{date}','{period}',{n[0]},{n[1]},{n[2]},{n[3]},{n[4]})"
print(cmd)
cursor.execute(cmd)
conn.commit()
conn.close()
舊版完整代碼
以下作法已過時不適用
本代碼適用 2023/12 之前的網頁,目前已全部失效,僅為記錄用。
請注意, BeautifulSoup 查詢 html 標籤的 class 屬性,比如 class=”td-w”,會產生錯誤。這是因為 class 是 Python 的關鍵字,所以要使用 class_=”td-w”。
#!/usr/bin/python3 import time, random from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.wait import WebDriverWait from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.support import expected_conditions as EC import datetime import mysql.connector as mysql from G import G from bs4 import BeautifulSoup def getData(yyyy, mm): yyy=yyyy-1911 browser.get("https://www.taiwanlottery.com.tw/Lotto/Lotto649/history.aspx") radio = browser.find_element(By.ID, 'Lotto649Control_history_radYM') radio.click() select_yy = Select(browser.find_element(By.ID, 'Lotto649Control_history_dropYear')) select_yy.select_by_value(f'{yyy}') select_mm = Select(browser.find_element(By.ID, 'Lotto649Control_history_dropMonth')) select_mm.select_by_value(f'{mm}') btn = browser.find_element(By.ID, 'Lotto649Control_history_btnSubmit') btn.click() rows=[] try: WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.TAG_NAME, "td"))) soup=BeautifulSoup(browser.page_source, "html.parser") tables=soup.find_all('table', class_='td_hm') for table in tables:
#底下的 class_ 是屬性,要加底線,才不會跟關鍵字 class 衝到
#td_w才有資料, tds[0] 是期數,tds[1] 是日期,tds[12:18] 是號碼
tds=table.find_all('td', class_='td_w') date_array=tds[1].text.replace("\n","").split("/") row=[f'{int(date_array[0])+1911}-{date_array[1]}-{date_array[2]}'] row.append(tds[0].text.replace(" ","")) for i in range(12, 19): row.append(tds[i].text) rows.append(row) time.sleep(random.randint(5,8)+random.random()) except: print("time out") return rows options = Options() options.add_argument('--headless') options.add_argument('--disable-gpu')
service=Service(ChromeDriverManager().install()) browser=webdriver.Chrome(service=service, options=options) current=datetime.datetime.now() if current.month>1: months=[current.month-1, current.month] else: months=[current.month] conn=mysql.connect(host=G.ip,user=G.account, password=G.password, database=G.db) cursor = conn.cursor() cmd="insert into 大樂透649 (日期, 期數, n1, n2, n3, n4, n5, n6, n7) values (%s, %s, %s, %s, %s, %s, %s, %s, %s)" for m in months: print(f"Getting Lotto649 for {current.year}/{m:02d}...") rows=getData(current.year, m) if(len(rows))>0: cursor.execute(f"delete from 大樂透649 where 日期 like '{current.year}-{m:02d}%'") conn.commit() cursor.executemany(cmd, rows) conn.commit() cursor.close() conn.close() browser.close() browser.quit()
