通常一門語言的學(xué)習(xí)都是由學(xué)習(xí)語法開始的,而本人在學(xué)習(xí)的過程中發(fā)現(xiàn)語法是很容易忘記的,即使學(xué)習(xí)完了假如不使用的話還是不會這門語言,因此本文通過一個具體的需求來學(xué)習(xí)python,一步一步走入python的世界。
題干:數(shù)據(jù)庫為mysql,數(shù)據(jù)庫名稱為db,給定一張article表,其中包含字段keywords,內(nèi)容為填寫的文章關(guān)鍵詞且用空格分隔多個關(guān)鍵詞。
實現(xiàn)功能:統(tǒng)計所有文章的關(guān)鍵詞及其個數(shù),存入數(shù)據(jù)表中。
功能分析:
讀取所有文章的keywords字段,按照空格分隔,統(tǒng)計出現(xiàn)頻次。
創(chuàng)建關(guān)鍵詞統(tǒng)計表,將統(tǒng)計結(jié)果存入表中。
實現(xiàn)文章增量記錄的讀取與處理。
將python文件打包布置到服務(wù)器上,定時執(zhí)行。
在項目開始之前,確保python開發(fā)環(huán)境已經(jīng)裝好,如何安裝在此就不贅述。本人使用的是python2.7,win7系統(tǒng),所有的代碼以python2.7為例。
下面,將一步步的介紹如何實現(xiàn)上面設(shè)定的小功能。
讀取Mysql數(shù)據(jù)庫
第一步需要連接mysql,并讀取article表中的keywords字段,為后續(xù)操作做準(zhǔn)備。
Python操作mysql數(shù)據(jù)庫用到的是MySQLdb包,使用命令pip install MySQLdb即可。
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
數(shù)據(jù)庫連接成功后,讀取article表,就要用到cursor.execute函數(shù),并設(shè)置異常處理。
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
##處理數(shù)據(jù)
try:
table_name = 'article'
sql = 'SELECT article_id,keywords FROM '+ table_name
count = cursor.execute(sql)
print count
except:
import traceback
traceback.print_exc()
finally:
conn.commit()
cursor.close()
conn.close()
cursor.execute函數(shù)得到是返回記錄個數(shù)。
如果返回有記錄,則處理keywords 字段的數(shù)據(jù),并統(tǒng)計詞頻。思路是,定義一個空的字典{}用于存放統(tǒng)計結(jié)果;遍歷記錄,通過空格拆分字段,去除空字符后,與字典進(jìn)行比對,若存在字典中則對應(yīng)詞條頻次加1,若不存在則向字典中新增詞條頻次為1。
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
##處理數(shù)據(jù)
try:
table_name = 'article'
sql = 'SELECT article_id,keywords FROM '+ table_name
count = cursor.execute(sql)
#print count
if(count>0):
results = cursor.fetchall() #取出全部數(shù)據(jù)集
results = list(results) #默認(rèn)取出的數(shù)據(jù)集為元組,設(shè)置為列表進(jìn)行遍歷
##獲取各關(guān)鍵詞數(shù)目
kw_list={} #存放詞頻統(tǒng)計結(jié)果的字典
#遍歷數(shù)據(jù)結(jié)果集
for r_id,row in results:
last_id = r_id #記錄依次賦值,直到最后一條記錄id
kw_str = ('%s' % row) #字段轉(zhuǎn)成字符類型
kw_str = kw_str.strip() #去除文本前后空格
if(kw_str!=''):
kw_arr = kw_str.split(' ')
for kw in kw_arr:
kw = kw.strip()
if (kw!=''):
if (kw_list.has_key(kw)):
kw_list[kw] = kw_list[kw] + 1
else:
kw_list[kw] = 1
print kw_list
except:
import traceback
traceback.print_exc()
finally:
conn.commit()
cursor.close()
conn.close()
打印kw_list即可看到計算出的關(guān)鍵詞頻次統(tǒng)計。其中,results = list(results)
這句非常重要,python從數(shù)據(jù)庫中取出的數(shù)據(jù)是元組,通過list()函數(shù)轉(zhuǎn)換成列表之后即可執(zhí)行遍歷操作。中文關(guān)鍵詞字段處理時,總是報編碼錯誤,加上kw_str = ('%s' % row)
語句將字段轉(zhuǎn)成字符類型即可。
至此,文章關(guān)鍵詞的頻次統(tǒng)計功能完成了,需要將統(tǒng)計結(jié)果存入數(shù)據(jù)表中。
操作Mysql數(shù)據(jù)庫
設(shè)計關(guān)鍵詞統(tǒng)計表article_keyword表,表結(jié)構(gòu)如下圖所示。
統(tǒng)計結(jié)果寫入表中,會出現(xiàn)兩種情況,一種是表中存在的詞,直接累加更新頻次即可;另一種是不存在的詞,則需要新增詞。
為了規(guī)避每循環(huán)一次就執(zhí)行一次更新或者新增的數(shù)據(jù)庫操作,提升數(shù)據(jù)庫訪問性能,采用批量執(zhí)行的方式。具體代碼實現(xiàn)如下所示:
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
##處理數(shù)據(jù)
try:
table_name = 'article'
sql = 'SELECT article_id,keywords FROM '+ table_name
count = cursor.execute(sql)
#print count
if(count>0):
results = cursor.fetchall() #取出全部數(shù)據(jù)集
results = list(results) #默認(rèn)取出的數(shù)據(jù)集為元組,設(shè)置為列表進(jìn)行遍歷
##獲取各關(guān)鍵詞數(shù)目
kw_list={} #存放詞頻統(tǒng)計結(jié)果的字典
#遍歷數(shù)據(jù)結(jié)果集
for r_id,row in results:
last_id = r_id #記錄依次賦值,直到最后一條記錄id
kw_str = ('%s' % row) #字段轉(zhuǎn)成字符類型
kw_str = kw_str.strip() #去除文本前后空格
if(kw_str!=''):
kw_arr = kw_str.split(' ')
for kw in kw_arr:
kw = kw.strip()
if (kw!=''):
if (kw_list.has_key(kw)):
kw_list[kw] = kw_list[kw] + 1
else:
kw_list[kw] = 1
#print kw_list
##結(jié)果寫入關(guān)鍵詞統(tǒng)計表中
update_values = [] ##update的值
insert_values = [] ##insert的值
for key,value in kw_list.items():
kw_count = cursor.execute("SELECT * FROM article_keyword WHERE name = '"+key+"'")
if(kw_count>0): #表中存在當(dāng)前詞
update_values.append((key,int(value)))
else: #表中不存在當(dāng)前詞
insert_values.append((key,int(value)))
#執(zhí)行批量更新語句
if (len(update_values)>0):
sub_str = ''
sub_str_in = ''
for k,v in update_values:
sub_str += 'WHEN \''+k+'\' THEN count+'+str(v) +' '
sub_str_in += "'"+k+"',"
sub_str_in = sub_str_in.strip(',')
sub_str_in = '(' + sub_str_in + ')'
sql_update = 'UPDATE article_keyword SET count = CASE name ' + sub_str +' END WHERE name IN '+sub_str_in
#原始數(shù)量+本次計算數(shù)量
cursor.execute(sql_update)
#執(zhí)行批量插入語句
if (len(insert_values)>0):
cursor.executemany('insert into zk_article_keyword(name,count) values(%s,%s)', insert_values)
except:
import traceback
traceback.print_exc()
finally:
conn.commit()
cursor.close()
conn.close()
本文使用mysql 自帶的語句構(gòu)建批量更新,實例如下:
UPDATE tablename
SET field = CASE id
WHEN 1 THEN 3
WHEN 2 THEN 4
WHEN 3 THEN 5
END
WHERE id IN (1,2,3)
意思是,更新tablename表中的field 字段,當(dāng)id=1時field =3;當(dāng)id=2時field =4;當(dāng)id=3時field =5。
MySQLdb提供了executemany函數(shù)執(zhí)行數(shù)據(jù)庫批量插入操作。
至此,實現(xiàn)了關(guān)鍵詞統(tǒng)計結(jié)果批量寫入數(shù)據(jù)庫中的功能。
分頁處理大數(shù)據(jù)量
以上的操作是一次性讀取所有文章并循環(huán)處理記錄。如果數(shù)據(jù)量較大,一次性讀取的記錄太多,會極大影響執(zhí)行效率,甚至產(chǎn)生內(nèi)存錯誤。
為了規(guī)避這類錯誤的發(fā)生,采用分頁處理的方式,設(shè)定每次處理的記錄數(shù)量,并記錄最后一個讀取到的記錄ID,直至數(shù)據(jù)讀取完成。
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
import random,time
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
##處理數(shù)據(jù)
number = 20 #設(shè)置每次處理的記錄條數(shù)
last_id = 0 #每次循環(huán)的最后一個處理記錄ID
try:
table_name = 'article'
while True:
if (last_id>0):
sql = 'SELECT article_id,keywords FROM '+ table_name +' WHERE article_id>'+str(last_id)+' LIMIT '+str(number)
elif(last_id==0):
#從第一個記錄開始執(zhí)行
sql = 'SELECT article_id,keywords FROM '+ table_name +' LIMIT '+str(number)
count = cursor.execute(sql)
if(count>0):
results = cursor.fetchall() #取出全部數(shù)據(jù)集
results = list(results) #默認(rèn)取出的數(shù)據(jù)集為元組,設(shè)置為列表進(jìn)行遍歷
##獲取各關(guān)鍵詞數(shù)目
kw_list={} #存放詞頻統(tǒng)計結(jié)果的字典
#遍歷數(shù)據(jù)結(jié)果集
for r_id,row in results:
last_id = r_id #記錄依次賦值,直到最后一條記錄id
kw_str = ('%s' % row) #字段轉(zhuǎn)成字符類型
kw_str = kw_str.strip() #去除文本前后空格
if(kw_str!=''):
kw_arr = kw_str.split(' ')
for kw in kw_arr:
kw = kw.strip()
if (kw!=''):
if (kw_list.has_key(kw)):
kw_list[kw] = kw_list[kw] + 1
else:
kw_list[kw] = 1
#print kw_list
##結(jié)果寫入關(guān)鍵詞統(tǒng)計表中
update_values = [] ##update的值
insert_values = [] ##insert的值
for key,value in kw_list.items():
kw_count = cursor.execute("SELECT * FROM article_keyword WHERE name = '"+key+"'")
if(kw_count>0):
update_values.append((key,int(value)))
else:
insert_values.append((key,int(value)))
#執(zhí)行批量更新語句
if (len(update_values)>0):
sub_str = ''
sub_str_in = ''
for k,v in update_values:
sub_str += 'WHEN \''+k+'\' THEN count+'+str(v) +' '
sub_str_in += "'"+k+"',"
sub_str_in = sub_str_in.strip(',')
sub_str_in = '(' + sub_str_in + ')'
sql_update = 'UPDATE article_keyword SET count = CASE name ' + sub_str +' END WHERE name IN '+sub_str_in
#print sql_update
#原始數(shù)量+本次計算數(shù)量
cursor.execute(sql_update)
#執(zhí)行批量插入語句
if (len(insert_values)>0):
cursor.executemany('insert into article_keyword(name,count) values(%s,%s)', insert_values)
else:
break #跳出while循環(huán)
except:
import traceback
traceback.print_exc()
finally:
conn.commit()
cursor.close()
conn.close()
設(shè)置每次讀取記錄數(shù)number,并記錄每次循環(huán)處理的最后一個記錄ID(last_id),使用while循環(huán)來讀取、處理數(shù)據(jù),直到?jīng)]有數(shù)據(jù)則跳出循環(huán)。
定時執(zhí)行增量數(shù)據(jù)
以上實現(xiàn)了對article表中所有記錄的分頁讀取與批量處理,但在實際作業(yè)中,article表中的記錄是會不斷新增的,不可能每次都對全部記錄執(zhí)行操作,因此需要在每次執(zhí)行程序后記錄最后一個記錄的ID,下次執(zhí)行程序前讀取文章ID,從該記錄之后讀取數(shù)據(jù)。
解決此問題的方式,我采用了通過一個txt文件記錄文章ID,文件名為bak.txt,初識值為0,每次程序執(zhí)行先讀取該文件記錄的ID,程序執(zhí)行完后將處理的最后一篇文章的ID放入文件中。這里就包含了python對txt文件的讀取和寫入功能,具體代碼如下所示:
#引入MySQLdb 庫,命名為mdb
import MySQLdb as mdb
import random,time
##讀取bak.txt文件中記錄的ID
txt_id = 0 #文件中記錄的ID
file_obj = open('bak.txt')
try:
txt_id = file_obj.read()
finally:
file_obj.close()
##連接數(shù)據(jù)庫
try:
#建立數(shù)據(jù)庫連接
conn = mdb.connect(host='127.0.0.1',port = 3306, user='root',passwd='pwd', db ='db',charset='utf8' )
#獲取操作游標(biāo)
cursor = conn.cursor()
except:
print "Could not connect to MySQL server."
exit(0)
##處理數(shù)據(jù)
number = 20 #設(shè)置每次處理的記錄條數(shù)
last_id = int(txt_id) #每次循環(huán)的最后一個處理記錄ID
try:
table_name = 'article'
while True:
if (last_id>0):
sql = 'SELECT article_id,keywords FROM '+ table_name +' WHERE article_id>'+str(last_id)+' LIMIT '+str(number)
elif(last_id==0):
#從第一個記錄開始執(zhí)行
sql = 'SELECT article_id,keywords FROM '+ table_name +' LIMIT '+str(number)
count = cursor.execute(sql)
if(count>0):
results = cursor.fetchall() #取出全部數(shù)據(jù)集
results = list(results) #默認(rèn)取出的數(shù)據(jù)集為元組,設(shè)置為列表進(jìn)行遍歷
##獲取各關(guān)鍵詞數(shù)目
kw_list={} #存放詞頻統(tǒng)計結(jié)果的字典
#遍歷數(shù)據(jù)結(jié)果集
for r_id,row in results:
last_id = r_id #記錄依次賦值,直到最后一條記錄id
kw_str = ('%s' % row) #字段轉(zhuǎn)成字符類型
kw_str = kw_str.strip() #去除文本前后空格
if(kw_str!=''):
kw_arr = kw_str.split(' ')
for kw in kw_arr:
kw = kw.strip()
if (kw!=''):
if (kw_list.has_key(kw)):
kw_list[kw] = kw_list[kw] + 1
else:
kw_list[kw] = 1
#print kw_list
##結(jié)果寫入關(guān)鍵詞統(tǒng)計表中
update_values = [] ##update的值
insert_values = [] ##insert的值
for key,value in kw_list.items():
kw_count = cursor.execute("SELECT * FROM article_keyword WHERE name = '"+key+"'")
if(kw_count>0):
update_values.append((key,int(value)))
else:
insert_values.append((key,int(value)))
#執(zhí)行批量更新語句
if (len(update_values)>0):
sub_str = ''
sub_str_in = ''
for k,v in update_values:
sub_str += 'WHEN \''+k+'\' THEN count+'+str(v) +' '
sub_str_in += "'"+k+"',"
sub_str_in = sub_str_in.strip(',')
sub_str_in = '(' + sub_str_in + ')'
sql_update = 'UPDATE article_keyword SET count = CASE name ' + sub_str +' END WHERE name IN '+sub_str_in
#print sql_update
#原始數(shù)量+本次計算數(shù)量
cursor.execute(sql_update)
#執(zhí)行批量插入語句
if (len(insert_values)>0):
cursor.executemany('insert into article_keyword(name,count) values(%s,%s)', insert_values)
else:
break #跳出while循環(huán)
except:
import traceback
traceback.print_exc()
finally:
conn.commit()
cursor.close()
conn.close()
##獲取處理完的最后一條記錄ID,寫入文件中
file_obj = open('bak.txt', 'w')
file_obj.writelines(str(last_id))
file_obj.close( )
需要注意的是last_id 的初始值不再是0,需要改成從txt文件中讀取的文章ID即last_id = int(txt_id)
。
另,python對于數(shù)據(jù)類型要求很嚴(yán)格,字符串的連接必須要先強(qiáng)制轉(zhuǎn)換成字符型,數(shù)值類型亦然。
至此,所有的代碼書寫完成。
Python文件打包成exe
寫完python腳本以后,需要在IDLE運(yùn)行才能執(zhí)行程序,而文章可能每天都會更新,因此需要腳本能夠定期自動運(yùn)行,所以考慮將python腳本打包成exe文件,設(shè)置成定時任務(wù)。
Python文件打包成exe有兩種工具:py2exe和pyInstaller,根據(jù)網(wǎng)友推薦選擇pyInstaller進(jìn)行python文件打包。步驟如下所示:
- 官網(wǎng)下載pyInstaller,解壓到任意文件夾下。本人下載的版本是PyInstaller-3.2.1,解壓到D盤。
- 在pyInstaller文件目錄下,點(diǎn)擊(Shift+鼠標(biāo)右鍵)在彈出菜單欄中選擇“在此處打開命令窗口”。在命令窗口中輸入
setup.py install
進(jìn)行安裝。 - 若安裝報錯,則還需要安裝PyWin32,輸入
import win32com
若不報錯則表示安裝成功。 - 在命令窗口輸入
pyinstaller.py -F D:/PyRoot/getkws.py
,執(zhí)行完成后在pyInstaller目錄下生成了名字為python文件名的文件夾getkws,打開后看到如下內(nèi)容。
Python文件打包成exe.png - 打開dist文件夾,即可看到生成好的getkws.exe文件。
exe文件生成成功后,在系統(tǒng)自帶的系統(tǒng)工具-任務(wù)計劃程序中“創(chuàng)建基本任務(wù)”,選擇執(zhí)行文件并設(shè)置定時間隔,設(shè)定完成后即可。
至此,使用Python實現(xiàn)文章關(guān)鍵詞分割、統(tǒng)計的功能就全部實現(xiàn)了。
本文通過一個簡單的需求,運(yùn)用python語言一步步的介紹了功能的解決思路和python的編程寫法,其中遇到了很多的坑,例如中文亂碼、字符串連接、批量修改、文件打包等。有問題的出現(xiàn)才會激發(fā)解決問題的沖動,不懂的就去百度谷歌,一個個的解決、實現(xiàn)、最終完成,很有成就感。雖然這只是一個小小的功能,代碼的書寫也比較基礎(chǔ),卻也明白了python整個開發(fā)流程和部署,算是對本人python入門項目的一次梳理。