从 beautifulsoup 切换到 htmlelement - 如何查找元素

Switching from beautifulsoup to htmlelement - how to find elements

我有一个现有的过程,可以从使用 xbrli xml 标准的 html 文档中提取元素。

可以找到文档示例here:

该过程运行良好(我正在使用多处理并行工作)但我有 ~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 标签,我可以轻松提取它们的值。

但是,当我在 html 元素中尝试类似的操作时,出现以下异常:

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

谷歌搜索让我找到了几篇文章,但我似乎无法取得进展

Parsing XML with namespace in Python via '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)

全部 return 没有 - 他们没有进入循环。我对如何使用 elementree 结构与 BS 的理解显然有一些非常错误的地方,但我不太清楚如何从一个转移到另一个。

欢迎提出任何建议。

在我得到建议的答案之前有两条一般性评论: 首先,您正在处理 xml 文档,因此通常最好使用 xml,而不是 html 解析器。这就是我在下面使用的,而不是 beautifull soup 或 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