使用gevent下载多个文件

1 投票
1 回答
1268 浏览
提问于 2025-04-18 07:22

我正在尝试使用 [gevent][1] 并行下载一系列文件。

我的代码稍微修改了一下这里建议的代码,链接在这里

monkey.patch_all()

def download_xbrl_files(download_folder, yq, list_of_xbrl_urls):
    def download_and_save_file(url, yr, qtr):
        if url is not None:
            full_url = "http://edgar.sec.gov" + url
            if not os.path.exists(full_url):
                try:
                    content = urllib2.urlopen(full_url).read()
                    filename = download_folder + "/" + str(y) + "/" + q + "/" + url.split('/')[-1]
                    print "Saving: ", filename
                    f_raw = open(filename, "w")
                    f = FileObject(f_raw, "w")
                    try:
                        f.write(content)
                    finally:
                        f.close()
                        return 'Done'
                except:
                    print "Warning: can't save or access for item:", url
                    return None
            else:
                return 'Exists'
        else:
            return None
    (y, q) = yq
    if utls.has_elements(list_of_xbrl_urls):
        filter_for_none = filter(lambda x: x is not None, list_of_xbrl_urls)
        no_duplicates = list(set(filter_for_none))
        download_files = [gevent.spawn(lambda x: download_and_save_file(x, y, q), x) for x in no_duplicates]
        gevent.joinall(download_files)
        return 'completed'
    else:
        return 'empty'

这段代码的工作流程是:

  1. 先进行一些清理工作。
  2. 然后用 gevent.spawn 启动 download_and_save_file 函数,具体步骤如下:
  3. 检查文件是否已经下载过。
  4. 如果没有,就用 urllib2.urlopen(full_url).read() 下载内容。
  5. 接着借助 gevent的 FileObject 保存文件。

我感觉 download_and_save 这个函数似乎只是在顺序执行。此外,我的应用程序会进入待机状态。我可以加一个 timeout,但我想在代码中优雅地处理失败情况。

我在想是不是哪里做错了——这是我第一次写Python代码。

编辑

这里是一个使用“线程”的代码版本。

def download_xbrl_files(download_folder, yq_and_url):
    (yq, url) = yq_and_url
    (yr, qtr) = yq
    if url is not None and url is not '':
        full_url = "http://edgar.sec.gov" + url
        filename = download_folder + "/" + str(yr) + "/" + qtr + "/" + url.split('/')[-1]
        if not os.path.exists(filename):
            try:
                content = urllib2.urlopen(full_url).read()
                print "Saving: ", filename
                f = open(filename, "wb")
                try:
                    f.write(content)
                    print "Writing done: ", filename
                finally:
                    f.close()
                    return 'Done'
            except:
                print "Warning: can't save or access for item:", url
                return None
        else:
            print "Exists: ", filename
            return 'Exists'
    else:
        return None


def download_filings(download_folder, yq_and_filings):
    threads = [threading.Thread(target=download_xbrl_files, args=(download_folder, x,)) for x in yq_and_filings]
    [thread.start() for thread in threads]
    [thread.join() for thread in threads]

1 个回答

1

我深入研究了一下,发现基本问题是 gevent.spawn() 创建的是绿色线程(greenlets),而不是独立的进程(所有绿色线程都在一个操作系统线程中运行)。

你可以试试下面这个简单的代码:

import gevent
from time import sleep
g = [gevent.spawn(sleep, 1) for x in range(100)]
gevent.joinall(g)

你会发现这个过程花了 100 秒。这就证明了上面的观点。

你其实是想要多线程的功能,这个可以在 threading 模块中找到。你可以看看这个问题:如何在 Python 中使用多线程?,里面有一些简单的用法。

---更新---

这里有一个快速示例,告诉你怎么做:

threads = [threading.Thread(target=sleep, args=(1,)) for x in range(10)]
[thread.start() for thread in threads]
[thread.join() for thread in threads]

撰写回答