Python處理海量手機號碼


Python處理海量手機號碼

一、任務描述

上周,老板給我一個小任務:批量生成手機號碼並去重。給了我一個Excel表,里面是中國移動各個地區的可用手機號碼前7位(如下圖),里面有十三張表,每個表里的電話號碼前綴估計大概是八千個,需要這些7位號碼生成每個都生成后4位組成11位手機號碼,也就說每一個格子里面的手機號碼都要生成一萬個手機號。而且還有,本來服務器已經使用了一部分手機號碼了,要在生成的號碼列表里去掉已經使用過的那一批。已經使用過的這一批號碼已經導出到了一批txt文本里,約4000w,每個txt有10w個號碼,里面有重復的,老板估計實際大概是3000w左右。老板可以給我分配使用一個16G內存、8核CPU的Windows服務器來跑程序。

二、任務分析

要處理海量數據,所以程序的執行效率和占用內存不能太高,應該能在開發機的4G內存下也大概跑得動,即關掉所有編程IDE和服務器軟件,只用Notepad++和瀏覽器(用來查資料)的情況下不會卡機。這次任務可能會用到的技術有:程序Excel的處理,文件的遍歷和讀寫,大型數組的操作,多線程並發。預估任務完成周期:一周(日常工作正常進行的前提下)。

三、技術分析

PHP:很熟悉,但是執行效率和內存占用不夠好,可能會卡機,要實現多線程似乎有點復雜。(有待斟酌)

Javascript:較熟悉,執行效率和內存占用不太清楚,但是弱類型通常都比較堪憂,各種回調比較干擾思維不順手。(不考慮)

Java:略懂,學起來和寫起來都比較麻煩,開發效率比較慢。(有待斟酌)

C#:沒用過,較難學(比JAVA易,比腳本難)。(有待斟酌)

C/C++:略懂,數組處理、多線程這兩個似乎比較難搞。(不考慮)

Python:沒用過,據說很容易學,有個研究生同學用它來做物理運算等,執行效率應該不低。(試試看)

於是就打着試試看的心態,打開了菜鳥教程(Runoob)的Python教程大概看了一下,目錄中有幾個數組(List、元組、字典)、文件IO、File和多線程,看了一下例程果然好簡單。再度娘了一下python處理Excel,果斷簡單快捷!於是開啟了玩蛇之路。

四、合並(./4000w/hebing.py)

本文開頭說到,有4000w的已用號碼列表,但是里面是有重復的,而后面的處理都需要用到這些號碼。而看到這400多個txt文件加起來大小約500M,所以全部讀進去再進行處理也可以承受。所以先把這些文件讀進去合並去重,輸出成為一個txt文件。最后得到的號碼有1800w多條,輸出txt文件大小約209M。

  hebing.py

其中對數組內的元素進行合並去重的那一句是 txtArr = list( set(txtArr) ) 。很神奇對吧,這兩個是什么函數?其實這兩個都是轉換類型的函數。先把它轉換成了 set 類型,再轉換為 list 類型(列表/數組)。python的set(集合)類型是一個無序不重復的元素集,所以list轉換為set之后就自動去重了,當然同時順序也會被打亂了,不過這里的順序不重要就不用管它啦。

最后數組轉換為字符串也是直接用字符串拼接數組就轉換了,不要用for循環,非常非常耗時間的。

五、Excel處理(./preNum.py)

根據網上例程直接讀取Excel第一個表里面的內容出來,合並成數組轉成字符串存到文本里面去。在轉成字符串的時候發現報錯似乎是說數據類型不對,才知道原來python與PHP、JS不同,是強類型的=_=!於是先在Excel里把表里面的數據轉換成字符串格式(excel里准確叫文本格式),轉換后excel表里面的數據格左上角是有綠色的小三角形的。每個表單獨處理生成一個文件,一個文件里面大概有八千個手機號碼前綴。

  preNum.py

六、生成號碼並去掉已用過的

首先來想想,有十三個表,一個表里面有大概八千個號碼前綴,每個前綴生成一萬個號碼,每個號碼要與前面所說的1800萬的已用手機號碼進行比對去重。你會怎么做呢???

↓↓

↓↓

我的想法是,分成十三次來做,每次一個表,每個表中八千多個號碼,使用多線程,八千多個線程,每個線程生成一萬個號碼並與那1800萬個號碼一 一比對。

