2014-11-08 85 views
1

我有一個Python腳本,我想每天運行,我寧願只需要1-2個小時即可運行。目前,它已經設置爲針對給定URL命中4個不同的API,捕獲結果,然後將數據保存到PostgreSQL數據庫中。問題是,我有超過160,000個URL需要經過,腳本花費了很長時間 - 我進行了一些初步測試,並且要花36個小時才能瀏覽每個網址的當前格式。所以,我的問題歸結爲:我應該優化腳本以同時運行多個線程嗎?還是應該擴大我使用的服務器數量?很明顯,第二種方法會更昂貴,所以我更願意在同一個實例上運行多個線程。提高Python腳本的速度:多線程還是多個實例?

我正在使用我創建的庫(SocialAnalytics),它提供了訪問不同API端點並解析結果的方法。以下是我已經配置了我的腳本:

import psycopg2 
from socialanalytics import pinterest 
from socialanalytics import facebook 
from socialanalytics import twitter 
from socialanalytics import google_plus 
from time import strftime, sleep 

conn = psycopg2.connect("dbname='***' user='***' host='***' password='***'") 
cur = conn.cursor() 

# Select all URLs 
cur.execute("SELECT * FROM urls;") 
urls = cur.fetchall() 

for url in urls: 

    # Pinterest 
    try: 
     p = pinterest.getPins(url[2]) 
    except: 
     p = { 'pin_count': 0 } 
    # Facebook 
    try: 
     f = facebook.getObject(url[2]) 
    except: 
     f = { 'comment_count': 0, 'like_count': 0, 'share_count': 0 } 
    # Twitter 
    try: 
     t = twitter.getShares(url[2]) 
    except: 
     t = { 'share_count': 0 } 
    # Google 
    try: 
     g = google_plus.getPlusOnes(url[2]) 
    except: 
     g = { 'plus_count': 0 } 

    # Save results 
    try: 
     now = strftime("%Y-%m-%d %H:%M:%S") 
     cur.execute("INSERT INTO social_stats (fetched_at, pinterest_pins, facebook_likes, facebook_shares, facebook_comments, twitter_shares, google_plus_ones) VALUES(%s, %s, %s, %s, %s, %s, %s, %s);", (now, p['pin_count'], f['like_count'], f['share_count'], f['comment_count'], t['share_count'], g['plus_count'])) 
     conn.commit() 
    except: 
     conn.rollback() 

你可以看到每個調用API使用Requests library,這是一個同步的,阻塞的事情。經過一些初步研究後,我發現Treq,這是一個在Twisted之上的API。 Twisted的異步非阻塞特性似乎是改進我的方法的好選擇,但我從來沒有使用它,我不確定它究竟會如何(以及如何)幫助我實現我的目標。

任何指導非常感謝!

+0

各種url請求是否需要共享內存空間?如果不是,一個簡單的解決方案將是使用多處理。對於一些快速代碼,請參閱:http://stackoverflow.com/questions/3842237/parallel-processing-in-python – duhaime 2014-11-09 02:44:41

+0

不,他們不需要共享內存空間。我可以考慮削減所需時間的唯一方法是運行10個進程,每個進程處理自己的URL。但是,我並不確定如何完成此操作。我用這種方法走向正確的方向嗎? https://gist.github.com/anonymous/b337afbd8f92d3991b47 – Abundnce10 2014-11-09 17:55:42

+0

對不起,剛剛得到了這個。似乎所有的工作:) – duhaime 2014-11-09 21:35:41

回答

2

首先,您應該測量腳本在每一步上花費的時間。可能你會發現一些有趣的事情:)

其次,你可以在塊分割你的網址:

chunk_size = len(urls)/cpu_core_count; // don't forget about remainder of division

完成這些步驟後,您可以使用multiprocessing爲並行處理每一個數據塊。這裏是你的例子:

import multiprocessing as mp 

p = mp.Pool(5) 

# first solution 
for urls_chunk in urls: # urls = [(url1...url6),(url7...url12)...] 
    res = p.map(get_social_stat, urls_chunk) 
    for record in res: 
     save_to_db(record) 

# or, simple 
res = p.map(get_social_stat, urls) 

for record in res: 
    save_to_db(record) 

另外,gevent可以幫助你。因爲它可以優化處理同步阻塞請求序列的時間花費。

+0

當然,你可以派生10個進程。但更好的是通過性能測試找到最佳數量。嘗試使用不同數量的進程分析100個網址。嘗試以70-80%的百分比加載你的CPU。 – Jimilian 2014-11-09 19:25:48

+0

我使用了上面的代碼,發現10個進程只會將我的CPU使用率提高到35%左右。但是,經過幾次測試,我發現我達到了Facebook的API限制。我現在正在獲得'(#4)已達到應用程序請求限制'http://stackoverflow.com/questions/14092989/facebook-api-4-application-request-limit-reached。謝謝你的幫助! – Abundnce10 2014-11-09 20:41:17