一、Selenium+ MySQL爬取並儲存股票資訊
碼雲倉庫:作業4/task1.py · 曹星才/2022級資料採集與融合技術 - 碼雲 - 開源中國
(一)步驟
爬取網站:http://quote.eastmoney.com/center/gridlist.html#hs_a_board
step1:定義spider類,並初始化user_agent(瀏覽器偽裝)、driver(Chrome WebDriver 例項)、data(暫時儲存獲取下來的資料)
def __init__(self):
# 設定 User-Agent,用於偽裝瀏覽器,避免被網站反爬蟲機制遮蔽
user_agent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0"
# 建立瀏覽器配置選項
chrome_options = webdriver.ChromeOptions()
# chrome_options.add_argument('--headless') # 可選:無頭模式,不彈出瀏覽器
# chrome_options.add_argument('--disable-gpu') # 禁用 GPU
chrome_options.add_argument(f"user-agent={user_agent}") # 設定瀏覽器的 User-Agent
# 目標網站 URL
self.url = "http://quote.eastmoney.com/center/gridlist.html#hs_a_board"
# 建立 Chrome WebDriver 例項,並應用之前的配置
self.driver = webdriver.Chrome(options=chrome_options)
# 初始化儲存資料的字典
self.data = {
"hs": { # hs 是一個板塊的識別符號
"xuHao": [],
"guPiaoDaiMa": [],
"guPiaoNingCheng": [],
"zuiXingBaoJia": [],
"zhangDieFu": [],
"zhangDieE": [],
"chengJiaoLiang": [],
"zhenFu": [],
"zuiGao": [],
"zuiDi": [],
"jinKai": [],
"zuoShou": [],
},
"sh": { # sh 是另一個板塊的識別符號
"xuHao": [],
"guPiaoDaiMa": [],
"guPiaoNingCheng": [],
"zuiXingBaoJia": [],
"zhangDieFu": [],
"zhangDieE": [],
"chengJiaoLiang": [],
"zhenFu": [],
"zuiGao": [],
"zuiDi": [],
"jinKai": [],
"zuoShou": [],
},
"sz": { # sz 是第三個板塊的識別符號
"xuHao": [],
"guPiaoDaiMa": [],
"guPiaoNingCheng": [],
"zuiXingBaoJia": [],
"zhangDieFu": [],
"zhangDieE": [],
"chengJiaoLiang": [],
"zhenFu": [],
"zuiGao": [],
"zuiDi": [],
"jinKai": [],
"zuoShou": [],
}
}
step2:確定爬取的形式,先爬取初始頁面(滬深A股)若干頁,再依次點選“上證A股”、“深證A股”板塊對其股票資訊的爬取
step3:找到下一頁的按鈕
程式碼:
self.driver.find_element(By.XPATH, '//*[@id="main-table_paginate"]/a[2]').click()
step4:找到跳轉到“上證A股”、“深證A股”板塊的按鈕
step5:得到程序控制部分函式
注意:因為頁面資料是動態載入的,所以要特別注意等待頁面元素出現時再對資料進行獲取或者是按鈕的點選,這裡我選用了隱式等待和time.sleep()強制等待,將time.sleep()加在載入稍微久一點的頁面之前。至於為什麼不選擇使用顯示等待呢,按理說顯示等待是一個綜合性最好的等待方式?因為頁面的跳轉沒有特定的元素可以等待出現,所以直接用強制等待會好點。
# 處理頁面資料,遍歷不同板塊並提取資料
def process(self, n, hs=True, sh=True, sz=True):
self.driver.implicitly_wait(10) # 設定隱式等待時間為 10 秒
self.driver.get(self.url) # 開啟目標 URL
# 處理滬深 A 股板塊
if hs:
select = 0 # hs 板塊
for i in range(n): # n 代表需要抓取的頁數
try:
self.parse(self.driver, select) # 解析當前頁面的資料
if i == n - 1: # 如果是最後一頁,則停止跳轉
break
else:
# 點選分頁按鈕,跳轉到下一頁
self.driver.find_element(By.XPATH, '//*[@id="main-table_paginate"]/a[2]').click()
time.sleep(2) # 等待 2 秒以確保頁面載入完成
except Exception as e:
print(e)
# 處理上海 A 股板塊
if sh:
select = 1 # sh 板塊
try:
self.driver.find_element(By.XPATH, "//li[@id='nav_sh_a_board']").click()
time.sleep(3)
for i in range(n):
self.parse(self.driver, select)
if i == n - 1:
break
else:
time.sleep(2)
self.driver.find_element(By.XPATH, '//*[@id="main-table_paginate"]/a[2]').click()
time.sleep(3)
except Exception as e:
print(e)
# 處理深圳 A 股板塊
if sz:
select = 2 # sz 板塊
try:
self.driver.find_element(By.XPATH, "//li[@id='nav_sz_a_board']").click()
time.sleep(3)
for i in range(n):
self.parse(self.driver, select)
if i == n - 1:
break
else:
time.sleep(2)
self.driver.find_element(By.XPATH, '//*[@id="main-table_paginate"]/a[2]').click()
time.sleep(3)
except Exception as e:
print(e)
step6:觀察頁面資料儲存結構
step7:獲取每一行tr
trs = driver.find_elements(By.XPATH, '//*[@id="table_wrapper-table"]/tbody/tr')
step8:解析每一行的資料
# 遍歷每一行,將資料提取出來並新增到字典中
for tr in trs:
self.data[data_se]["xuHao"].append(tr.find_element(By.XPATH, "td[1]").text)
self.data[data_se]["guPiaoDaiMa"].append(tr.find_element(By.XPATH, "td[2]").text)
self.data[data_se]["guPiaoNingCheng"].append(tr.find_element(By.XPATH, "td[3]").text)
self.data[data_se]["zuiXingBaoJia"].append(tr.find_element(By.XPATH, "td[5]").text)
self.data[data_se]["zhangDieFu"].append(tr.find_element(By.XPATH, "td[6]").text)
self.data[data_se]["zhangDieE"].append(tr.find_element(By.XPATH, "td[7]").text)
self.data[data_se]["chengJiaoLiang"].append(tr.find_element(By.XPATH, "td[8]").text)
self.data[data_se]["zhenFu"].append(tr.find_element(By.XPATH, "td[10]").text)
self.data[data_se]["zuiGao"].append(tr.find_element(By.XPATH, "td[11]").text)
self.data[data_se]["zuiDi"].append(tr.find_element(By.XPATH, "td[12]").text)
self.data[data_se]["jinKai"].append(tr.find_element(By.XPATH, "td[13]").text)
self.data[data_se]["zuoShou"].append(tr.find_element(By.XPATH, "td[14]").text)
step9:效果展示
step10:將獲取到的資料插入mysql資料庫
# 將資料儲存到 MySQL 資料庫
def mySQL(self, name):
try:
# 連線到 MySQL 資料庫
con = mysql.connector.connect(
host="localhost", # 資料庫地址
user="root", # 資料庫使用者名稱
password="123456", # 資料庫密碼
database="DataAcquisition" # 資料庫名稱
)
cursor = con.cursor()
try:
# 如果表已存在,先刪除
cursor.execute(f"DROP TABLE IF EXISTS {name}")
# 建立新表
sql = f"""
CREATE TABLE IF NOT EXISTS {name} (
xuHao VARCHAR(32) PRIMARY KEY,
guPiaoDaiMa VARCHAR(32),
guPiaoNingCheng VARCHAR(32),
zuiXingBaoJia VARCHAR(32),
zhangDieFu VARCHAR(32),
zhangDieE VARCHAR(32),
chengJiaoLiang VARCHAR(32),
zhenFu VARCHAR(32),
zuiGao VARCHAR(32),
zuiDi VARCHAR(32),
jinKai VARCHAR(32),
zuoShou VARCHAR(32)
)
"""
cursor.execute(sql)
except Exception as e:
print("Error creating table:", e)
# 準備插入資料的 SQL 語句
sql = f"""
INSERT INTO {name} (xuHao, guPiaoDaiMa, guPiaoNingCheng, zuiXingBaoJia, zhangDieFu, zhangDieE, chengJiaoLiang, zhenFu, zuiGao, zuiDi, jinKai, zuoShou)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
try:
# 遍歷資料並插入到資料庫
for i in range(len(self.data[name]["xuHao"])):
# 獲取每一列的資料
xuHao = self.data[name]["xuHao"][i]
guPiaoDaiMa = self.data[name]["guPiaoDaiMa"][i]
guPiaoNingCheng = self.data[name]["guPiaoNingCheng"][i]
zuiXingBaoJia = self.data[name]["zuiXingBaoJia"][i]
zhangDieFu = self.data[name]["zhangDieFu"][i]
zhangDieE = self.data[name]["zhangDieE"][i]
chengJiaoLiang = self.data[name]["chengJiaoLiang"][i]
zhenFu = self.data[name]["zhenFu"][i]
zuiGao = self.data[name]["zuiGao"][i]
zuiDi = self.data[name]["zuiDi"][i]
jinKai = self.data[name]["jinKai"][i]
zuoShou = self.data[name]["zuoShou"][i]
# 執行插入操作
cursor.execute(sql, (xuHao, guPiaoDaiMa, guPiaoNingCheng, zuiXingBaoJia, zhangDieFu, zhangDieE, chengJiaoLiang, zhenFu, zuiGao, zuiDi, jinKai, zuoShou))
except Exception as err:
print("Error inserting data:", err)
# 提交事務並關閉連線
con.commit()
con.close()
except Exception as err:
print("Error:", err)
step11:啟動程式
# 建立爬蟲物件
stock = spider()
# 開始抓取資料,抓取兩頁資料(n=2),同時抓取滬深A股、上證A股、深證A股
stock.process(n=2, hs=True, sh=True, sz=True)
# 獲取抓取的資料
data = stock.getData()
# 將資料儲存到 MySQL 資料庫中
stock.mySQL("hs")
stock.mySQL("sh")
stock.mySQL("sz")
step12:資料庫中檢視結果
🍔滬深A股
🥟上證A股
🍟深證A股
(二)心得
對於本次基於Selenium和MySQL的股票資訊爬取專案,我深入瞭解瞭如何利用Selenium模擬瀏覽器操作來抓取動態載入的網頁資料,並將抓取的資料儲存到MySQL資料庫中。由於目標網頁的資料是動態載入的,我特別注意了等待機制,使用了隱式等待和time.sleep()
來確保頁面載入完成,從而獲取到完整的資料。資料抓取過程中,我透過XPath定位頁面中的股票資訊,將每一行的資料提取並儲存在字典中,包括股票程式碼、名稱、價格、漲跌幅等多個欄位。其次,我將抓取到的資料透過Python的mysql.connector
庫插入到MySQL資料庫中,首先建立資料表,然後批次插入每一條股票記錄。過程中,我還增加了異常處理機制,確保在出現錯誤時不會導致整個程式崩潰,例如在點選分頁按鈕或獲取元素時發生異常時,程式能夠捕獲並輸出錯誤資訊。
儘管如此,爬蟲仍面臨一些最佳化空間。例如,使用time.sleep()
來強制等待並不是最優方案,顯式等待可以更精確地控制頁面載入時間,提高效率,不過該網站沒有合適的顯示等待的元素,所以我並沒有採用顯示等待控制頁面載入時間;另外,隨著資料量的增加,爬取速度和儲存效率也需要進一步最佳化,例如透過併發技術提升抓取速度或使用批次儲存減少記憶體壓力。
總體而言,這次實踐不僅讓我深入瞭解了Selenium和MySQL的應用,還讓我意識到爬蟲開發中的挑戰,尤其是在應對動態載入和反爬蟲機制時需要細緻的技術方案。透過本次專案,我積累了不少經驗,也對未來提升爬蟲效率和穩定性充滿信心。
二、Selenium+ MySQL爬取並儲存中國mooc網課程資源資訊
碼雲倉庫:作業4/task2.py · 曹星才/2022級資料採集與融合技術 - 碼雲 - 開源中國
(一)步驟
爬取網站:https://www.icourse163.org
step1:定義spider類並進行初始化
def __init__(self):
# 設定一個自定義的User-Agent,避免被網站識別為機器人
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
# 建立一個Chrome瀏覽器選項物件
chrome_options = webdriver.ChromeOptions()
# 設定為無頭模式
# chrome_options.add_argument('--headless')
# chrome_options.add_argument('--disable-gpu')
# 使用自定義的User-Agent
chrome_options.add_argument(f"user-agent={user_agent}")
# 設定目標網址
self.url = "https://www.icourse163.org"
# 初始化瀏覽器驅動物件,使用自定義的選項
self.driver = webdriver.Chrome(options=chrome_options)
# 用於儲存抓取的資料
self.data = {
"cousrseID": [], # 課程ID
"courseName": [], # 課程名稱
"courseCollege": [], # 所屬學院
"teacherMain": [], # 主講老師
"teacherS": [], # 其他講師
"number": [], # 學生人數
"progress": [], # 學習進度
"brief": [] # 課程簡介
}
step2:先解決登陸問題,找到登陸按鈕
程式碼:
self.driver.find_element(By.XPATH, "//div[@class='_3uWA6']").click()
step3:查詢並進入iframe
iframe,即內聯框架(Inline Frame),是HTML中的一個元素,它允許在當前HTML頁面中嵌入另一個獨立的HTML文件。這個元素在網頁設計中非常有用,尤其是當需要在一個頁面中顯示另一個頁面的內容時。所有主流瀏覽器都支援iframe標籤。
程式碼:
# 查詢並切換到登入框的iframe
iframe = self.driver.find_element(By.XPATH,
"/html/body/div[13]/div[2]/div/div/div/div/div/div[1]/div/div[1]/div[2]/div[2]/div[1]/div/iframe")
self.driver.switch_to.frame(iframe)
step4:查詢輸入框和登陸按鈕
程式碼:
# 輸入手機號和密碼進行登入
self.driver.find_element(By.XPATH, '//*[@id="phoneipt"]').send_keys("手機號")
self.driver.find_element(By.XPATH, '//*[@id="login-form"]/div/div[4]/div[2]/input[2]').send_keys("密碼")
# 點選登入按鈕
self.driver.find_element(By.XPATH, '//a[@id="submitBtn"]').click()
step5:要注意,登陸後要退出iframe,使driver切回至主頁面
# 切換回主頁面
self.driver.switch_to.default_content()
step6:成功登陸後,想要在輸入框中輸入課程並查詢,就得先同意它的隱私條款,這裡就是要找到同意的button並點選
程式碼:
# 等待隱私條款同意按鈕載入完成並點選
WebDriverWait(self.driver, 10, 0.5).until(
EC.element_to_be_clickable((By.XPATH, '//button[@id="privacy-ok"]'))
)
self.driver.find_element(By.XPATH, '//button[@id="privacy-ok"]').click()
step7:找到搜尋框,並向其輸入關鍵字
程式碼:
# 搜尋課程函式
def search(self, course):
# 向搜尋框輸入課程名稱
self.driver.find_element(By.XPATH, '//input[@class="ant-input"]').send_keys(course)
# 點選搜尋按鈕
self.driver.find_element(By.XPATH, '//span[@class="E3Zsq"]').click()
step8:成功搜尋
step9:接下來就是解析網頁,分析需求,我們需要獲取網課程資源資訊(課程號、課程名稱、學校名稱、主講教師、團隊成員、參加人數、課程進度、課程簡介),其中的課程號沒有在頁面上直接顯示,但是我們可以分析課程的url可以發現,其課程對應的url的一部分就是課程號,比如url"//www.icourse163.org/course/BIT-1001870001?from=searchPage&outVendor=zw_mooc_pcssjg_",其課程號為BIT-1001870001
step10:提取課程號
href = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]/div/div/a[1]').get_attribute("href")
cousrseID = re.search(r'course/([^?&]+)', href).group(1)
step11:其它資訊則使用xpath對頁面解析直接獲取
程式碼:
moocsList = self.driver.find_elements(By.XPATH, '//div[@class="m-course-list"]/div/div')
for mooc in moocsList:
try:
href = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]/div/div/a[1]').get_attribute("href")
cousrseID = re.search(r'course/([^?&]+)', href).group(1)
courseName = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]/div/div/a[1]/span').text
courseCollege = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]/div/div[2]/a[1]').text
teachers = mooc.find_elements(By.XPATH, './/a[@class="f-fc9"]')
teacherMain = teachers[0].text
teacherS = ""
for teacher in teachers[1:]:
teacherS = teacher.text + "、" + teacherS
# print(teacher.text)
if teacherS.endswith("、"):
teacherS = teacherS[:-1]
number = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]//span[@class="hot"]').text
progress = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]//span[@class="txt"]').text
brief = mooc.find_element(By.XPATH, './/div[@class="g-mn1c"]/div/a/span').text
step11.5:進行翻頁處理,找到翻頁的按鈕並進行點選
程式碼:
for i in range(1, page + 1):
self.parse()
if i==page:
break
else:
self.driver.find_element(By.XPATH, '//ul[@class="ux-pager"]/li[5]/a').click()
step12:解析出資料並列印
step13:儲存到MySQL中,編寫插入函式
def mySQL(self):
try:
# 連線到 MySQL 資料庫(修改為你的資料庫配置)
con = mysql.connector.connect(
host="localhost", # 資料庫地址
user="root", # 資料庫使用者名稱
password="123456", # 資料庫密碼
database="DataAcquisition" # 你資料庫的名稱
)
cursor = con.cursor()
try:
# 刪除舊錶(如果存在)
cursor.execute(f"DROP TABLE IF EXISTS mooc")
# 建立表結構
sql = f"""
CREATE TABLE IF NOT EXISTS mooc (
cousrseID VARCHAR(32) PRIMARY KEY,
courseName VARCHAR(32),
courseCollege VARCHAR(32),
teacherMain VARCHAR(32),
teacherS VARCHAR(32),
number_ VARCHAR(32),
progress VARCHAR(32),
brief VARCHAR(256)
)
"""
cursor.execute(sql)
except Exception as e:
print("Error creating table:", e)
# 準備插入資料
sql = f"""
INSERT INTO mooc (cousrseID, courseName, courseCollege, teacherMain, teacherS, number_, progress, brief)
VALUES (%s, %s, %s, %s, %s, %s,%s, %s)
"""
try:
for i in range(len(self.data["cousrseID"])):
cousrseID = self.data["cousrseID"][i]
courseName = self.data["courseName"][i]
courseCollege = self.data["courseCollege"][i]
teacherMain = self.data["teacherMain"][i]
teacherS = self.data["teacherS"][i]
number = self.data["number"][i]
progress = self.data["progress"][i]
brief = self.data["brief"][i]
# 執行 SQL 插入語句
cursor.execute(sql, (cousrseID, courseName, courseCollege, teacherMain, teacherS, number, progress, brief))
except Exception as err:
print("Error inserting data:", err)
# 提交事務並關閉連線
con.commit()
con.close()
except Exception as err:
print("Error:", err)
step14:使用類進行排程,執行程式碼
mooc = spider()
mooc.process(course="爬蟲", page=2)
mooc.mySQL()
step15:效果展示
step16:資料庫中檢視(排版有點醜不要介意🤕)
(二)心得
🍩對於Selenium的應用
首先,Selenium作為一個強大的自動化測試工具,在網頁爬取過程中發揮了至關重要的作用。在爬取MOOC網站的資料時,初步遇到的問題是如何正確模擬使用者登入。由於MOOC網站有複雜的登入框架和iframe結構,我需要透過XPath準確定位登入按鈕及輸入框。尤其是在使用iframe時,必須先切換到相應的iframe內,進行操作後再切換回主頁面,這一過程需要較為細緻的除錯。
🍪對於資料的抓取與解析
抓取課程資訊時,我不僅需要從網頁元素中提取基礎資訊(如課程名、學院、講師、學生人數等),還需要透過分析URL來提取課程ID。這種從URL中提取關鍵資訊的技巧,幫助我規避了直接在頁面上找不到課程ID的難題。
Selenium提供了靈活的定位方式,除了可以直接透過XPath提取資訊,還能透過find_element
或find_elements
等方法獲取單個或多個元素。透過對MOOC課程頁面的分析,我能夠準確提取出每一門課程的詳細資訊,並儲存在一個字典中,待後續儲存到資料庫。
🍰對於翻頁與非同步處理
MOOC網站的課程資訊是分頁顯示的,因此在抓取資料時需要處理翻頁問題。為了能夠抓取多頁資料,我使用了Selenium的翻頁操作,透過找到“下一頁”按鈕並模擬點選,來迴圈抓取每一頁的資料。這個操作非常考驗程式碼的魯棒性,因為如果翻頁過程中遇到元素載入慢,可能會導致錯誤。
透過使用WebDriverWait
和EC.element_to_be_clickable
方法,我確保了每次翻頁操作都能等待頁面完全載入後再進行,避免了因為頁面未完全載入而導致的抓取失敗。
🍫對於資料儲存與MySQL
在完成資料的抓取和解析後,我將結果儲存到MySQL資料庫中。為了避免重複插入資料,我首先刪除了資料庫中已有的表,並建立了一個新的表結構。然後,透過INSERT INTO
語句將抓取到的資料逐條插入資料庫。
這個過程並不複雜,但需要注意資料的準確性和格式問題,例如如何處理空值、特殊字元等。雖然Selenium能夠抓取到頁面上的內容,但在儲存到資料庫時,仍然需要對資料進行一定的清洗和格式化,確保其能夠正確儲存和查詢。
🍭總結
雖然本次實踐的過程中我成功抓取並儲存了資料,但仍然存在一些問題,比如在處理網頁載入緩慢或翻頁時出現的異常情況。在今後的工作中,我將進一步最佳化爬蟲的穩定性和效率,例如透過使用多執行緒技術提高爬取速度,或者加入更強的異常處理機制,提升程式的容錯能力。
總之,這次專案不僅提高了我在自動化爬蟲和資料儲存方面的技能,也讓我深刻認識到爬蟲技術在實際開發中的重要性和挑戰性。
三、Flume日誌採集
(一)步驟
3.1 Python指令碼生成測試資料
3.2 下載安裝並配置Kafka
3.2.1 下載kafka客戶端
3.2.2 校驗下載的客戶端檔案包
3.2.3 安裝kafka執行環境
3.2.4 檢視topic資訊
3.3 安裝Flume客戶端
3.3.1 下載Flume客戶端
3.3.2 校驗下載的客戶端檔案包
3.3.3 重啟Flume服務
3.4 配置Flume採集資料
3.5 MySQL中準備結果表與維度表資料
3.5.1 建立維度表並插入資料
3.5.2 建立Flink作業的結果表
3.6 使用DLI中的Flink作業進行資料分析
3.6.1 測試網路連通性
3.6.2 執行Flink作業
驗證資料分析:
SQL查詢:
3.7 DLV資料視覺化
3.7.1 數字翻拍器及其設定
3.7.2 定時執行資料生成指令碼
10秒後:
(二)心得體會
整個實驗過程讓我更加深入地瞭解了大資料技術的各個方面,尤其是在資料傳輸、實時資料處理與分析、以及視覺化展示等環節的應用。我認識到,搭建一個高效的大資料處理平臺並不僅僅是一個技術問題,它還涉及到如何有效配置資源、如何處理不同系統之間的資料流動與互動,以及如何確保系統在執行過程中的高效與穩定。
透過這次實驗,我不僅掌握了Kafka、Flume、Flink等大資料處理工具的使用,還學會了如何在實際的工作環境中搭建和維護大資料分析系統。此外,我還提升了自己對大資料平臺架構的整體理解,尤其是在雲平臺服務與資料流轉方面的運用。
這次實驗為我今後在大資料領域的學習和工作奠定了堅實的基礎,也激發了我對大資料技術的進一步探索與實踐的興趣。我將繼續學習更多先進的大資料處理技術,以應對未來更復雜的技術挑戰。