不好意思,拖堂了,此為終章,補(bǔ)充蠻重要的兩點(diǎn)
一、GUI
二、多進(jìn)程
一、GUI
話說,python做圖形界面并不明智,效率并不高。但在某些特殊需求下還是需要我們?nèi)ナ褂?,所以python擁有多個(gè)第三方庫(kù)用以實(shí)現(xiàn)GUI,本章我們使用python基本模塊tkinter進(jìn)行學(xué)習(xí),因?yàn)樾枨蟛⒉淮?,所以不做太多拓展?br> 繼續(xù)改寫上一章的IP查詢系統(tǒng)(= =,要玩爛了),首先略改下IpWhere.py以備調(diào)用~
import requests
import re
def ip_adr(target_ip):
headers = {'user-agent': 'ceshi/0.0.1'}
r = requests.get('http://ip.tool.chinaz.com/{}'.format(target_ip),headers=headers)
find_adr = re.compile(r'<span class="Whwtdhalf w50-0">.*</span>')
find_ip = re.compile(r'<span class="Whwtdhalf w15-0">.*</span>')
res1=find_adr.findall(r.text)
res2=find_ip.findall(r.text)
adr=re.findall(r'>.*<',str(res1[1]))
adr=str(adr)[3:]
adr=str(adr)[:-3]
ip=re.findall(r'>.*<',str(res2[4]))
ip=str(ip)[3:]
ip=str(ip)[:-3]
return '目標(biāo)IP為:{}\n物理地址為:{}'.format(ip,adr)
if "__main__" == __name__ :
print(ip_adr('baidu.com'))
然后使用tkinter模塊進(jìn)行圖形界面的實(shí)現(xiàn),調(diào)用預(yù)編譯的IpWhere模塊 :
from tkinter import *
import tkinter.messagebox as messagebox
from IpWhere import ip_adr
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.nameInput = Entry(self)
self.nameInput.pack()
self.alertButton = Button(self, text='點(diǎn)擊查詢', command=self.ipis)
self.alertButton.pack()
def ipis(self):
try:
res = self.nameInput.get()
messagebox.showinfo('查詢結(jié)果','{}' .format(ip_adr(res)))
except:
messagebox.showinfo('查詢結(jié)果', '乖,好好輸入')
app = Application()
app.master.title('IP查詢系統(tǒng)')
app.mainloop()
額,太丑了,但基本實(shí)現(xiàn)我們小小的需求,在以后的py學(xué)習(xí)中,我們?cè)偕婕捌渌牡谌侥K,此處就當(dāng)是入門了解吧。


二、多進(jìn)程
十分抱歉把這么重要的內(nèi)容放在最后,要不是大佬指點(diǎn),此次學(xué)習(xí)可能就要錯(cuò)過多進(jìn)程的問題了。
Unix系統(tǒng)提供了forx,python可借助os模塊調(diào)用,從而實(shí)現(xiàn)多進(jìn)程,然而windows系統(tǒng)并不具備,所以我們選擇python內(nèi)置的multiprocessing多進(jìn)程模塊進(jìn)行學(xué)習(xí)。
友情提示:請(qǐng)先自行補(bǔ)充線程、進(jìn)程基本概念。
首先我們借助直接調(diào)用多進(jìn)程來改寫下我們?cè)诙嗑€程章節(jié)用到的例子!
import time
from multiprocessing import Process
def eating():
print('吃飯時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來吃飯
print('吃飽啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def sleeping():
print('睡覺時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來睡覺
print('醒啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def hitting():
print('打豆豆時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來打豆豆
print('打出翔了!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
if __name__=='__main__':
p=Process(target=eating())
p.start()
p.join()
p=Process(target=sleeping())
p.start()
p.join()
p=Process(target=hitting())
p.start()
p.join()
#輸出:
吃飯時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:16:32
吃飽啦!當(dāng)前時(shí)間:2019-01-29 00:16:34
睡覺時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:16:34
醒啦!當(dāng)前時(shí)間:2019-01-29 00:16:36
打豆豆時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:16:36
打出翔了!當(dāng)前時(shí)間:2019-01-29 00:16:38
顯然,這么寫實(shí)在太蠢了,如果我們的任務(wù)量巨大,這并不合適。所以我們引入了進(jìn)程池的概念,使用進(jìn)程池進(jìn)行改寫:
import time
from multiprocessing import Pool
def eating():
print('吃飯時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來吃飯
print('吃飽啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def sleeping():
print('睡覺時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來睡覺
print('醒啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def hitting():
print('打豆豆時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
time.sleep(2) #休眠兩秒鐘用來打豆豆
print('打出翔了!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
if __name__=='__main__':
p = Pool(3)
for i in [eating(),sleeping(),hitting()]:
p.apply_async(i)
p.close()
p.join()
#輸出:
吃飯時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:23:02
睡覺時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:23:02
打豆豆時(shí)間到!當(dāng)前時(shí)間:2019-01-29 00:23:02
吃飽啦!當(dāng)前時(shí)間:2019-01-29 00:23:04
醒啦!當(dāng)前時(shí)間:2019-01-29 00:23:04
打出翔了!當(dāng)前時(shí)間:2019-01-29 00:23:04
在此,我們可以看到所有進(jìn)程是并發(fā)執(zhí)行的,同樣,我們?cè)诙嗑€程章節(jié)就講過,主進(jìn)程的結(jié)束意味著程序退出,所以我們需要借助join()方法堵塞進(jìn)程。
進(jìn)程間通信
我們知道線程共享內(nèi)存空間,而進(jìn)程的內(nèi)存是獨(dú)立的,同一個(gè)進(jìn)程的線程之間可以直接交流,也就帶來了線程同步的苦惱,這個(gè)我們?cè)诙嗑€程章節(jié)已經(jīng)講過了;而兩個(gè)進(jìn)程想通信,則必須通過一個(gè)中間代理來實(shí)現(xiàn),即我們接下來的內(nèi)容:進(jìn)程間通信。
進(jìn)程之間肯定是需要通信的,操作系統(tǒng)提供了很多機(jī)制來實(shí)現(xiàn)進(jìn)程間的通信。Python的multiprocessing模塊包裝了底層的機(jī)制,提供了Queue、Pipes等多種方式來交換數(shù)據(jù)。我們接下來就以Queue的方式進(jìn)行學(xué)習(xí)。
Queue.Queue是進(jìn)程內(nèi)非阻塞隊(duì)列,multiprocess.Queue是跨進(jìn)程通信隊(duì)列,前者是各自私有,后者是各子進(jìn)程共有。
還有一個(gè)在后者基礎(chǔ)上進(jìn)行封裝的multiprocess.Manager.Queue()方法,如果要使用Pool創(chuàng)建進(jìn)程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否則會(huì)得到一條如下的錯(cuò)誤信息:RuntimeError: Queue objects should only be shared between processes through inheritance.
接下來我們就借助進(jìn)程池來進(jìn)行多進(jìn)程操作的改寫,感謝大佬一路輔導(dǎo)。
import time
import os
from multiprocessing import Manager,Pool
def eating(q):
print('吃飯時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來吃飯
print('吃飽啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
q.get()
def sleeping(q):
print('睡覺時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來睡覺
print('醒啦!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
q.get()
def hitting(q):
print('打豆豆時(shí)間到!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來打豆豆
print('打出翔了!當(dāng)前時(shí)間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
q.get()
def working(funcName,q):
if funcName == "eating":
eating(q)
if funcName == "hitting":
hitting(q)
if funcName == "sleeping":
sleeping(q)
def main():
p=Pool(3) #創(chuàng)建進(jìn)程池
q=Manager().Queue(2) #創(chuàng)建隊(duì)列,為了演示更清晰,我們故意設(shè)兩個(gè)隊(duì)列
for i in ["eating","sleeping","hitting"]:
q.put(i) #添加隊(duì)列
print("隊(duì)列添加成功")
p.apply_async(working, args=(i,q))
#p.close()
#p.join()
while True: #用上方注釋的jion()亦可,此處使用循環(huán)堵塞隊(duì)列
if q.empty(): #如隊(duì)列為空,break,循環(huán)結(jié)束,主線程結(jié)束
break
if __name__=='__main__':
main()
#輸出:
隊(duì)列添加成功
隊(duì)列添加成功
睡覺時(shí)間到!當(dāng)前時(shí)間:2019-01-29 15:25:36
Process to running: 11872
吃飯時(shí)間到!當(dāng)前時(shí)間:2019-01-29 15:25:36
Process to running: 25284
吃飽啦!當(dāng)前時(shí)間:2019-01-29 15:25:38
醒啦!當(dāng)前時(shí)間:2019-01-29 15:25:38
隊(duì)列添加成功
打豆豆時(shí)間到!當(dāng)前時(shí)間:2019-01-29 15:25:38
Process to running: 18536
打出翔了!當(dāng)前時(shí)間:2019-01-29 15:25:40
Process finished with exit code 0
我們可以看到兩個(gè)子線程先執(zhí)行,然后一個(gè)子線程單獨(dú)執(zhí)行,此處有意而為之,讓大家更清晰的了解隊(duì)列的使用。期間有一處我們放棄使用jion()方法堵塞,而是自己寫了個(gè)循環(huán)堵塞,大家根據(jù)自己習(xí)慣來就好。
實(shí)例拓展
話說,真的沒人吐槽么?上面的例子從需求上來講,完全就不需要多線程好不好!emmmm,我們來點(diǎn)實(shí)力拓展,寫一個(gè)有智商的多線程腳本,順便結(jié)合上一節(jié)的web來一個(gè)綜合篇,隨便找個(gè)現(xiàn)實(shí)需求吧!
emmm,比如我們來到當(dāng)當(dāng)網(wǎng)買書,搜一下我們想要的書籍,發(fā)現(xiàn)?。√嗔耍?!真J2亂!!看不過來!!不想翻頁(yè)!!直接告訴我哪個(gè)便宜、哪個(gè)牛逼好不好!!

簡(jiǎn)單看下這個(gè)url:
http://search.dangdang.com/?key=滲透測(cè)試&ddsale=1&page_index=2
其中ddsale參數(shù)代表當(dāng)當(dāng)自營(yíng),page_index代表頁(yè)數(shù),key代表搜索內(nèi)容,我們本次的變量只有頁(yè)數(shù)。
所以我們構(gòu)造請(qǐng)求的url為:
'http://search.dangdang.com/?key=滲透測(cè)試&ddsale=1&page_index='+str(page)
如果修改的內(nèi)容不使用str字符串轉(zhuǎn)化,會(huì)收到如下報(bào)錯(cuò):
TypeError: can only concatenate str (not "int") to str
然后我們看一下頁(yè)面內(nèi)容的分布情況,本次我們關(guān)心賣什么書,賣多少錢?

對(duì)應(yīng)的編寫我們的正則匹配規(guī)則,當(dāng)然了,有更簡(jiǎn)便的第三方庫(kù)可以幫我們處理,但為了更好的形成流程性認(rèn)識(shí),我們這里依然使用正則。
我們對(duì)應(yīng)我們需要的書籍名稱和當(dāng)前價(jià)格匹配如下:
<a title=" (.*?)" ddclick=
<span class="search_now_price">¥(.*?)</span>
那么,思路理清了,我們就開始使用多線程來寫我們的小系統(tǒng)~
import requests
import re
from multiprocessing import Pool, Manager
headers = {'user-agent': 'ceshi/0.0.1'}
#信息爬取模塊
def getInfo(page,yourkey):
r = requests.get('http://search.dangdang.com/?key='+str(yourkey)+'&ddsale=1&page_index='+str(page) , headers=headers)
r1=re.compile(r'<a title=" (.*?)" ddclick=')
r2=re.compile(r'<span class="search_now_price">¥(.*?)</span>')
re1 = r1.findall(r.text)
re2 = r2.findall(r.text)
return dict(zip(re1, re2)) #將列表轉(zhuǎn)為字典
#文件存儲(chǔ)模塊
def saveinfo(page,yourkey,q):
fw = open('DangDang.txt', 'a')
di=getInfo(page,yourkey) #新建字典接收返回結(jié)果
for i in di: #整理格式寫入文件
fw.write('書籍名稱:'+i+'\t'+'當(dāng)前價(jià)格'+di[i]+'\n')
fw.close()
q.put(page)
#進(jìn)程池管理模塊
def poolmana(pages,yourkey):
p = Pool(10)
q = Manager().Queue()
for i in range(pages):
p.apply_async(saveinfo, args=(i+1, yourkey,q))
p.close()
p.join()
print('讀取完成>>>>>\n請(qǐng)查看當(dāng)前路徑下文件:DangDang.txt')
#助手函數(shù),輸入判斷
def is_number(s): #當(dāng)了個(gè)助手函數(shù)用來判斷用戶輸入內(nèi)容
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
#主函數(shù),實(shí)現(xiàn)界面
def main():
print('''
============================================
|| 【歡迎來到史上最屌的當(dāng)當(dāng)網(wǎng)查詢系統(tǒng)】 ||
|| (輸入exit退出) ||
============================================
''')
while True:
try:
yourkey=input('請(qǐng)輸入您要查詢的內(nèi)容:')
if yourkey=='exit':
break
pa=input('請(qǐng)輸入希望檢索的頁(yè)數(shù):\n(默認(rèn)為3)\n')
if pa=='exit':
break
if is_number(pa)==False: #使用助手函數(shù)判斷輸入是否為數(shù)字,如否,使用默認(rèn)值3
pa=3
print('讀取ing~>>>>>>\n數(shù)據(jù)量較大,請(qǐng)耐心等待>>>>>')
poolmana(int(pa),str(yourkey))
except:
print("請(qǐng)規(guī)范您的輸入!")
if "__main__" == __name__ :
main()
#輸出:
============================================
|| 【歡迎來到史上最屌的當(dāng)當(dāng)網(wǎng)查詢系統(tǒng)】 ||
|| (輸入exit退出) ||
============================================
請(qǐng)輸入您要查詢的內(nèi)容:滲透測(cè)試
請(qǐng)輸入希望檢索的頁(yè)數(shù):
(默認(rèn)為3)
讀取ing~>>>>>>
數(shù)據(jù)量較大,請(qǐng)耐心等待>>>>>
讀取完成>>>>>
請(qǐng)查看當(dāng)前路徑下文件:DangDang.txt
請(qǐng)輸入您要查詢的內(nèi)容:exit
Process finished with exit code 0
然后我們?nèi)ゲ榭匆幌挛覀兊慕Y(jié)果文件~

現(xiàn)在這個(gè)小系統(tǒng)具備的功能就是根據(jù)用戶需要選擇要檢索的書籍,然后整理下名稱和價(jià)格,開了10個(gè)線程,如果小伙伴pc給力的話可以繼續(xù)加。簡(jiǎn)單的異常處理機(jī)制和界面交互,基本滿足日常所需。
emmmmm,py入門系列到此結(jié)束!當(dāng)初立的flag并沒有垮掉!

小結(jié)一下,本次py學(xué)習(xí)應(yīng)該是從1.11號(hào)開始的,完全0基礎(chǔ)開始,砍去出差、周末時(shí)間,工作之余零零散散的學(xué)習(xí)時(shí)間應(yīng)該在十天左右,所以十天的時(shí)間用點(diǎn)心的話,自學(xué)python應(yīng)該是沒有問題的。
關(guān)于學(xué)習(xí)材料的話,廖雪峰老師的教程配上菜鳥教程的基本理論,再來幾個(gè)可以幫你解答疑惑的py大佬,足夠了。雖然較專業(yè)開發(fā)與框架運(yùn)用還有很大差距,但,py的征途已經(jīng)開始了~