不好意思,拖堂了,此為終章,補充蠻重要的兩點
一、GUI
二、多進程
話說,python做圖形界面並不明智,效率並不高。但在某些特殊需求下還是需要我們去使用,所以python擁有多個第三方庫用以實現GUI,本章我們使用python基本模塊tkinter進行學習,因為需求並不大,所以不做太多拓展。
繼續改寫上一章的IP查詢系統(= =,要玩爛了),首先略改下IpWhere.py以備調用~
然後使用tkinter模塊進行圖形界面的實現,調用預編譯的IpWhere模塊 :
額,太醜了,但基本實現我們小小的需求,在以後的py學習中,我們再涉及其他的第三方模塊,此處就當是入門了解吧。
正確噠
錯誤噠
二、多進程十分抱歉把這麼重要的內容放在最後,要不是大佬指點,此次學習可能就要錯過多進程的問題了。
Unix系統提供了forx,python可藉助os模塊調用,從而實現多進程,然而windows系統並不具備,所以我們選擇python內置的multiprocessing多進程模塊進行學習。
友情提示:請先自行補充線程、進程基本概念。
首先我們藉助直接調用多進程來改寫下我們在多線程章節用到的例子!
顯然,這麼寫實在太蠢了,如果我們的任務量巨大,這並不合適。所以我們引入了進程池的概念,使用進程池進行改寫:
在此,我們可以看到所有進程是並發執行的,同樣,我們在多線程章節就講過,主進程的結束意味著程序退出,所以我們需要藉助join()方法堵塞進程。
進程間通信我們知道線程共享內存空間,而進程的內存是獨立的,同一個進程的線程之間可以直接交流,也就帶來了線程同步的苦惱,這個我們在多線程章節已經講過了;而兩個進程想通信,則必須通過一個中間代理來實現,即我們接下來的內容:進程間通信。
進程之間肯定是需要通信的,作業系統提供了很多機制來實現進程間的通信。Python的multiprocessing模塊包裝了底層的機制,提供了Queue、Pipes等多種方式來交換數據。我們接下來就以Queue的方式進行學習。
Queue.Queue是進程內非阻塞隊列,multiprocess.Queue是跨進程通信隊列,前者是各自私有,後者是各子進程共有。
還有一個在後者基礎上進行封裝的multiprocess.Manager.Queue()方法,如果要使用Pool創建進程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否則會得到一條如下的錯誤信息:RuntimeError: Queue objects should only be shared between processes through inheritance.
接下來我們就藉助進程池來進行多線程操作的改寫,感謝大佬一路輔導。
import time
import os
from multiprocessing import Manager,Pool
def eating(q):
print('吃飯時間到!當前時間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來吃飯
print('吃飽啦!當前時間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
q.get()
def sleeping(q):
print('睡覺時間到!當前時間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來睡覺
print('醒啦!當前時間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
q.get()
def hitting(q):
print('打豆豆時間到!當前時間:{}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
print('Process to running: %s' % os.getpid())
time.sleep(2) #休眠兩秒鐘用來打豆豆
print('打出翔了!當前時間:{}'.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) #創建進程池
q=Manager().Queue(2) #創建隊列,為了演示更清晰,我們故意設兩個隊列
for i in ["eating","sleeping","hitting"]:
q.put(i) #添加隊列
print("隊列添加成功")
p.apply_async(working, args=(i,q))
#p.close()
#p.join()
while True: #用上方注釋的jion()亦可,此處使用循環堵塞隊列
if q.empty(): #如隊列為空,break,循環結束,主線程結束
break
if __name__=='__main__':
main()
我們可以看到兩個子線程先執行,然後一個子線程單獨執行,此處有意而為之,讓大家更清晰的了解隊列的使用。期間有一處我們放棄使用jion()方法堵塞,而是自己寫了個循環堵塞,大家根據自己習慣來就好。
實例拓展話說,真的沒人吐槽麼?上面的例子從需求上來講,完全就不需要多線程好不好!emmmm,我們來點實力拓展,寫一個有智商的多線程腳本,順便結合上一節的web來一個綜合篇,隨便找個現實需求吧!
emmm,比如我們來到當當網買書,搜一下我們想要的書籍,發現!!太多了!!真J2亂!!看不過來!!不想翻頁!!直接告訴我哪個便宜、哪個牛逼好不好!!
當當網
簡單看下這個url:
http://search.dangdang.com/?key=滲透測試&ddsale=1&page_index=2
其中ddsale參數代表噹噹自營,page_index代表頁數,key代表搜索內容,我們本次的變量只有頁數。
所以我們構造請求的url為:
'http://search.dangdang.com/?key=滲透測試&ddsale=1&page_index='+str(page)
如果修改的內容不使用str字符串轉化,會收到如下報錯:
TypeError: can only concatenate str (not "int") to str
然後我們看一下頁面內容的分布情況,本次我們關心賣什麼書,賣多少錢?
網站源碼
對應的編寫我們的正則匹配規則,當然了,有更簡便的第三方庫可以幫我們處理,但為了更好的形成流程性認識,我們這裡依然使用正則。
我們對應我們需要的書籍名稱和當前價格匹配如下:
<a title=" (.*?)" ddclick=
<span>¥(.*?)</span>
那麼,思路理清了,我們就開始使用多線程來寫我們的小系統~
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>¥(.*?)</span>')
re1 = r1.findall(r.text)
re2 = r2.findall(r.text)
return dict(zip(re1, re2)) #將列錶轉為字典
#文件存儲模塊
def saveinfo(page,yourkey,q):
fw = open('DangDang.txt', 'a')
di=getInfo(page,yourkey) #新建字典接收返回結果
for i in di: #整理格式寫入文件
fw.write('書籍名稱:'+i+'\t'+'當前價格'+di[i]+'\n')
fw.close()
q.put(page)
#進程池管理模塊
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請查看當前路徑下文件:DangDang.txt')
#助手函數,輸入判斷
def is_number(s): #當了個助手函數用來判斷用戶輸入內容
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
#主函數,實現界面
def main():
print('''
============================================
【歡迎來到史上最屌的當當網查詢系統】
(輸入exit退出)
============================================
''')
while True:
try:
yourkey=input('請輸入您要查詢的內容:')
if yourkey=='exit':
break
pa=input('請輸入希望檢索的頁數:\n(默認為3)\n')
if pa=='exit':
break
if is_number(pa)==False: #使用助手函數判斷輸入是否為數字,如否,使用默認值3
pa=3
print('讀取ing~>>>>>>\n數據量較大,請耐心等待>>>>>')
poolmana(int(pa),str(yourkey))
except:
print("請規範您的輸入!")
if "__main__" == __name__ :
main()
然後我們去查看一下我們的結果文件~
DangDang.txt
現在這個小系統具備的功能就是根據用戶需要選擇要檢索的書籍,然後整理下名稱和價格,開了10個線程,如果小夥伴pc給力的話可以繼續加。簡單的異常處理機制和界面交互,基本滿足日常所需。
emmmmm,py入門系列到此結束!當初立的flag並沒有垮掉!flag
小結一下,本次py學習應該是從1.11號開始的,完全0基礎開始,砍去出差、周末時間,工作之餘零零散散的學習時間應該在十天左右,所以十天的時間用點心的話,自學python應該是沒有問題的。
關於學習材料的話,廖雪峰老師的教程配上菜鳥教程的基本理論,再來幾個可以幫你解答疑惑的py大佬,足夠了。雖然較專業開發與框架運用還有很大差距,但,py的徵途已經開始了~