Python爬蟲抓取東方財富網股票數據並實現MySQL數據庫存儲

Python爬蟲能夠說是好玩又好用了。現想利用Python爬取網頁股票數據保存到本地csv數據文件中,同時想把股票數據保存到MySQL數據庫中。需求有了,剩下的就是實現了。html

 

在開始以前,保證已經安裝好了MySQL並須要啓動本地MySQL數據庫服務。提到安裝MySQL數據庫,前兩天在一臺電腦上安裝MySQL5.7時,死活裝不上,老是提示缺乏Visual Studio 2013 Redistributable,可是很疑惑,明明已經安裝了呀,原來問題出在版本上,更換一個版本後就能夠了。小問題大苦惱,不知道有沒有人像我同樣悲催。mysql

 

言歸正傳,啓動本地數據庫服務:sql

    用管理員身份打開「命令提示符(管理員)」,而後輸入「net start mysql57」(我把數據庫服務名定義爲mysql57了,安裝MySQL時能夠修改)就能夠開啓服務了。注意使用管理員身份打開小黑框,若是不是管理員身份,我這裏會提示沒有權限,你們能夠試試。數據庫

 

啓動服務以後,咱們能夠選擇打開「MySQL 5.7 Command Line Client」小黑框,須要先輸入你的數據庫的密碼,安裝的時候定義過,在這裏能夠進行數據庫操做。app

 

下面開始上正餐。函數

 

1、Python爬蟲抓取網頁數據並保存到本地數據文件中fetch

首先導入須要的數據模塊,定義函數:優化

#導入須要使用到的模塊
import urllib
import re
import pandas as pd
import pymysql
import os

#爬蟲抓取網頁函數
def getHtml(url):
    html = urllib.request.urlopen(url).read()
    html = html.decode('gbk')
    return html

#抓取網頁股票代碼函數
def getStackCode(html):
    s = r'<li><a target="_blank" href="http://quote.eastmoney.com/\S\S(.*?).html">'
    pat = re.compile(s)
    code = pat.findall(html)
    return code

 

真正幹活的代碼塊:編碼

Url = 'http://quote.eastmoney.com/stocklist.html'#東方財富網股票數據鏈接地址
filepath = 'D:\\data\\'#定義數據文件保存路徑
#實施抓取
code = getStackCode(getHtml(Url)) 
#獲取全部股票代碼(以6開頭的,應該是滬市數據)集合
CodeList = []
for item in code:
    if item[0]=='6':
        CodeList.append(item)
#抓取數據並保存到本地csv文件
for code in CodeList:
    print('正在獲取股票%s數據'%code)
    url = 'http://quotes.money.163.com/service/chddata.html?code=0'+code+\
        '&end=20161231&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP'
    urllib.request.urlretrieve(url, filepath+code+'.csv')

以上代碼實現了爬蟲網頁抓取股票數據,並保存到本地文件中。關於爬蟲的東西,有不少資料能夠參考,大都是一個套路,再也不多說。同時,本文實現過程當中也參考了不少的網頁資源,在此對全部原創者表示感謝!url

 

先看下抓取的結果。CodeList是抓取到的全部股票代碼的集合,咱們看到它共包含1416條元素,即1416支股票數據。由於股票太多,因此抓取的是以6開頭的,貌似是滬市股票數據(原諒我不懂金融)。

 

 抓取到的股票數據會分別存儲到csv文件中,一隻股票數據一個文件。理論上會有1416個csv文件,和股票代碼數一致。但原諒個人渣網速,下載一個都費勁,也是呵呵了。

 

 打開一個本地數據文件看一下抓取的數據長什麼樣子:

 其實和人工手動下載也沒什麼區別了,硬要說區別,那就是解放了勞動力,提升了生產力(怎麼聽起來像政治?)。

 

2、將數據存儲到MySQL數據庫

 首先創建本地數據庫鏈接:

#數據庫名稱和密碼
name = 'xxxx'
password = 'xxxx'  #替換爲本身的用戶名和密碼
#創建本地數據庫鏈接(須要先開啓數據庫服務)
db = pymysql.connect('localhost', name, password, charset='utf8')
cursor = db.cursor()

其中,數據庫名稱(name)和密碼(password)是安裝MySQL時設置的。

 

建立數據庫,專門用來存儲本次股票數據:

#建立數據庫stockDataBase,若是存在則跳過
sqlSentence1 = "create database if not exists stockDataBase"
cursor.execute(sqlSentence1)#選擇使用當前數據庫
sqlSentence2 = "use stockDataBase;"
cursor.execute(sqlSentence2)

在首次運行的時候通常都會正常建立數據庫,但若是再次運行,因數據庫已經存在,那麼跳過建立,繼續往下執行。建立好數據庫後,選擇使用剛剛建立的數據庫,在該數據庫中存儲數據表。

 