其中生成和去重的核心代碼如下,每生成一個號碼的時間大概是0.5秒。所以估計了一下時間,10000*0.5s ≈ 83min,八千個線程大概要一個半小時左右。

復制代碼
#這里是重復的號碼
#fileobj = open('testBugNum.txt');    #測試的
fileobj = open('list-dataAll.txt');    #實際的
bugTxtStr = fileobj.read();
fileobj.close();
bugTxtArr = bugTxtStr.splitlines();    #已用過的號碼的列表

while j < 10000:    #生成一萬個同前綴號碼
    newNum = str( int(txtNum)*10000+j );
    j += 1;
#        print newNum;

    if not newNum in bugTxtArr:        #如果不在已用過的號碼列表里
        numArr.append( '+86'+newNum );
        print '+86'+newNum;
復制代碼

我在本機大概運行了一下,觀察了幾分鍾,似乎線程創建得比較慢,有些已經跑到了十幾個了,有的才剛創建線程。線程調度嘛,不按照順序嘛,除了輸出很亂以外,似乎也沒有什么其它問題。於是就上傳到服務器上去跑了,然后再過了一會就下班回家了。第二天回到公司,連上服務器看看,出乎意料啊,看到輸出的信息里面,那些線程才跑到 三百多,天吶什么時候才能跑到一萬啊。而且還有坑爹的是,偶爾就看到有些線程創建失敗⊙o⊙

七、思考思考(./doData.py)

從前面的運行信息來看,這里使用多線程似乎並沒有加快程序的運行啊,這是為什么呢?如果不能用多線程,那么生成比對的地方就要改成另外更高效的方式了,有嗎?

第一問,從網上找到答案,確實說在計算密集型程序中,多線程比單線程更糟,因為一個CPU就那么幾個核,不同的線程還是一樣要占用CPU資源,再加上線程調度的時間和空間,真是天坑。第二問,PHP中有從一個數組去除另一個數組的函數(官方說法叫做數組的差集 array_diff() ),那么python應該也會有這樣的函數,先成一萬個號碼的數組再進行差集 會不會比原來 每個號碼對比再合並效率快呢?

實踐了一下,證明確實效率高了極多極多,python中的數組差集是這個樣子的 numArr = list( set(numArr) - set(bugTxtArr) ) 測了一下,大概三秒完成一批號碼(一批約等於一萬個號碼),之前是半秒一個號碼\( ^▽^ )/。但也注意現在生成的一萬個號碼排序是亂的,因為中間轉換成的set類型是無序的,如果需要從小到大排序,那還要再加個函數排序一下,問了老板說不用按順序,那就直接這樣了。

期間調試的時候發現,使用多線程有時會報錯 Unhandled exception in thread started by sys.excepthook is missing 之類的,網上查資料說是因為主進程已經執行完畢,那么其創建的線程就會被關掉。所以我的做法就是讓主進程最后為一直執行空語句,很像當年用C語言做單片機的做法呢→_→雖然最后不用多線程了,直接單線程處理,安全穩定。

  doData.py

八、合並整理(./hbData.py)

經過了大概六個小時的號碼生成,最后就是把一個Excel表生成的八千多個文件整理,每十個文件合成一個,每個文件約十萬個號碼,每個號碼前面加上 “+86” 。就遍歷一下目錄,沒什么技術點就不詳說了。

  hbData.py

后話,總共只有十三個表,這些程序稍微改一下,執行十三次就行了。值得注意的是,我這里的程序幾乎每個都有一個全局變量 tblIndex, 是以防一文件里面一個個修改目錄名和文件名,疏忽有可能導致的數據覆蓋。

總結

  1. 使用腳本語言有一個很重要的要點:要盡量用語言提供的函數,不要自己實現算法,尤其是循環的那種,執行速度不在一個數量級。
  2. 處理大批量的數據,要拆分步驟,生成中間文件。大量數據復雜操作要小批量小批量地慢慢調試,結果無誤才逐步切換成真實數據。
  3. 多線程在運算密集型的場景中是沒有用武之處的,就算CPU是多核也沒什么用,反而會造成順序隨機不易觀察,線程不穩定容易出錯,線程間切換內存消耗加大等弊端。
  4. 據說GPU可以用來挖礦、暴力破解等,對本場景這種高並發、簡單邏輯的運算應該也非常適用,以后可能要用得上GPU編程(求推薦教程)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM