Python爬蟲(chóng)可以說(shuō)是好玩又好用了?,F(xiàn)想利用Python爬取網(wǎng)頁(yè)股票數(shù)據(jù)保存到本地csv數(shù)據(jù)文件中,,同時(shí)想把股票數(shù)據(jù)保存到MySQL數(shù)據(jù)庫(kù)中。需求有了,,剩下的就是實(shí)現(xiàn)了,。 在開(kāi)始之前,保證已經(jīng)安裝好了MySQL并需要啟動(dòng)本地MySQL數(shù)據(jù)庫(kù)服務(wù),。提到安裝MySQL數(shù)據(jù)庫(kù),,前兩天在一臺(tái)電腦上安裝MySQL5.7時(shí),死活裝不上,,總是提示缺少Visual Studio 2013 Redistributable,,但是很疑惑,明明已經(jīng)安裝了呀,,原來(lái)問(wèn)題出在版本上,,更換一個(gè)版本后就可以了。小問(wèn)題大苦惱,,不知道有沒(méi)有人像我一樣悲催,。 言歸正傳,啟動(dòng)本地?cái)?shù)據(jù)庫(kù)服務(wù): 用管理員身份打開(kāi)“命令提示符(管理員)”,,然后輸入“net start mysql57”(我把數(shù)據(jù)庫(kù)服務(wù)名定義為mysql57了,,安裝MySQL時(shí)可以修改)就可以開(kāi)啟服務(wù)了。注意使用管理員身份打開(kāi)小黑框,,如果不是管理員身份,,我這里會(huì)提示沒(méi)有權(quán)限,大家可以試試,。 啟動(dòng)服務(wù)之后,,我們可以選擇打開(kāi)“MySQL 5.7 Command Line Client”小黑框,需要先輸入你的數(shù)據(jù)庫(kù)的密碼,安裝的時(shí)候定義過(guò),,在這里可以進(jìn)行數(shù)據(jù)庫(kù)操作,。 下面開(kāi)始上正餐。 一,、Python爬蟲(chóng)抓取網(wǎng)頁(yè)數(shù)據(jù)并保存到本地?cái)?shù)據(jù)文件中 首先導(dǎo)入需要的數(shù)據(jù)模塊,,定義函數(shù): #導(dǎo)入需要使用到的模塊
import urllib
import re
import pandas as pd
import pymysql
import os
#爬蟲(chóng)抓取網(wǎng)頁(yè)函數(shù)
def getHtml(url):
html = urllib.request.urlopen(url).read()
html = html.decode('gbk')
return html
#抓取網(wǎng)頁(yè)股票代碼函數(shù)
def getStackCode(html):
s = r'<li><a target='_blank'
pat = re.compile(s)
code = pat.findall(html)
return code
真正干活的代碼塊: Url = 'http://quote.eastmoney.com/stocklist.html'#東方財(cái)富網(wǎng)股票數(shù)據(jù)連接地址 filepath = 'D:\\data\\'#定義數(shù)據(jù)文件保存路徑 #實(shí)施抓取 code = getStackCode(getHtml(Url)) #獲取所有股票代碼(以6開(kāi)頭的,應(yīng)該是滬市數(shù)據(jù))集合 CodeList = [] for item in code: if item[0]=='6': CodeList.append(item) #抓取數(shù)據(jù)并保存到本地csv文件 for code in CodeList: print('正在獲取股票%s數(shù)據(jù)'%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') 以上代碼實(shí)現(xiàn)了爬蟲(chóng)網(wǎng)頁(yè)抓取股票數(shù)據(jù),,并保存到本地文件中,。關(guān)于爬蟲(chóng)的東西,有很多資料可以參考,,大都是一個(gè)套路,,不再多說(shuō)。同時(shí),,本文實(shí)現(xiàn)過(guò)程中也參考了很多的網(wǎng)頁(yè)資源,,在此對(duì)所有原創(chuàng)者表示感謝! 先看下抓取的結(jié)果,。CodeList是抓取到的所有股票代碼的集合,,我們看到它共包含1416條元素,即1416支股票數(shù)據(jù),。因?yàn)楣善碧?,所以抓取的是?開(kāi)頭的,貌似是滬市股票數(shù)據(jù)(原諒我不懂金融),。 抓取到的股票數(shù)據(jù)會(huì)分別存儲(chǔ)到csv文件中,,一只股票數(shù)據(jù)一個(gè)文件。理論上會(huì)有1416個(gè)csv文件,,和股票代碼數(shù)一致,。但原諒我的渣網(wǎng)速,下載一個(gè)都費(fèi)勁,,也是呵呵了,。 打開(kāi)一個(gè)本地?cái)?shù)據(jù)文件看一下抓取的數(shù)據(jù)長(zhǎng)什么樣子: 其實(shí)和人工手動(dòng)下載也沒(méi)什么區(qū)別了,硬要說(shuō)區(qū)別,,那就是解放了勞動(dòng)力,,提高了生產(chǎn)力(怎么聽(tīng)起來(lái)像政治?),。 二,、將數(shù)據(jù)存儲(chǔ)到MySQL數(shù)據(jù)庫(kù) 首先建立本地?cái)?shù)據(jù)庫(kù)連接: #數(shù)據(jù)庫(kù)名稱(chēng)和密碼
name = 'xxxx'
password = 'xxxx' #替換為自己的用戶(hù)名和密碼
#建立本地?cái)?shù)據(jù)庫(kù)連接(需要先開(kāi)啟數(shù)據(jù)庫(kù)服務(wù))
db = pymysql.connect('localhost', name, password, charset='utf8')
cursor = db.cursor()
其中,數(shù)據(jù)庫(kù)名稱(chēng)(name)和密碼(password)是安裝MySQL時(shí)設(shè)置的,。 創(chuàng)建數(shù)據(jù)庫(kù),,專(zhuān)門(mén)用來(lái)存儲(chǔ)本次股票數(shù)據(jù): #創(chuàng)建數(shù)據(jù)庫(kù)stockDataBase,如果存在則跳過(guò) sqlSentence1 = 'create database if not exists stockDataBase' cursor.execute(sqlSentence1)#選擇使用當(dāng)前數(shù)據(jù)庫(kù) sqlSentence2 = 'use stockDataBase;' cursor.execute(sqlSentence2) 在首次運(yùn)行的時(shí)候一般都會(huì)正常創(chuàng)建數(shù)據(jù)庫(kù),但如果再次運(yùn)行,,因數(shù)據(jù)庫(kù)已經(jīng)存在,,那么跳過(guò)創(chuàng)建,繼續(xù)往下執(zhí)行,。創(chuàng)建好數(shù)據(jù)庫(kù)后,,選擇使用剛剛創(chuàng)建的數(shù)據(jù)庫(kù),,在該數(shù)據(jù)庫(kù)中存儲(chǔ)數(shù)據(jù)表,。 下面看具體的存儲(chǔ)代碼: #獲取本地文件列
fileList = os.listdir(filepath) #依次對(duì)每個(gè)數(shù)據(jù)文件進(jìn)行存儲(chǔ) for fileName in fileList: data = pd.read_csv(filepath fileName, encoding='gbk') #創(chuàng)建數(shù)據(jù)表,如果數(shù)據(jù)表已經(jīng)存在,,會(huì)跳過(guò)繼續(xù)執(zhí)行下面的步驟print('創(chuàng)建數(shù)據(jù)表stock_%s'% fileName[0:6]) sqlSentence3 = 'create table if not exists stock_%s' % fileName[0:6] '(日期 date, 股票代碼 VARCHAR(10), 名稱(chēng) VARCHAR(10), 收盤(pán)價(jià) float,\ 最高價(jià) float, 最低價(jià) float, 開(kāi)盤(pán)價(jià) float, 前收盤(pán) float, 漲跌額 float, 漲跌幅 float, 換手率 float,\ 成交量 bigint, 成交金額 bigint, 總市值 bigint, 流通市值 bigint)' cursor.execute(sqlSentence3)#迭代讀取表中每行數(shù)據(jù),,依次存儲(chǔ)(整表存儲(chǔ)還沒(méi)嘗試過(guò)) print('正在存儲(chǔ)stock_%s'% fileName[0:6]) length = len(data) for i in range(0, length): record = tuple(data.loc[i]) #插入數(shù)據(jù)語(yǔ)句 try: sqlSentence4 = 'insert into stock_%s' % fileName[0:6] '(日期, 股票代碼, 名稱(chēng), 收盤(pán)價(jià), 最高價(jià), 最低價(jià), 開(kāi)盤(pán)價(jià),\ 前收盤(pán), 漲跌額, 漲跌幅, 換手率, 成交量, 成交金額, 總市值, 流通市值) \ values ('%s',%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' % record #獲取的表中數(shù)據(jù)很亂,包含缺失值,、Nnone,、none等,插入數(shù)據(jù)庫(kù)需要處理成空值 sqlSentence4 = sqlSentence4.replace('nan','null').replace('None','null').replace('none','null') cursor.execute(sqlSentence4) except:#如果以上插入過(guò)程出錯(cuò),,跳過(guò)這條數(shù)據(jù)記錄,,繼續(xù)往下進(jìn)行 break 代碼并不復(fù)雜,只要注意其中幾個(gè)點(diǎn)就好了,。 1.邏輯層次: 包含兩層循環(huán),,外層循環(huán)是對(duì)股票代碼的循環(huán),內(nèi)層循環(huán)是對(duì)當(dāng)前股票的每一條記錄的循環(huán),。說(shuō)白了就是按照股票一支一支的存儲(chǔ),,對(duì)于每一支股票,按照它每日的記錄一條一條的存儲(chǔ),。是不是很簡(jiǎn)單很暴力,?是的!完全沒(méi)有考慮更加優(yōu)化的方式,。 2.讀取本地?cái)?shù)據(jù)文件的編碼方式: 使用'gbk'編碼,,默認(rèn)應(yīng)該是'utf8',但好像不支持中文,。 3.創(chuàng)建數(shù)據(jù)表: 同樣的,,如果數(shù)據(jù)表已經(jīng)存在(判斷是否存在if not exists),則跳過(guò)創(chuàng)建,,繼續(xù)執(zhí)行下面的步驟(會(huì)繼續(xù)存儲(chǔ)),。有個(gè)問(wèn)題是,有可能數(shù)據(jù)重復(fù)存儲(chǔ),,可以選擇跳過(guò)存儲(chǔ)或者只存儲(chǔ)最新數(shù)據(jù),。我在這里沒(méi)有考慮太多額外的處理。其次,指定字段格式,,后邊幾個(gè)字段成交量,、成交金額、總市值,、流通市值,,因?yàn)閿?shù)據(jù)較大,選擇使用bigint類(lèi)型,。 4.沒(méi)有指定數(shù)據(jù)表的主鍵: 最初是打算使用日期作為主鍵的,,后來(lái)發(fā)現(xiàn)獲取到的數(shù)據(jù)中竟然包含重復(fù)日期的數(shù)據(jù),這就打破了主鍵的唯一性,,會(huì)出bug的,,然后我也沒(méi)有多去思考數(shù)據(jù)文件的內(nèi)容,也不會(huì)進(jìn)一步使用這些個(gè)數(shù)據(jù),,也就圖省事直接不設(shè)置主鍵了,。 5.構(gòu)造sql語(yǔ)句sqlSentence4: 該過(guò)程實(shí)現(xiàn)中,直接把股票數(shù)據(jù)記錄tuple了,,然后使用字符串格式化(%操作符),。造成的精度問(wèn)題沒(méi)有多考慮,不知道會(huì)不會(huì)產(chǎn)生什么樣的影響,。%s有的上邊帶著' ',,是為了在sql語(yǔ)句中表示字符串。其中有一個(gè)%s',,只有右邊有單引號(hào),,匹配的是股票代碼,只有一邊單引號(hào),,這是因?yàn)閺臄?shù)據(jù)文件中讀取到的字符串已經(jīng)包含了左邊的單引號(hào),,左邊不需要再添加了。這是數(shù)據(jù)文件格式的問(wèn)題,,為了表示文本形式預(yù)先使用了單引號(hào),。 6.異常值處理: 文本文件中,包含有空值,、None,、none等不標(biāo)準(zhǔn)化數(shù)據(jù),這里全部替換為null了,,即數(shù)據(jù)庫(kù)的空值,。 完成MySQL數(shù)據(jù)庫(kù)數(shù)據(jù)存儲(chǔ)后,需要關(guān)閉數(shù)據(jù)庫(kù)連接: #關(guān)閉游標(biāo),,提交,,關(guān)閉數(shù)據(jù)庫(kù)連接 cursor.close() db.commit() db.close() 不關(guān)閉數(shù)據(jù)庫(kù)連接,,就無(wú)法在MySQL端進(jìn)行數(shù)據(jù)庫(kù)的查詢(xún)等操作,相當(dāng)于數(shù)據(jù)庫(kù)被占用,。 三,、MySQL數(shù)據(jù)庫(kù)查詢(xún) #重新建立數(shù)據(jù)庫(kù)連接
db = pymysql.connect('localhost', name, password, 'stockDataBase')
cursor = db.cursor()
#查詢(xún)數(shù)據(jù)庫(kù)并打印內(nèi)容
cursor.execute('select * from stock_600000')
results = cursor.fetchall()
for row in results:
print(row)
#關(guān)閉
cursor.close()
db.commit()
db.close()
以上逐條打印,會(huì)凌亂到死的,。也可以在MySQL端查看,,先選中數(shù)據(jù)庫(kù):use stockDatabase;,然后查詢(xún):select * from stock_600000;,,結(jié)果大概就是下面這個(gè)樣子了: 四,、完整代碼 實(shí)際上,整個(gè)事情完成了兩個(gè)相對(duì)獨(dú)立的過(guò)程:1.爬蟲(chóng)獲取網(wǎng)頁(yè)股票數(shù)據(jù)并保存到本地文件,;2.將本地文件數(shù)據(jù)儲(chǔ)存到MySQL數(shù)據(jù)庫(kù),。并沒(méi)有直接的考慮把從網(wǎng)頁(yè)上抓取到的數(shù)據(jù)實(shí)時(shí)(或者通過(guò)一個(gè)臨時(shí)文件)扔進(jìn)數(shù)據(jù)庫(kù),,跳過(guò)本地?cái)?shù)據(jù)文件這個(gè)過(guò)程,。這里只是嘗試著去實(shí)現(xiàn)了一下這件事情,代碼沒(méi)有做任何的優(yōu)化考慮,。本身不實(shí)際去使用,,只是樂(lè)趣而已,差不多先這樣,。哈哈~~ #導(dǎo)入需要使用到的模塊 import urllib import re import pandas as pd import pymysql import os #爬蟲(chóng)抓取網(wǎng)頁(yè)函數(shù) def getHtml(url): html = urllib.request.urlopen(url).read() html = html.decode('gbk') return html #抓取網(wǎng)頁(yè)股票代碼函數(shù) def getStackCode(html): s = r'<li><a target='_blank' pat = re.compile(s) code = pat.findall(html) return code #########################開(kāi)始干活############################ Url = 'http://quote.eastmoney.com/stocklist.html'#東方財(cái)富網(wǎng)股票數(shù)據(jù)連接地址 filepath = 'C:\\Users\\Lenovo\\Desktop\\data\\'#定義數(shù)據(jù)文件保存路徑 #實(shí)施抓取 code = getStackCode(getHtml(Url)) #獲取所有股票代碼(以6開(kāi)頭的,,應(yīng)該是滬市數(shù)據(jù))集合 CodeList = [] for item in code: if item[0]=='6': CodeList.append(item) #抓取數(shù)據(jù)并保存到本地csv文件 for code in CodeList: print('正在獲取股票%s數(shù)據(jù)'%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') ##########################將股票數(shù)據(jù)存入數(shù)據(jù)庫(kù)########################### #數(shù)據(jù)庫(kù)名稱(chēng)和密碼 name = 'xxxx' password = 'xxxx' #替換為自己的賬戶(hù)名和密碼 #建立本地?cái)?shù)據(jù)庫(kù)連接(需要先開(kāi)啟數(shù)據(jù)庫(kù)服務(wù)) db = pymysql.connect('localhost', name, password, charset='utf8') cursor = db.cursor() #創(chuàng)建數(shù)據(jù)庫(kù)stockDataBase sqlSentence1 = 'create database stockDataBase' cursor.execute(sqlSentence1)#選擇使用當(dāng)前數(shù)據(jù)庫(kù) sqlSentence2 = 'use stockDataBase;' cursor.execute(sqlSentence2) #獲取本地文件列表 fileList = os.listdir(filepath) #依次對(duì)每個(gè)數(shù)據(jù)文件進(jìn)行存儲(chǔ) for fileName in fileList: data = pd.read_csv(filepath fileName, encoding='gbk') #創(chuàng)建數(shù)據(jù)表,如果數(shù)據(jù)表已經(jīng)存在,,會(huì)跳過(guò)繼續(xù)執(zhí)行下面的步驟print('創(chuàng)建數(shù)據(jù)表stock_%s'% fileName[0:6]) sqlSentence3 = 'create table stock_%s' % fileName[0:6] '(日期 date, 股票代碼 VARCHAR(10), 名稱(chēng) VARCHAR(10), 收盤(pán)價(jià) float, 最高價(jià) float, 最低價(jià) float, 開(kāi)盤(pán)價(jià) float, 前收盤(pán) float, 漲跌額 float, 漲跌幅 float, 換手率 float, 成交量 bigint, 成交金額 bigint, 總市值 bigint, 流通市值 bigint)' cursor.execute(sqlSentence3) except: print('數(shù)據(jù)表已存在,!') #迭代讀取表中每行數(shù)據(jù),依次存儲(chǔ)(整表存儲(chǔ)還沒(méi)嘗試過(guò)) print('正在存儲(chǔ)stock_%s'% fileName[0:6]) length = len(data) for i in range(0, length): record = tuple(data.loc[i]) #插入數(shù)據(jù)語(yǔ)句 try: sqlSentence4 = 'insert into stock_%s' % fileName[0:6] '(日期, 股票代碼, 名稱(chēng), 收盤(pán)價(jià), 最高價(jià), 最低價(jià), 開(kāi)盤(pán)價(jià), 前收盤(pán), 漲跌額, 漲跌幅, 換手率, 成交量, 成交金額, 總市值, 流通市值) values ('%s',%s','%s',%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' % record #獲取的表中數(shù)據(jù)很亂,,包含缺失值,、Nnone、none等,,插入數(shù)據(jù)庫(kù)需要處理成空值 sqlSentence4 = sqlSentence4.replace('nan','null').replace('None','null').replace('none','null') cursor.execute(sqlSentence4) except: #如果以上插入過(guò)程出錯(cuò),,跳過(guò)這條數(shù)據(jù)記錄,繼續(xù)往下進(jìn)行 break #關(guān)閉游標(biāo),,提交,,關(guān)閉數(shù)據(jù)庫(kù)連接 cursor.close() db.commit() db.close() ###########################查詢(xún)剛才操作的成果################################## #重新建立數(shù)據(jù)庫(kù)連接 db = pymysql.connect('localhost', name, password, 'stockDataBase') cursor = db.cursor() #查詢(xún)數(shù)據(jù)庫(kù)并打印內(nèi)容 cursor.execute('select * from stock_600000') results = cursor.fetchall() for row in results: print(row) #關(guān)閉 cursor.close() db.commit() db.close() |
|