[Python爬蟲教學]輕鬆學會Python網頁爬蟲與MySQL資料庫的整合方式

Photo by Markus Spiske on Unsplash

在數據爆炸的時代,想要透過資料分析來取得其中有價值的資訊,就需要先獲取大量的資料,並且有效的儲存起來,如此才能夠進行多樣化的應用。而網頁則是最常見的資料蒐集管道,通常會利用API、Open Data(開放資料)或Python網頁爬蟲等技術來進行取得,那又該如何把這些資料儲存起來呢?

本文將以Yahoo奇摩股市為例,分享如何利用Python網頁爬蟲取得關注的股票資料後,存入MySQL資料庫中,讓後續能夠進行資料分析使用。其中的重點包含:

Yahoo奇摩股市網頁分析

建置Python網頁爬蟲

建立MySQL資料庫

建立MySQL資料表

存入爬取的網頁資料

一、Yahoo奇摩股市網頁分析

在進行Python網頁爬蟲開發前,首先來分析一下所要爬取的Yahoo股市網頁結構。前往Yahoo奇摩官方網站,在左側的地方可以看到「股市」的選項,如下圖:

進入Yahoo奇摩股市後,在上方即可看到股票查詢的功能,可以點擊右方的「台股代號查詢」,這時候會跳出一個小視窗,來選擇關注的股票,如下圖:

假設,選擇「2451創建」,則會顯示出當日的行情資料,如下圖:

在這個網頁中,有幾個部份是等一下開發Python網頁爬蟲時,所要取得的資料,分別是右上角的「資料日期」及「當日行情資料」,如下圖:

瞭解想要爬取的目標後,還有一個需求是,在一般情況下,用戶通常會關注一支以上的股票,如果要透過Python網頁爬蟲來自動化取得資料的話,該如何切換到另一支股票呢?

這時候可以看到上方的網址,最後都會帶有股票代號的參數,如下圖:

所以,Python網頁爬蟲只要替換這個股票代號,即可爬取不同股票的當日行情資料。

二、建置Python網頁爬蟲

Yahoo奇摩股市網頁分析好後,接下來就可以進行Python網頁爬蟲的開發,其中本文使用物件導向的設計,詳細的觀念可以參考[Python物件導向]淺談Python類別(Class)文章。

首先,利用以下的指令來安裝Python網頁爬蟲開發時所需的套件:

$ pip install beautifulsoup4

$ pip install requests

$ pip install lxml

其中lxml為BeautifulSoup所支援的HTML解析器,執行速度較快且擁有較佳的容錯能力。

開啟開發工具,本文以Visual Studio Code為例,建立scraper.py檔案,引用beautifulsoup及requests套件,如下範例:

from bs4 import BeautifulSoup

import requests

接著,建立一個股票類別(Stock),其中包含建構式(Constructor)及爬取(Scrape)方法(Method),如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self):

        pass


    #爬取

    def scrape(self):

        pass

由於在初始化股票(Stock)物件時,想要讓用戶能夠傳入多個股票代碼,以便可以利用Python網頁爬蟲取得不同股票的當日行情資料,所以,在建構式(Constructor)的地方使用*args參數,將傳入的多個股票代碼打包成一個元組(Tuple),如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers

        print(self.stock_numbers)


    #爬取

    def scrape(self):

        pass


stock = Stock("2451","2454")

執行結果

('2451', '2454')

範例中,在股票(Stock)物件初始化時,傳入兩個股票代碼,使用*args參數後,就會打包成元組(Tuple),這樣在等一下開發Python網頁爬蟲時,就能夠透過迴圈的方式,讀取元組(Tuple)中的股票代碼,取得對應的當日行情資料。

接下來,在scrape()方法(Method)中,利用requests套件發送GET請求到Yahoo奇摩股市的「2451創建」當日行情網頁,在取得回傳結果(原始碼)後,使用lxml解析器來建立BeautifulSoup物件,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text, "lxml")

