Парсинг XML-файла с URL-адреса на астропию, которую можно скачать без загрузки - PullRequest
0 голосов
/ 07 октября 2019

Из http://svo2.cab.inta -csic.es / теория / fps / вы можете получить кривые пропускания для многих фильтров, используемых в астрономических наблюдениях. Я хотел бы получить эти данные, открыв URL-адрес с соответствующим XML-файлом (для каждого фильтра), проанализировав его с votable astropy, который помогает легко читать данные таблицы.

Мне удалось сделать это, открыв файл, преобразовав его в файл UTF-8 и сохранив локально в формате XML. Тогда открытие локального файла работает нормально, как видно из следующего примера.

Однако я не хочу сохранять файл и открывать его снова. Когда я попробовал это, выполнив: votable = parse (xml_file), он вызывает OSError: File name слишком долго, так как он принимает весь файл в виде строки.

from urllib.request import urlopen

fltr = 'http://svo2.cab.inta-csic.es/theory/fps/fps.php?ID=2MASS/2MASS.H'
url = urlopen(fltr).read()
xml_file = url.decode('UTF-8')
with open('tmp.xml','w') as out:
    out.write(xml_file)

votable = parse('tmp.xml')
data = votable.get_first_table().to_table(use_names_over_ids=True)

print(votable)
print(data["Wavelength"])

Выход в этом случае:

<VOTABLE>... 1 tables ...</VOTABLE>
Wavelength
AA    
----------
12890.0
13150.0
...
18930.0
19140.0
Length = 58 rows

1 Ответ

1 голос
/ 08 октября 2019

Действительно в соответствии с документацией API , первый аргумент votable.parse - это либо имя файла, либо читаемый файлоподобный объект. Это не определяет это точно, но, очевидно, файл также должен быть seekable , что означает, что он может быть прочитан с произвольным доступом.

Объект HTTPResponse, возвращаемый urlopen,на самом деле это файлоподобный объект с методом .read(), поэтому в принципе можно было бы напрямую перейти к parse(), но я обнаружил, что он должен быть доступен для поиска:

fltr = 'http://svo2.cab.inta-csic.es/theory/fps/fps.php?ID=2MASS/2MASS.H'
u = urlopen(fltr)
>>> parse(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "astropy/io/votable/table.py", line 135, in parse
    _debug_python_based_parser=_debug_python_based_parser) as iterator:
  File "/usr/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "astropy/utils/xml/iterparser.py", line 157, in get_xml_iterator
    with _convert_to_fd_or_read_function(source) as fd:
  File "/usr/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "astropy/utils/xml/iterparser.py", line 63, in _convert_to_fd_or_read_function
    with data.get_readable_fileobj(fd, encoding='binary') as new_fd:
  File "/usr/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "astropy/utils/data.py", line 210, in get_readable_fileobj
    fileobj.seek(0)
io.UnsupportedOperation: seek

Таким образом, вам нужно обернуть данные в доступный для поиска файловый объект. В соответствии с тем, что написал @keflavich, вы можете использовать io.BytesIO (io.StringIO не будет работать, как описано ниже).

Оказывается, что нет никакой причины явно декодировать данные UTF-8 в Unicode. Я избавлю пример, но, попробовав сам, выясняется, что parse() работает с необработанными байтами (что я нахожу немного странным, но все в порядке). Таким образом, вы можете прочитать все содержимое URL в io.BytesIO, который представляет собой просто файл-подобный объект в памяти, который поддерживает произвольный доступ:

>>> u = urlopen(fltr)
>>> s = io.BytesIO(u.read())
>>> v = parse(s)
WARNING: W42: None:2:0: W42: No XML namespace specified [astropy.io.votable.tree]
>>> v.get_first_table().to_table(use_names_over_ids=True)
<Table masked=True length=58>
Wavelength Transmission
    AA
 float32     float32
---------- ------------
   12890.0          0.0
   13150.0          0.0
       ...          ...
   18930.0          0.0
   19140.0          0.0

Это, как правило, путь в Pythonделать что-то с данными, как будто это файл, без записи фактического файла в файловую систему.

Обратите внимание, однако, что это не сработает, если весь файл не помещается в памяти. В этом случае вам все еще может потребоваться записать его на диск. Но если это просто для какой-то временной обработки и вы не хотите засорять свой диск tmp.xml, как в вашем примере, вы всегда можете использовать модуль tempfile, чтобы, среди прочего, создавать временные файлыкоторые автоматически удаляются, когда они больше не используются.

...