我有一个Python脚本,我希望每天都运行,我更喜欢它只需要1-2个小时才能运行。它目前设置为给定URL命中4个不同的API,捕获结果,然后将数据保存到PostgreSQL数据库中。问题是我有超过160,000个URL需要通过,脚本最终耗费了很长时间 - 我进行了一些初步测试,以当前格式浏览每个URL需要36个小时。所以,我的问题归结为:我应该优化我的脚本同时运行多个线程吗?或者我应该扩展我使用的服务器数量?显然第二种方法会更昂贵,所以我更喜欢在同一个实例上运行多个线程。
我正在使用我创建的库(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 :(得分: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可以为您提供帮助。因为它可以优化处理同步阻塞请求序列的时间花费。