首页 > 解决方案 > 使用 Python 多处理加速抓取?

问题描述

我正在编写一个脚本,该脚本使用该模块从 Reddit 中刮取评论,并使用frompraw对刮取的内容应用情绪分析。这是我的功能:SentimentIntensityAnalyzernltk.sentiment

def analyze_keyword_reddit(keywords):

    results = reddit.subreddit("all").search(keywords, sort="comments", limit=None)

    all_posts = ""
    all_comments = 0
    all_upvotes = 0

    for post in results:
        all_comments += post.num_comments
        all_upvotes += post.score

        # get all posts for keyword
        submission = reddit.submission(id=post.id)
        submission.comments.replace_more(limit=0, threshold=10)

        posts = " ".join([post.body.lower() for post in submission.comments.list()])
        all_posts = all_posts + " " + posts

    polarity_scores = sia.polarity_scores(all_posts)

    return {**{'all_comments': all_comments, 'all_upvotes': all_upvotes}, **polarity_scores}

在分析这个函数的运行时间时,超过 90% 的运行时间发生在展平评论树的步骤(这一行:)submission.comments.replace_more(limit=0, threshold=5)。遗憾的是,我还没有发现太多关于此命令的运行时间是否可以显着改善(除了设置threshold参数并因此限制检索到的评论数量),因此我开始探索多处理选项。使用上面的函数,我运行了以下命令:

if __name__ == '__main__':
    tic = time.time()
    with multiprocessing.Pool() as p:
        print(p.starmap(analyze_keyword_reddit, ['$u unity stock','$fsly fastly stock','$ttcf tattooed chef stock']))
    toc = time.time()

然后,为了比较,我还运行了以下一个没有多处理:

tic = time.time()
for t in ['$u unity stock','$fsly fastly stock','$ttcf tattooed chef stock']:
    print(analyze_keyword_reddit(t))
toc = time.time()
print(f"Scraping without multiprocessing: {round(toc-tic,2)} seconds.")

普通版耗时 128 秒。然而,多处理版本似乎创建了四个线程,并行评估完全相同的输入四次(当我添加一个简单的print(keywords)into时,这一点变得很明显analyze_keyword_reddit())。这四个线程分别花费了 708、727、729 和 731 秒。此外,多处理片段似乎仍处于无限循环中 - 它并没有在第三个关键字的末尾停止。

我的实施哪里错了?我的目标是加快抓取过程,我是否走在正确的轨道上,还是应该完全采用另一种实现方式?


编辑: 根据下面 Ron Serruya 的精彩回复,我更新了现在使用的代码,map()而不是starmap()

if __name__ == '__main__':
    tic = time.time()
    with Pool() as p:
        print(p.map(analyze_keyword_reddit, ['$u unity stock','$fsly fastly stock','$ttcf tattooed chef stock']))
    toc = time.time()
    print(f"Scraping with multiprocessing: {round(toc-tic,2)} seconds.")

然而,这一次这个过程似乎还没有开始。该功能analyze_keyword_reddit似乎永远不会被评估。我在它的开头添加了一个打印功能,终端上根本没有输出。

标签: pythonmultiprocessingpraw

解决方案


尝试使用map而不是starmap

Starmap 期望 args 是可迭代的,然后将可迭代解包到函数中

所以p.starmap(analyze_keyword_reddit, ["hello", "world"]) 会打电话analyze_keyword_reddit(['h','e','l','l','o'])analyze_keyword_reddit(['w','o','r','l','d'])

我可以假设它使 Reddit 库更努力地工作

In [1]: from multiprocessing import Pool

In [2]: with Pool() as p:
   ...:     p.starmap(print, ["hello", "world"])
   ...:
h e l l o
w o r l d

In [3]: with Pool() as p:
   ...:     p.map(print, ["hello", "world"])
   ...:
hello
world

推荐阅读