2013-09-04 3 views
1

Я новичок в python. У меня возникли проблемы с чтением содержимого tarfile в python.Читать содержание Tarfile в Python - «поиск назад не разрешен»

Данные являются содержимым статьи журнала (размещенной в центральном офисе). См. Информацию ниже. И ссылку на tarfile, которую я хочу прочитать в Python.

http://www.pubmedcentral.nih.gov/utils/oa/oa.fcgi?id=PMC13901 ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/b0/ac/Breast_Cancer_Res_2001_Nov_9_3(1)_61-65.tar.gz

У меня есть список подобных файлов .tar.gz я в конце концов, будет нужно читать, а также. Я думаю (знаю), все файлы tarfiles связаны с ними .nxml-файлом. Это содержимое файлов .nxml, на которых я действительно заинтересован в извлечении/чтении. Открыта для любых предложений по наилучшему способу сделать это ...

Вот что у меня есть, если я сохраню tarfile на своем ПК. Все работает как ожидалось.

tarfile_name = "F:/PMC_OA_TextMining/Breast_Cancer_Res_2001_Nov_9_3(1)_61-65.tar.gz" 
tfile = tarfile.open(tarfile_name) 

tfile_members = tfile.getmembers() 

tfile_members1 = [] 
for i in range(len(tfile_members)): 
tfile_members_name = tfile_members[i].name 
tfile_members1.append(tfile_members_name) 

tfile_members2 = [] 
for i in range(len(tfile_members1)): 
if tfile_members1[i].endswith('.nxml'): 
    tfile_members2.append(tfile_members1[i]) 

tfile_extract1 = tfile.extractfile(tfile_members2[0]) 
tfile_extract1_text = tfile_extract1.read() 

Я сегодня узнал, что для того, чтобы получить доступ к файлу обработаны непосредственно из PubMed централей FTP сайта я должен настроить сетевой запрос с использованием urllib. Ниже приводится пересмотренный код (и ссылку на StackOverflow ответ, который я получил):

Read contents of .tar.gz file from website into a python 3.x object

tarfile_name = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/b0/ac/Breast_Cancer_Res_2001_Nov_9_3(1)_61-65.tar.gz" 
ftpstream = urllib.request.urlopen(tarfile_name) 
tfile = tarfile.open(fileobj=ftpstream, mode="r|gz") 

Однако, когда я запускаю оставшуюся часть кода (ниже) я получаю сообщение об ошибке ("поиск в обратном направлении не допускается"). Как так?

tfile_members = tfile.getmembers() 

tfile_members1 = [] 
for i in range(len(tfile_members)): 
tfile_members_name = tfile_members[i].name 
tfile_members1.append(tfile_members_name) 

tfile_members2 = [] 
for i in range(len(tfile_members1)): 
if tfile_members1[i].endswith('.nxml'): 
    tfile_members2.append(tfile_members1[i]) 

tfile_extract1 = tfile.extractfile(tfile_members2[0]) 
tfile_extract1_text = tfile_extract1.read() 

Код не работает в последней строке, где я пытаюсь прочитать содержимое .nxml, связанное с моим tarfile. Ниже приведено фактическое сообщение об ошибке. Что это значит? Каково наилучшее решение для чтения/доступа к содержимому этих файлов .nxml, которые все встроены в tarfiles?

Traceback (most recent call last): 
File "F:\PMC_OA_TextMining\test2.py", line 135, in <module> 
tfile_extract1_text = tfile_extract1.read() 
File "C:\Python30\lib\tarfile.py", line 804, in read 
buf += self.fileobj.read() 
File "C:\Python30\lib\tarfile.py", line 715, in read 
return self.readnormal(size) 
File "C:\Python30\lib\tarfile.py", line 722, in readnormal 
self.fileobj.seek(self.offset + self.position) 
File "C:\Python30\lib\tarfile.py", line 531, in seek 
raise StreamError("seeking backwards is not allowed") 
tarfile.StreamError: seeking backwards is not allowed 

Заранее за вашу помощь. Chris

ответ

9

Ошибка: Тарные файлы хранятся чередующимися. Они входят в заголовок, данные, заголовок, данные, заголовок, данные и т. Д. Когда вы перечисляете файлы с getmembers(), вы уже прочитали весь файл, чтобы получить заголовки. Затем, когда вы попросили объект tarfile прочитать данные, он попытался отыскать назад от последнего заголовка до первых данных. Но вы не можете искать назад в сетевом потоке, не закрывая и не открывая запрос urllib.

Как обходить его: Вам необходимо загрузить файл, сохранить временную копию на диск или в StringIO, перечислить файлы в этой временной копии, а затем извлечь нужные файлы.

#!/usr/bin/env python3 
from io import BytesIO 
import urllib.request 
import tarfile 

tarfile_url = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/b0/ac/Breast_Cancer_Res_2001_Nov_9_3(1)_61-65.tar.gz" 
ftpstream = urllib.request.urlopen(tarfile_url) 

# BytesIO creates an in-memory temporary file. 
# See the Python manual: http://docs.python.org/3/library/io.html 
tmpfile = BytesIO() 
while True: 
    # Download a piece of the file from the connection 
    s = ftpstream.read(16384) 

    # Once the entire file has been downloaded, tarfile returns b'' 
    # (the empty bytes) which is a falsey value 
    if not s: 
     break 

    # Otherwise, write the piece of the file to the temporary file. 
    tmpfile.write(s) 
ftpstream.close() 

# Now that the FTP stream has been downloaded to the temporary file, 
# we can ditch the FTP stream and have the tarfile module work with 
# the temporary file. Begin by seeking back to the beginning of the 
# temporary file. 
tmpfile.seek(0) 

# Now tell the tarfile module that you're using a file object 
# that supports seeking backward. 
# r|gz forbids seeking backward; r:gz allows seeking backward 
tfile = tarfile.open(fileobj=tmpfile, mode="r:gz") 

# You want to limit it to the .nxml files 
tfile_members2 = [filename 
        for filename in tfile.getnames() 
        if filename.endswith('.nxml')] 

tfile_extract1 = tfile.extractfile(tfile_members2[0]) 
tfile_extract1_text = tfile_extract1.read() 

# And when you're done extracting members: 
tfile.close() 
tmpfile.close() 
+0

Как сохранить временную копию к 'StringIO'? К сожалению, мы работаем с Python менее чем за неделю. – Chris

+0

Я добавил рабочий код Python 3, чтобы сделать то, что вам кажется. –

+0

Спасибо за подробное и полезное объяснение @tepples – Chris

0

У меня была такая же ошибка при попытке requests.get файла, поэтому я извлек все директории TMP вместо использования BytesIO или extractfile(member):

# stream == requests.get 
inputs = [tarfile.open(fileobj=LZMAFile(stream), mode='r|')] 
t = "/tmp" 
for tarfileobj in inputs:   
    tarfileobj.extractall(path=t, members=None) 
for fn in os.listdir(t): 
    with open(os.path.join(t, fn)) as payload: 
     print(payload.read())