У меня есть существующий процесс, который извлекает элементы из html-документов, использующих стандарт xbrl xml.

И пример документа можно найти :

Процесс работает хорошо (я использую многопроцессорную обработку для параллельной работы), но у меня есть ~ 20 миллионов html и xml файлов для обработки, и я считаю, что beautifulsoup является основным узким местом.

Я смотрю на htmlelement как на более быструю альтернативу извлечению данных, которые мне нужны, но я изо всех сил пытаюсь найти элементы. Например, в BS я могу делать следующее:

for tag in soup.find_all('xbrli:unit'):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

    l_unit_dict[l_unitid] = {'unitid':l_unitid,'value':l_value}

Который найдет все теги xbrli: unit, и я могу легко извлечь их значения.

Однако, когда я пробую что-то подобное в htmlelement, я получаю следующее исключение:

import htmlement

source = htmlement.parse("Prod223_2542_00010416_20190331.html")

for tag in source.iterfind('.//xbrli:unit'):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

    print(l_unitid)
    print(l_value)

    SyntaxError: prefix 'xbrli' not found in prefix map

Небольшой поиск в Google привел меня к нескольким статьям, но я не могу добиться прогресса SyntaxError: префикс 'a' не найден в карте префиксов

Разбор XML с пространством имен в Python через 'ElementTree'

Я пробовал добавить карту пространства имен, но она просто ничего не находит, независимо от того, в какую сторону я помещаю вещи или какие теги я ищу

source = htmlement.parse("Prod223_2542_00010416_20190331.html")

namespaces = {'xbrli': 'period'}

for tag in source.iterfind('.//xbrli:period',namespaces):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

namespaces = {'xbrli': 'period'}

for tag in source.iterfind('.//{xbrli}period',namespaces):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

    print(l_unitid)
    print(l_value)

namespaces = {'period':'xbrli'}
for tag in source.iterfind('.//{xbrli}period',namespaces):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

    print(l_unitid)
    print(l_value)

namespaces = {'period':'xbrli'}

for tag in source.iterfind('.//period',namespaces):

    l_unitid = tag.attrs.get('id')
    l_value = tag.text

    print(l_unitid)
    print(l_value)

Все ничего не возвращают - они не входят в цикл. У меня явно что-то не так в моем понимании того, как использовать элементарную структуру по сравнению с BS, но я не совсем знаю, как перейти от одного к другому.

Любые предложения приветствуются.

1
user7863288 16 Апр 2020 в 13:29

1 ответ

Лучший ответ

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

Во-вторых, о xbrl в целом: из горького опыта (и как указали многие другие) xbrl ужасен. Поверхность блестящая, но как только вы открываете капюшон, все становится беспорядочно. Так что я тебе не завидую ...

И, с учетом сказанного, я попытался приблизительно определить то, что вы, вероятно, ищете. Я не стал создавать словари или списки, а просто использовал оператор print(). Очевидно, что если это вам поможет, вы можете изменить его в соответствии со своими требованиями:

from lxml import etree
import requests
r = requests.get('https://beta.companieshouse.gov.uk/company/00010416/filing-history/MzI1MTU3MzQzMmFkaXF6a2N4/document?format=xhtml&download=1') 

root = etree.fromstring(r.content)
units = root.xpath(".//*[local-name()='unit'][@id]/@id")
for unit in units:
    unit_id = unit
    print('unit: ', unit)
print('----------------------------')

context = root.xpath(".//*[local-name()='context']")
for tag in context:
    id = tag.xpath('./@id')
    print('ID: ',id)
    info = tag.xpath('./*[local-name()="entity"]')
    identifier = info[0].xpath('.//*[local-name()="identifier"]')[0].text
    print('identifier: ',identifier)
    member = info[0].xpath('.//*[local-name()="explicitMember"]')
    if len(member)>0:
        dimension = member[0].attrib['dimension']
        explicitMember = member[0].text
        print('dimension: ',dimension,' explicit member: ',explicitMember)
    periods = tag.xpath('.//*[local-name()="period"]')
    for period in periods:
        for child in period.getchildren():
            if 'instant' in child.tag:
                instant = child.text
                print('instant: ',instant)
            else:
                dates = period.xpath('.//*')
                start_date = dates[0].text
                end_date = dates[1].text
        print('start date: ', start_date,' end date: ',end_date)

    print('===================')

Случайная выборка из вывода:

ID:  ['cfwd_31_03_2018']
identifier:  00010416
instant:  2018-03-31
start date:  2017-04-01  end date:  2018-03-31
===================
ID:  ['CountriesHypercube_FY_31_03_2019_Set1']
identifier:  00010416
dimension:  ns15:CountriesRegionsDimension  explicit member:  ns15:EnglandWales
instant:  2018-03-31
start date:  2018-04-01  end date:  2019-03-31
1
Jack Fleeting 16 Апр 2020 в 15:47