python分布式計算
1. python五大應用領域是什麼
一、網路爬蟲
網路爬蟲是Python比較常用的一個場景,國際上,google在前期大量地運用Python言語作為網路爬蟲的根底,帶動了整個Python言語的運用發展。
二、數據處理
Python有很齊備的生態環境。"大數據"分析中涉及到的分布式核算、數據可視化、資料庫操作等,Python中都有成熟的模塊能夠挑選完結其功能。關於Hadoop-MapRece和Spark,都能夠直接運用Python完結核算邏輯,這不管關於數據科學家仍是關於數據工程師而言都是十分便當的。
三、web開發
Python的誕生前史比Web還要早,由於Python是一種解說型的腳本言語,開發效率高,所以十分適合用來做Web開發。
Django 是 Python 編程言語驅動的一個開源模型-視圖-控制器(MVC)風格的 Web 運用程序結構。運用 Django,咱們在幾分鍾之內就能夠創建高品質、易維護、資料庫驅動的運用程序。
四、數據分析
關於數據分析師來說,不只要自己理解數據背面的含義,而且還要給更直地展現數據的含義。
Scipy是一組專門解決科學核算中各種規范問題域的包的集合。Numpy是python科學核算的根底包。Pandas處理上千萬的數據是一揮而就的工作,同時隨後咱們也將看到它比SQL有更強的表達能力,能夠做很多復雜的操作,要寫的code也更少。
五、人工智慧
人工智慧是現在十分火的一個方向,AI熱潮讓Python言語的未來充滿了無限的潛力。現在釋放出來的幾個十分有影響力的AI結構,大多是Python的實現,為什麼呢?
在人工智慧大領域領域內的數據發掘、機器學習、神經網路、深度學習等方面都是主流的編程言語,得到廣泛的支持和運用。人工智慧的核心演算法大部分仍是依賴於C/C++的,由於是核算密集型,需求十分精細的優化,還需求GPU、專用硬體之類的介面,這些都只要C/C++能做到。
關於Python五大應用領域是什麼,環球青藤小編就和大家分享到這里了,學習是永無止境的,學習一項技能更是受益終身,所以,只要肯努力學,什麼時候開始都不晚。如果您還想繼續了解關於python編程的學習方法及素材等內容,可以點擊本站其他文章學習。
2. python分布式爬蟲是什麼意思
一、分布式爬蟲架構
在了解分布式爬蟲架構之前,首先回顧一下Scrapy的架構,如下圖所示。
我們需要做的就是在多台主機上同時運行爬蟲任務協同爬取,而協同爬取的前提就是共享爬取隊列。這樣各台主機就不需要各自維護爬取隊列,而是從共享爬取隊列存取Request。但是各台主機還是有各自的Scheler和Downloader,所以調度和下載功能分別完成。如果不考慮隊列存取性能消耗,爬取效率還是會成倍提高。
二、維護爬取隊列
那麼這個隊列用什麼來維護?首先需要考慮的就是性能問題。我們自然想到的是基於內存存儲的Redis,它支持多種數據結構,例如列表(List)、集合(Set)、有序集合(Sorted Set)等,存取的操作也非常簡單。
Redis支持的這幾種數據結構存儲各有優點。
列表有lpush()、lpop()、rpush()、rpop()方法,我們可以用它來實現先進先出式爬取隊列,也可以實現先進後出棧式爬取隊列。
集合的元素是無序的且不重復的,這樣我們可以非常方便地實現隨機排序且不重復的爬取隊列。
有序集合帶有分數表示,而Scrapy的Request也有優先順序的控制,我們可以用它來實現帶優先順序調度的隊列。
我們需要根據具體爬蟲的需求來靈活選擇不同的隊列。
三、如何去重
Scrapy有自動去重,它的去重使用了Python中的集合。這個集合記錄了Scrapy中每個Request的指紋,這個指紋實際上就是Request的散列值。我們可以看看Scrapy的源代碼,如下所示:
importhashlib
defrequest_fingerprint(request, include_headers=None):
ifinclude_headers:
include_headers = tuple(to_bytes(h.lower())
forhinsorted(include_headers))
cache = _fingerprint_cache.setdefault(request, {})
ifinclude_headersnotincache:
fp = hashlib.sha1()
fp.update(to_bytes(request.method))
fp.update(to_bytes(canonicalize_url(request.url)))
fp.update(request.bodyorb'')
ifinclude_headers:
forhdrininclude_headers:
ifhdrinrequest.headers:
fp.update(hdr)
forvinrequest.headers.getlist(hdr):
fp.update(v)
cache[include_headers] = fp.hexdigest()
returncache[include_headers]
request_fingerprint()就是計算Request指紋的方法,其方法內部使用的是hashlib的sha1()方法。計算的欄位包括Request的Method、URL、Body、Headers這幾部分內容,這里只要有一點不同,那麼計算的結果就不同。計算得到的結果是加密後的字元串,也就是指紋。每個Request都有獨有的指紋,指紋就是一個字元串,判定字元串是否重復比判定Request對象是否重復容易得多,所以指紋可以作為判定Request是否重復的依據。
那麼我們如何判定重復呢?Scrapy是這樣實現的,如下所示:
def__init__(self):
self.fingerprints = set()
defrequest_seen(self, request):
fp = self.request_fingerprint(request)
iffpinself.fingerprints:
returnTrue
self.fingerprints.add(fp)
在去重的類RFPDupeFilter中,有一個request_seen()方法,這個方法有一個參數request,它的作用就是檢測該Request對象是否重復。這個方法調用request_fingerprint()獲取該Request的指紋,檢測這個指紋是否存在於fingerprints變數中,而fingerprints是一個集合,集合的元素都是不重復的。如果指紋存在,那麼就返回True,說明該Request是重復的,否則這個指紋加入到集合中。如果下次還有相同的Request傳遞過來,指紋也是相同的,那麼這時指紋就已經存在於集合中,Request對象就會直接判定為重復。這樣去重的目的就實現了。
Scrapy的去重過程就是,利用集合元素的不重復特性來實現Request的去重。
對於分布式爬蟲來說,我們肯定不能再用每個爬蟲各自的集合來去重了。因為這樣還是每個主機單獨維護自己的集合,不能做到共享。多台主機如果生成了相同的Request,只能各自去重,各個主機之間就無法做到去重了。
那麼要實現去重,這個指紋集合也需要是共享的,Redis正好有集合的存儲數據結構,我們可以利用Redis的集合作為指紋集合,那麼這樣去重集合也是利用Redis共享的。每台主機新生成Request之後,把該Request的指紋與集合比對,如果指紋已經存在,說明該Request是重復的,否則將Request的指紋加入到這個集合中即可。利用同樣的原理不同的存儲結構我們也實現了分布式Reqeust的去重。
四、防止中斷
在Scrapy中,爬蟲運行時的Request隊列放在內存中。爬蟲運行中斷後,這個隊列的空間就被釋放,此隊列就被銷毀了。所以一旦爬蟲運行中斷,爬蟲再次運行就相當於全新的爬取過程。
要做到中斷後繼續爬取,我們可以將隊列中的Request保存起來,下次爬取直接讀取保存數據即可獲取上次爬取的隊列。我們在Scrapy中指定一個爬取隊列的存儲路徑即可,這個路徑使用JOB_DIR變數來標識,我們可以用如下命令來實現:
scrapy crawl spider -s JOB_DIR=crawls/spider
更加詳細的使用方法可以參見官方文檔,鏈接為:https://doc.scrapy.org/en/latest/topics/jobs.html。
在Scrapy中,我們實際是把爬取隊列保存到本地,第二次爬取直接讀取並恢復隊列即可。那麼在分布式架構中我們還用擔心這個問題嗎?不需要。因為爬取隊列本身就是用資料庫保存的,如果爬蟲中斷了,資料庫中的Request依然是存在的,下次啟動就會接著上次中斷的地方繼續爬取。
所以,當Redis的隊列為空時,爬蟲會重新爬取;當Redis的隊列不為空時,爬蟲便會接著上次中斷之處繼續爬取。
五、架構實現
我們接下來就需要在程序中實現這個架構了。首先實現一個共享的爬取隊列,還要實現去重的功能。另外,重寫一個Scheer的實現,使之可以從共享的爬取隊列存取Request。
幸運的是,已經有人實現了這些邏輯和架構,並發布成叫Scrapy-Redis的Python包。接下來,我們看看Scrapy-Redis的源碼實現,以及它的詳細工作原理
3. Python分布式進程中你會遇到的坑
寫在前面
小驚大怪
你是不是在用Python3或者在windows系統上編程?最重要的是你對進程和線程不是很清楚?那麼恭喜你,在python分布式進程中,會有坑等著你去挖。。。(hahahaha,此處允許我嚇唬一下你)開玩笑的啦,不過,如果你知道序列中不支持匿名函數,那這個坑就和你say byebye了。好了話不多數,直接進入正題。
分布式進程
正如大家所知道的Process比Thread更穩定,而且Process可以分布到多台機器上,而Thread最多隻能分布到同一陸坦咐台機器的多個CPU上。Python的multiprocessing模塊不但支持多進程,其中managers子模塊還支持把多進程分布到多台機器上。一個服務進程可以作為調度者,將任務分布到其他多個進程中,依靠網路通信。由於managers模塊封裝很好,不必了解網路通信的細節,就可以很容易地編寫分布式多進程程序。
代碼記錄
舉個例子
如果我們已經有一個通過Queue通信的多進程程序在同一台機器上運行,現在,由於處理任務的進程任務繁重,希望把發送任務的進程和處理任務的進程分布到兩台機器上,這應該怎麼用分布式進程來實現呢?你已經知道了原有的Queue可以繼續使用,而且通過managers模塊把Queue通過網路暴露出去,就可以讓其他機器的進程來訪問Queue了。好,那我們就這么干!
寫個task_master.py
我們先看服務進程。服務進程負責啟動Queue,把Queue注冊到網路上,然後往Queue裡面寫入任務。
請注意,當我們在一台機器上寫多進程程序時,創建的Queue可以直接拿來用,但是,在分布式多進程環境下,添加任務到Queue不可以直接對原始的task_queue進行操作,那樣就繞過了QueueManager的封裝,必須通過manager.get_task_queue()獲得的Queue介面添加。然後,在另一台機器上啟動任務進程(本機上啟動也可以)
寫個task_worker.py
任務進程要通過網路連接到服務進程,所以要指定服務進程的IP。
運行結果
現在,可信沒以試試分布式進程的工作效果了。先啟動task_master.py服務進程:
task_master.py進程發送完任務後,開始等待result隊列的結果。現在啟動task_worker.py進程:
看到沒,結果都出錯了,我們好好分析一下到底哪出錯了。。。
錯誤分析
在task_master.py的報錯提示中,我們知道它說lambda錯誤,這是因為序列化不支持匿名函數,所以我們得修改代碼,重新對queue用QueueManager進行封裝放到網路中。
其中task_queue和result_queue是兩個隊列,分別存放任務和結果。它們用來進行進程間通信,交換對象。
因為是分布式的環境,放入queue中的數據需要等待Workers機器運算處理後再進行讀取,這樣就需要對queue用QueueManager進行封裝放到網路中,這是通過上面的2行代碼來實現的。我們給return_task_queue的網路調用介面取了一個名早純get_task_queue,而return_result_queue的名字是get_result_queue,方便區分對哪個queue進行操作。task.put(n)即是對task_queue進行寫入數據,相當於分配任務。而result.get()即是等待workers機器處理後返回的結果。
值得注意 在windows系統中你必須要寫IP地址,而其他操作系統比如linux操作系統則就不要了。
修改後的代碼
在task_master.py中修改如下:
在task_worker.py中修改如下:
先運行task_master.py,然後再運行task_worker.py
(1)task_master.py運行結果如下
(2)task_worker.py運行結果如下
知識補充
這個簡單的Master/Worker模型有什麼用?其實這就是一個簡單但真正的分布式計算,把代碼稍加改造,啟動多個worker,就可以把任務分布到幾台甚至幾十台機器上,比如把計算n*n的代碼換成發送郵件,就實現了郵件隊列的非同步發送。
Queue對象存儲在哪?注意到task_worker.py中根本沒有創建Queue的代碼,所以,Queue對象存儲在task_master.py進程中:
而Queue之所以能通過網路訪問,就是通過QueueManager實現的。由於QueueManager管理的不止一個Queue,所以,要給每個Queue的網路調用介面起個名字,比如get_task_queue。task_worker這里的QueueManager注冊的名字必須和task_manager中的一樣。對比上面的例子,可以看出Queue對象從另一個進程通過網路傳遞了過來。只不過這里的傳遞和網路通信由QueueManager完成。
authkey有什麼用?這是為了保證兩台機器正常通信,不被其他機器惡意干擾。如果task_worker.py的authkey和task_master.py的authkey不一致,肯定連接不上。
4. 如何用 Python 從海量文本抽取主題
代碼
我們在Jupyter Notebook中新建一個Python 2筆記本,起名為topic-model。
為了處理表格數據,我們依然使用數據框工具Pandas。先調用它。
import pandas as pd
然後讀入我們的數據文件datascience.csv,注意它的編碼是中文GB18030,不是Pandas默認設置的編碼,所以此處需要顯式指定編碼類型,以免出現亂碼錯誤。
df = pd.read_csv("datascience.csv", encoding='gb18030')
我們來看看數據框的頭幾行,以確認讀取是否正確。
df.head()
顯示結果如下:
沒問題,頭幾行內容所有列都正確讀入,文字顯式正常。我們看看數據框的長度,以確認數據是否讀取完整。
df.shape
執行的結果為:
(1024, 3)
行列數都與我們爬取到的數量一致,通過。
下面我們需要做一件重要工作——分詞。這是因為我們需要提取每篇文章的關鍵詞。而中文本身並不使用空格在單詞間劃分。
我們首先調用jieba分詞包。
import jieba
我們此次需要處理的,不是單一文本數據,而是1000多條文本數據,因此我們需要把這項工作並行化。這就需要首先編寫一個函數,處理單一文本的分詞。
def chinese_word_cut(mytext):
return " ".join(jieba.cut(mytext))
有了這個函數之後,我們就可以不斷調用它來批量處理數據框裡面的全部文本(正文)信息了。你當然可以自己寫個循環來做這項工作。
下面這一段代碼執行起來,可能需要一小段時間。請耐心等候。
df["content_cutted"] = df.content.apply(chinese_word_cut)
執行過程中可能會出現如下提示。沒關系,忽略就好。
Building prefix dict from the default dictionary ...
Loading model from cache /var/folders/8s//T/jieba.cache
Loading model cost 0.406 seconds.
Prefix dict has been built succesfully.
執行完畢之後,我們需要查看一下,文本是否已經被正確分詞。
df.content_cutted.head()