在剛剛第一節Yahoo奇摩股市網頁分析時,可以看到要爬取的「當日行情資料」表格中,股票名稱下方有「加到投資組合」的文字,並不需要,為了避免等一下爬取時,還要進行額外的處理,所以在取得網頁的回傳結果(原始碼)後,可以利用replace()方法(Method),將這個文字去掉,如下範例第14行:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

接著,就可以來爬取「資料日期」,點擊右鍵選擇檢查,即可看到HTML原始碼,如下圖:

從上圖可以看到,利用標籤及它的樣式類別(class)即可進行元素的定位,再透過BeautifulSoup套件的getText()方法就可以取得其中的資料日期。

由於取得的資料日期含有空白及「資料日期」文字,這時候就可以使用Python的strip()方法(Method)去除前後的空白後,再利用字串裁切的語法,取得其中的日期資料,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

                "font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

而表格中的「當日行情資料」比照同樣的方式,可以看到如下圖的HTML原始碼:

首先,「當日行情資料」表格位於網頁表格中的第三個,所以,利用BeautifulSoup套件的find_all()方法取得網頁上所有的表格(table)後,取第三個,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

                "font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

tables = soup.find_all("table")[2]  #取得網頁中第三個表格(索引從0開始計算)

定位到「當日行情資料」表格後,就可以再利用find_all()方法(Method)取得底下的所有格子(td),並且只截取前10格,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

                "font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

tables = soup.find_all("table")[2]  #取得網頁中第三個表格(索引從0開始計算)

tds = tables.find_all("td")[0:11]  #取得表格中1到10格

接著,就可以利用Python的Comprehension語法,讀取每一格的資料,經過取得文字(getText)及去除空白(strip)後,存放在元組(Tuple)中,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

                "font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

tables = soup.find_all("table")[2]  #取得網頁中第三個表格(索引從0開始計算)

tds = tables.find_all("td")[0:11]  #取得表格中1到10格

tuple(td.getText().strip() for td in tds))

在這個元組(Tuple)中包含了「當日行情資料」,但是並沒有包含「資料日期」,所以就需要把剛剛所爬取的「資料日期」轉型為元組(Tuple)後,加入到「當日行情資料」元組(Tuple)中,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=2451")

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

                "font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

tables = soup.find_all("table")[2]  #取得網頁中第三個表格(索引從0開始計算)

tds = tables.find_all("td")[0:11]  #取得表格中1到10格

(stock_date,) + tuple(td.getText().strip() for td in tds))

到目前為止,都只是爬取一支股票的資料,當有多支股票時,就需要透過迴圈來重覆執行,並且使用串列(List),將每一支股票的爬取結果打包起來,如下範例:

from bs4 import BeautifulSoup

import requests



class Stock:

    #建構式

    def __init__(self, *stock_numbers):

        self.stock_numbers = stock_numbers


    #爬取

    def scrape(self):

result = list()  #最終結果


for stock_number in self.stock_numbers:

response = requests.get(

"https://tw.stock.yahoo.com/q/q?s=" + stock_number)

soup = BeautifulSoup(response.text.replace("加到投資組合", ""), "lxml")

stock_date = soup.find(

"font", {"class": "tt"}).getText().strip()[-9:]  #資料日期

tables = soup.find_all("table")[2]  #取得網頁中第三個表格(索引從0開始計算)

tds = tables.find_all("td")[0:11]  #取得表格中1到10格

result.append((stock_date,) +

tuple(td.getText().strip() for td in tds))

return result

stock = Stock('2451', '2454')

print(stock.scrape())

執行結果

[  ('109/07/31', '2451創見', '13:30', '66.0', '65.9', '66.0', '△0.2', '551', '65.8', '66.0', '66.0', '65.7'),

  ('109/07/31', '2454聯發科', '14:30', '701', '701', '702', '△18', '14,483', '683', '678', '707', '670')]

特別注意範例中的第18行,網址的最後參數需搭配迴圈替換初始化的股票代碼。

三、建立MySQL資料庫

Python網頁爬蟲開發完成後,接下來,就要準備MySQL資料庫,來儲存爬取的股票「當日行情資料」。

MySQL資料庫的安裝方式可以參考[Python實戰應用]掌握Python連結MySQL資料庫的重要操作文章的第二節。

