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()