urllib2.HTTPError Python错误
我有一个文件,里面有GI编号,我想从ncbi获取FASTA
序列。
from Bio import Entrez
import time
Entrez.email ="eigtw59tyjrt403@gmail.com"
f = open("C:\\bioinformatics\\gilist.txt")
for line in iter(f):
handle = Entrez.efetch(db="nucleotide", id=line, retmode="xml")
records = Entrez.read(handle)
print ">GI "+line.rstrip()+" "+records[0]["GBSeq_primary-accession"]+" "+records[0]["GBSeq_definition"]+"\n"+records[0]["GBSeq_sequence"]
time.sleep(1) # to make sure not many requests go per second to ncbi
f.close()
这个脚本运行得很好,但在处理几个序列后,我突然收到了这个错误信息。
Traceback (most recent call last):
File "C:/Users/Ankur/PycharmProjects/ncbiseq/getncbiSeq.py", line 7, in <module>
handle = Entrez.efetch(db="nucleotide", id=line, retmode="xml")
File "C:\Python27\lib\site-packages\Bio\Entrez\__init__.py", line 139, in efetch
return _open(cgi, variables)
File "C:\Python27\lib\site-packages\Bio\Entrez\__init__.py", line 455, in _open
raise exception
urllib2.HTTPError: HTTP Error 500: Internal Server Error
当然,我可以使用http://www.ncbi.nlm.nih.gov/sites/batchentrez
,但我想创建一个自动化的流程,所以希望能有一些自动化的方式。
我该如何防止ncbi把我“踢出去”呢?
3 个回答
这是一种“正常”的Entrez API临时故障,即使你遵循了所有Entrez API的规则,也可能会发生这种情况。Biopython的文档在这一部分中解释了如何处理这种情况。
有时候,你会遇到来自Entrez的间歇性错误,比如HTTPError 5XX。我们使用一种“尝试-捕获-暂停-重试”的方式来解决这个问题。例如,
# This assumes you have already run a search as shown above, # and set the variables count, webenv, query_key try: from urllib.error import HTTPError # for Python 3 except ImportError: from urllib2 import HTTPError # for Python 2 batch_size = 3 out_handle = open("orchid_rpl16.fasta", "w") for start in range(0, count, batch_size): end = min(count, start+batch_size) print("Going to download record %i to %i" % (start+1, end)) attempt = 0 while attempt < 3: attempt += 1 try: fetch_handle = Entrez.efetch(db="nucleotide", rettype="fasta", retmode="text", retstart=start, retmax=batch_size, webenv=webenv, query_key=query_key, idtype="acc") except HTTPError as err: if 500 <= err.code <= 599: print("Received error from server %s" % err) print("Attempt %i of 3" % attempt) time.sleep(15) else: raise data = fetch_handle.read() fetch_handle.close() out_handle.write(data) out_handle.close()
所以你不用为这个错误感到内疚,只需要捕捉到它就可以了。
有一种解决办法叫做efetch。你可以把你的列表分成每批200个(我觉得这个批量大小还不错),然后用efetch一次性发送所有这些ID。
首先,这样做比发送200个单独的请求快得多。其次,这样也能有效遵守“每秒3个请求”的规则,因为每个请求的处理时间超过0.33秒,但不会太长。
不过,你需要一个机制来处理那些“坏苹果”。如果你的200个ID中有一个是无效的,NCBI会返回0个结果。换句话说,只有当你所有的200个ID都是有效的,NCBI才会返回结果。
如果遇到坏苹果,我会逐个检查这200个ID,忽略掉无效的那个。这种“如果有坏苹果”的情况也提醒你不要把批量做得太大,以防出现坏苹果。如果批量太大,首先,出现坏苹果的机会就更大,也就是说你更频繁地需要检查整个列表。其次,批量越大,你需要检查的单个项目就越多。
我用以下代码来下载CAZy蛋白质,效果很好:
import urllib2
prefix = "http://www.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&rettype=fasta&id="
id_per_request = 200
def getSeq (id_list):
url = prefix + id_list[:len(id_list)-1]
temp_content = ""
try:
temp_content += urllib2.urlopen(url).read()
### if there is a bad apple, try one by one
except:
for id in id_list[:len(id_list)-1].split(","):
url = prefix + id
#print url
try:
temp_content += urllib2.urlopen(url).read()
except:
#print id
pass
return temp_content
content = ""
counter = 0
id_list = ""
#define your accession numbers first, here it is just an example!!
accs = ["ADL19140.1","ABW01768.1","CCQ33656.1"]
for acc in accs:
id_list += acc + ","
counter += 1
if counter == id_per_request:
counter = 0
content += getSeq(id_list)
id_list = ""
if id_list != "":
content += getSeq(id_list)
id_list = ""
print content
我对ncbi API不太熟悉,但我猜可能是你违反了某种请求频率限制(即使你用了“sleep(1)”),所以你之前的请求能成功,但在发了几次请求后,服务器发现你请求得太频繁,就把你给屏蔽了。这对你来说很麻烦,因为你的代码里没有错误处理。
我建议你把获取数据的部分放在一个try/except块里,这样如果遇到问题,脚本就可以等更久再试。如果实在不行,就把导致错误的id写到一个文件里,然后继续执行(以防这个id是问题的根源,可能导致Entrez库生成了一个错误的URL)。
试着把你的代码改成这样(未经测试):
from urllib2 import HTTPError
from Bio import Entrez
import time
def get_record(_id):
handle = Entrez.efetch(db="nucleotide", id=_id, retmode="xml")
records = Entrez.read(handle)
print ">GI "+line.rstrip()+" "+records[0]["GBSeq_primary-accession"]+" "+records[0]["GBSeq_definition"]+"\n"+records[0]["GBSeq_sequence"]
time.sleep(1) # to make sure not many requests go per second to ncbi
Entrez.email ="eigtw59tyjrt403@gmail.com"
f = open("C:\\bioinformatics\\gilist.txt")
for id in iter(f):
try:
get_record(id)
except HTTPError:
print "Error fetching", id
time.sleep(5) # we have angered the API! Try waiting longer?
try:
get_record(id)
except:
with open('error_records.bad','a') as f:
f.write(str(id)+'\n')
continue #
f.close()