2014-11-08 46 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