使用多线程下载NCBI文件的Python方法

2 投票
3 回答
643 浏览
提问于 2025-04-17 23:28

最近我开始负责从ncbi数据库下载大量文件。不过,我遇到了一些需要创建多个数据库的情况。这里的代码可以用来下载ncbi网站上的所有病毒文件。我的问题是,有没有办法加快这些文件的下载速度呢?

目前这个程序的运行时间超过了5个小时。我尝试过多线程下载,但一直没能成功,因为有些文件下载时间超过10秒,我不知道怎么处理这种卡住的情况。(我刚开始学习编程)另外,如何处理urllib2.HTTPError: HTTP Error 502: Bad Gateway这个错误呢?我有时会在某些重启和最大重启次数的组合下遇到这个问题。这会导致程序崩溃,我不得不通过改变for语句中的0来从不同的位置重新开始下载。

import urllib2
from BeautifulSoup import BeautifulSoup

#This is the SearchQuery into NCBI. Spaces are replaced with +'s.
SearchQuery = 'viruses[orgn]+NOT+Retroviridae[orgn]'
#This is the Database that you are searching.
database = 'protein'
#This is the output file for the data
output = 'sample.fasta'


#This is the base url for NCBI eutils.
base = 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/'
#Create the search string from the information above
esearch = 'esearch.fcgi?db='+database+'&term='+SearchQuery+'&usehistory=y'
#Create your esearch url
url = base + esearch
#Fetch your esearch using urllib2
print url
content = urllib2.urlopen(url)
#Open url in BeautifulSoup
doc = BeautifulSoup(content)
#Grab the amount of hits in the search
Count = int(doc.find('count').string)
#Grab the WebEnv or the history of this search from usehistory.
WebEnv = doc.find('webenv').string
#Grab the QueryKey
QueryKey = doc.find('querykey').string
#Set the max amount of files to fetch at a time. Default is 500 files.
retmax = 10000
#Create the fetch string
efetch = 'efetch.fcgi?db='+database+'&WebEnv='+WebEnv+'&query_key='+QueryKey
#Select the output format and file format of the files. 
#For table visit: http://www.ncbi.nlm.nih.gov/books/NBK25499/table/chapter4.chapter4_table1
format = 'fasta'
type = 'text'
#Create the options string for efetch
options = '&rettype='+format+'&retmode='+type


#For statement 0 to Count counting by retmax. Use xrange over range
for i in xrange(0,Count,retmax):
    #Create the position string
    poision = '&retstart='+str(i)+'&retmax='+str(retmax)
    #Create the efetch URL
    url = base + efetch + poision + options
    print url
    #Grab the results
    response = urllib2.urlopen(url)
    #Write output to file
    with open(output, 'a') as file:
        for line in response.readlines():
            file.write(line)
    #Gives a sense of where you are
    print Count - i - retmax

3 个回答

0

有两种方法可以完成你的任务。第一种是使用进程而不是线程,你应该使用的模块是 multiprocess。第二种是基于事件的方式,gevent 是合适的模块。

502 错误并不是你脚本的问题。简单来说,可以使用以下模式来进行重试。

try_count = 3
while try_count > 0:
    try:
        download_task()
    except urllib2.HTTPError:
        clean_environment_for_retry()
    try_count -= 1

在 except 这一行,你可以根据具体的 HTTP 状态码来细化处理,做一些特别的事情。

0

你应该使用多线程,这样下载任务会更有效率。

"these files take more than 10seconds to download and I do not know how to handle stalling",

我觉得这不会是个问题,因为Python的多线程可以处理这个情况,或者我更想说,多线程就是为了处理这种需要输入输出的工作。当一个线程在等待下载完成时,CPU会让其他线程去做它们的事情。

总之,你最好还是试试看,看看会发生什么。

5

要使用多个线程下载文件:

#!/usr/bin/env python
import shutil
from contextlib import closing
from multiprocessing.dummy import Pool # use threads
from urllib2 import urlopen

def generate_urls(some, params): #XXX pass whatever parameters you need
    for restart in range(*params):
        # ... generate url, filename
        yield url, filename

def download((url, filename)):
    try:
        with closing(urlopen(url)) as response, open(filename, 'wb') as file:
            shutil.copyfileobj(response, file)
    except Exception as e:
        return (url, filename), repr(e)
    else: # success
        return (url, filename), None

def main():
    pool = Pool(20) # at most 20 concurrent downloads
    urls = generate_urls(some, params)
    for (url, filename), error in pool.imap_unordered(download, urls):
        if error is not None:
           print("Can't download {url} to {filename}, "
                 "reason: {error}".format(**locals())

if __name__ == "__main__":
   main()

撰写回答