安裝完成後,開啟MySQL Workbench資料庫管理工具,可以在Windows作業系統上,利用「開始」的搜尋功能,輸入關鍵字來進行開啟,如下圖:

點擊左下角的本地端伺服器,輸入安裝時所設定的密碼,即可登入。接下來,就可以建立Python網頁爬蟲所需的資料庫,如下圖:

填入資料庫名稱及設定utf8字元集,點擊右下角的「Apply」即可,如下圖:

四、建立MySQL資料表

在建立的資料庫(stock)下,點擊「Table(資料表)」右鍵,選擇「Create Table」,如下圖:

填入資料表名稱(Table Name)、設定utf8字元集與建立欄位,如下圖:

其中,market_date(資料日期)及stock_name(股票名稱)需設定為主鍵(Primary Key)。完成後同樣點擊右下角的「Apply」按鈕即可。

五、存入爬取的網頁資料

MySQL資料庫準備好後,現在就可以儲存Python網頁爬蟲所取得的資料,而要讓Python應用程式能夠與MySQL資料庫連接,需要安裝pymysql套件,可以利用以下的指令來安裝:

$ pip install pymysql

開啟scraper.py檔案,引用pymysql套件,並且新增一個save()方法(Method),含有股票資料的參數(stocks)。

接著,在save()方法(Method)裡面設定MySQL資料庫的連線資訊,如下範例:

def save(self, stocks):


    db_settings = {

        "host": "127.0.0.1",

        "port": 3306,

        "user": "root",

        "password": "******",

        "db": "stock",

        "charset": "utf8"

    }

有了連線的資訊,就可以透過pymysql套件的connect()方法(Method)來進行連接,如下範例:

def save(self, stocks):


    db_settings = {

        "host": "127.0.0.1",

        "port": 3306,

        "user": "root",

        "password": "mysql123",

        "db": "stock",

        "charset": "utf8"

    }

    try:

        conn = pymysql.connect(**db_settings)


    except Exception as ex:

        print("Exception:", ex)

而要進行資料庫的操作,就需要有cursor物件,才能夠執行MySQL的新增資料指令,如下範例:

def save(self, stocks):


    db_settings = {

        "host": "127.0.0.1",

        "port": 3306,

        "user": "root",

        "password": "mysql123",

        "db": "stock",

        "charset": "utf8"

    }

    try:

        conn = pymysql.connect(**db_settings)

with conn.cursor() as cursor:

            sql = """INSERT INTO market(

                            market_date,

                            stock_name,

                            market_time,

                            final_price,

                            buy_price,

                            sell_price,

                            ups_and_downs,

                            lot,

                            yesterday_price,

                            opening_price,

                            highest_price,

                            lowest_price)

                    VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""


            for stock in stocks:

                cursor.execute(sql, stock)

            conn.commit()


    except Exception as ex:

        print("Exception:", ex)

範例中,第15行使用with陳述式,在資料庫操作完成後,會自動釋放連線的資源。另外,第31行透過Python迴圈,讀取傳入的股票資料串列(stocks),將每一支股票資料傳入MySQL的新增資料指令(sql)中,最後,利用commit()方法(Method)進行儲存。

完成資料庫的存入方法(save)後,現在,建立一個股票(Stock)物件,傳入兩個公司的股票代碼來進行初始化,接著,呼叫scrape()方法(Method)爬取Yahoo奇摩股市的「當日行情資料」,將回傳的串列(List)結果傳入save()方法(Method),來存入MySQL資料庫中,如下範例:

stock = Stock('2451', '2454')

stock.save(stock.scrape())

執行結果

六、小結

透過本文的實際範例教學,瞭解如何將Python網頁爬蟲所取得的資料有效存入MySQL資料庫中,後續即可利用這些資料來進行分析、圖形化或提供預測的服務等,詳細的程式碼可以參考以下的GitHub網址。另外,您有關注的股票嗎?利用本文的教學來開發一個Python網頁爬蟲,自動化取得股票資料吧。

你可能感兴趣的:([Python爬蟲教學]輕鬆學會Python網頁爬蟲與MySQL資料庫的整合方式)