Переход с красивого супа на htmlelement - как найти элементы - PullRequest
1 голос
/ 16 апреля 2020

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

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

Процесс работает хорошо (я использую многопроцессорную работу для параллельной работы), но у меня есть ~ 20m 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

Небольшое прибегание к гуглу привело меня к нескольким статьям, но я не могу добиться прогресса 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)

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

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

1 Ответ

1 голос
/ 16 апреля 2020

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

Во-вторых, о 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
...