下面看具體的存儲代碼:

#獲取本地文件列
fileList = os.listdir(filepath)
#
依次對每一個數據文件進行存儲 for fileName in fileList: data = pd.read_csv(filepath+fileName, encoding="gbk") #建立數據表,若是數據表已經存在,會跳過繼續執行下面的步驟print('建立數據表stock_%s'% fileName[0:6]) sqlSentence3 = "create table if not exists stock_%s" % fileName[0:6] + "(日期 date, 股票代碼 VARCHAR(10), 名稱 VARCHAR(10), 收盤價 float,\
最高價 float, 最低價 float, 開盤價 float, 前收盤 float, 漲跌額 float, 漲跌幅 float, 換手率 float,\
成交量 bigint, 成交金額 bigint, 總市值 bigint, 流通市值 bigint)
" cursor.execute(sqlSentence3)#迭代讀取表中每行數據,依次存儲(整表存儲還沒嘗試過) print('正在存儲stock_%s'% fileName[0:6]) length = len(data) for i in range(0, length): record = tuple(data.loc[i]) #插入數據語句 try: sqlSentence4 = "insert into stock_%s" % fileName[0:6] + "(日期, 股票代碼, 名稱, 收盤價, 最高價, 最低價, 開盤價,\
前收盤, 漲跌額, 漲跌幅, 換手率, 成交量, 成交金額, 總市值, 流通市值) \
values ('%s',%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
" % record #獲取的表中數據很亂,包含缺失值、Nnone、none等,插入數據庫須要處理成空值 sqlSentence4 = sqlSentence4.replace('nan','null').replace('None','null').replace('none','null') cursor.execute(sqlSentence4) except:#若是以上插入過程出錯,跳過這條數據記錄,繼續往下進行 break

 

 代碼並不複雜,只要注意其中幾個點就行了。

1.邏輯層次:

    包含兩層循環,外層循環是對股票代碼的循環,內層循環是對當前股票的每一條記錄的循環。說白了就是按照股票一支一支的存儲,對於每一支股票,按照它每日的記錄一條一條的存儲。是否是很簡單很暴力?是的!徹底沒有考慮更加優化的方式。

2.讀取本地數據文件的編碼方式:

    使用'gbk'編碼,默認應該是'utf8',但好像不支持中文。

3.建立數據表:

    一樣的,若是數據表已經存在(判斷是否存在if not exists),則跳過建立,繼續執行下面的步驟(會繼續存儲)。有個問題是,有可能數據重複存儲,能夠選擇跳過存儲或者只存儲最新數據。我在這裏沒有考慮太多額外的處理。其次,指定字段格式,後邊幾個字段成交量、成交金額、總市值、流通市值,由於數據較大,選擇使用bigint類型。

4.沒有指定數據表的主鍵:

    最初是打算使用日期做爲主鍵的,後來發現獲取到的數據中居然包含重複日期的數據,這就打破了主鍵的惟一性,會出bug的,而後我也沒有多去思考數據文件的內容,也不會進一步使用這些個數據,也就圖省事直接不設置主鍵了。

5.構造sql語句sqlSentence4:

    該過程實現中,直接把股票數據記錄tuple了,而後使用字符串格式化(%操做符)。形成的精度問題沒有多考慮,不知道會不會產生什麼樣的影響。%s有的上邊帶着' ',是爲了在sql語句中表示字符串。其中有一個%s',只有右邊有單引號,匹配的是股票代碼,只有一邊單引號,這是由於從數據文件中讀取到的字符串已經包含了左邊的單引號,左邊不須要再添加了。這是數據文件格式的問題,爲了表示文本形式預先使用了單引號。

 6.異常值處理:

    文本文件中,包含有空值、None、none等不標準化數據,這裏所有替換爲null了,即數據庫的空值。

 

完成MySQL數據庫數據存儲後,須要關閉數據庫鏈接:

#關閉遊標,提交,關閉數據庫鏈接
cursor.close()
db.commit()
db.close()

不關閉數據庫鏈接,就沒法在MySQL端進行數據庫的查詢等操做,至關於數據庫被佔用。

 

3、MySQL數據庫查詢

#從新創建數據庫鏈接
db = pymysql.connect('localhost', name, password, 'stockDataBase')
cursor = db.cursor()
#查詢數據庫並打印內容
cursor.execute('select * from stock_600000')
results = cursor.fetchall()
for row in results:
    print(row)
#關閉
cursor.close()
db.commit()
db.close()

以上逐條打印,會凌亂到死的。也能夠在MySQL端查看,先選中數據庫:use stockDatabase;,而後查詢:select * from stock_600000;,結果大概就是下面這個樣子了:

 

4、完整代碼

    實際上,整個事情完成了兩個相對獨立的過程:1.爬蟲獲取網頁股票數據並保存到本地文件;2.將本地文件數據儲存到MySQL數據庫。並無直接的考慮把從網頁上抓取到的數據實時(或者經過一個臨時文件)扔進數據庫,跳過本地數據文件這個過程。這裏只是嘗試着去實現了一下這件事情,代碼沒有作任何的優化考慮。自己不實際去使用,只是樂趣而已,差很少先這樣。哈哈~~

 

#導入須要使用到的模塊
import urllib
import re
import pandas as pd
import pymysql
import os

#爬蟲抓取網頁函數
def getHtml(url):
    html = urllib.request.urlopen(url).read()
    html = html.decode('gbk')
    return html

#抓取網頁股票代碼函數
def getStackCode(html):
    s = r'<li><a target="_blank" href="http://quote.eastmoney.com/\S\S(.*?).html">'
    pat = re.compile(s)
    code = pat.findall(html)
    return code
    
#########################開始幹活############################
Url = 'http://quote.eastmoney.com/stocklist.html'#東方財富網股票數據鏈接地址
filepath = 'C:\\Users\\Lenovo\\Desktop\\data\\'#定義數據文件保存路徑
#實施抓取
code = getStackCode(getHtml(Url)) 
#獲取全部股票代碼(以6開頭的,應該是滬市數據)集合
CodeList = []
for item in code:
    if item[0]=='6':
        CodeList.append(item)
#抓取數據並保存到本地csv文件
for code in CodeList:
    print('正在獲取股票%s數據'%code)
    url = 'http://quotes.money.163.com/service/chddata.html?code=0'+code+\
        '&end=20161231&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP'
    urllib.request.urlretrieve(url, filepath+code+'.csv')


##########################將股票數據存入數據庫###########################

#數據庫名稱和密碼
name = 'xxxx'
password = 'xxxx'  #替換爲本身的帳戶名和密碼
#創建本地數據庫鏈接(須要先開啓數據庫服務)
db = pymysql.connect('localhost', name, password, charset='utf8')
cursor = db.cursor()
#建立數據庫stockDataBase
sqlSentence1 = "create database stockDataBase"
cursor.execute(sqlSentence1)#選擇使用當前數據庫
sqlSentence2 = "use stockDataBase;"
cursor.execute(sqlSentence2)

#獲取本地文件列表
fileList = os.listdir(filepath)
#依次對每一個數據文件進行存儲
for fileName in fileList:
    data = pd.read_csv(filepath+fileName, encoding="gbk")
   #建立數據表,若是數據表已經存在,會跳過繼續執行下面的步驟print('建立數據表stock_%s'% fileName[0:6])
    sqlSentence3 = "create table stock_%s" % fileName[0:6] + "(日期 date, 股票代碼 VARCHAR(10),     名稱 VARCHAR(10),\
                       收盤價 float,    最高價    float, 最低價 float, 開盤價 float, 前收盤 float, 漲跌額    float, \
                       漲跌幅 float, 換手率 float, 成交量 bigint, 成交金額 bigint, 總市值 bigint, 流通市值 bigint)"
    cursor.execute(sqlSentence3)
    except:
        print('數據表已存在!')

    #迭代讀取表中每行數據,依次存儲(整表存儲還沒嘗試過)
    print('正在存儲stock_%s'% fileName[0:6])
    length = len(data)
    for i in range(0, length):
        record = tuple(data.loc[i])
        #插入數據語句
        try:
            sqlSentence4 = "insert into stock_%s" % fileName[0:6] + "(日期, 股票代碼, 名稱, 收盤價, 最高價, 最低價, 開盤價, 前收盤, 漲跌額, 漲跌幅, 換手率, \
            成交量, 成交金額, 總市值, 流通市值) values ('%s',%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" % record
            #獲取的表中數據很亂,包含缺失值、Nnone、none等,插入數據庫須要處理成空值
            sqlSentence4 = sqlSentence4.replace('nan','null').replace('None','null').replace('none','null') 
            cursor.execute(sqlSentence4)
        except:
            #若是以上插入過程出錯,跳過這條數據記錄,繼續往下進行
            break

#關閉遊標,提交,關閉數據庫鏈接
cursor.close()
db.commit()
db.close()


###########################查詢剛纔操做的成果##################################

#從新創建數據庫鏈接
db = pymysql.connect('localhost', name, password, 'stockDataBase')
cursor = db.cursor()
#查詢數據庫並打印內容
cursor.execute('select * from stock_600000')
results = cursor.fetchall()
for row in results:
    print(row)
#關閉
cursor.close()
db.commit()
db.close()
相關文章
相關標籤/搜索