【问题标题】:Python extract data from xmlPython从xml中提取数据
【发布时间】:2021-03-18 23:37:27
【问题描述】:

我正在尝试从此网页获取值:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<ArrayOfVwHistoryDetail xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
<vwHistoryDetail>
<idVariable>2561</idVariable>
<DateTime>2020-12-01T00:00:00</DateTime>
<idPeriodType>1</idPeriodType>
<Value>28671555</Value>
<ValueDetail>4415</ValueDetail>
</vwHistoryDetail>
<vwHistoryDetail>
<idVariable>2561</idVariable>
<DateTime>2020-12-02T00:00:00</DateTime>
<idPeriodType>1</idPeriodType>
<Value>28675970</Value>
<ValueDetail>4279</ValueDetail>
</vwHistoryDetail>
<vwHistoryDetail>
<idVariable>2561</idVariable>
<DateTime>2020-12-03T00:00:00</DateTime>
<idPeriodType>1</idPeriodType>
<Value>28680249</Value>
<ValueDetail>3975</ValueDetail>
</vwHistoryDetail>
<vwHistoryDetail>
<idVariable>2561</idVariable>
<DateTime>2020-12-04T00:00:00</DateTime>
<idPeriodType>1</idPeriodType>
<Value>28684224</Value>
<ValueDetail>4236</ValueDetail>
</vwHistoryDetail>
</ArrayOfVwHistoryDetail>

我用这段代码测试过:

import xml.etree.ElementTree as ET
from urllib import request


url = "http://SomeSite/WebService.asmx/LoadVariableHistory?username=USERNAME&password=PASSWORD&variableName=CBT2_G_PRM_FB2&startDateTime=2020-12-01&endDateTime=2020-12-02&sampling=3"

print ("Obter: ", url)
html = request.urlopen(url)
data = html.read()
print("Obtido: ",len(data),"caracteres")

tree = ET.fromstring(data)
results = tree.findall('Value')
for i in results:
  print(i)

出于安全原因,我隐藏了完整的 URL。 我做错了什么没有得到价值?我需要通过这部分,以便可以使用 DataTime 构建字典:值

提前谢谢你

【问题讨论】:

  • 我认为您不需要页面的 HTML...如果您使用 requests 库,您可以像这样获取数据:requests.get(url).content。请注意,您必须通过 pip 或其他方式安装请求。开头的“此 XML 文件似乎没有...”可能无法正确解析 XML 文件。
  • 当我打印 requests.get(url).content 时,我得到了这个:b'&lt;?xml version="1.0" encoding="utf-8"?&gt;\r\n&lt;ArrayOfVwHistoryDetail xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/"&gt;\r\n &lt;vwHistoryDetail&gt;\r\n &lt;idVariable&gt;2561&lt;/idVariable&gt;\r\n &lt;DateTime&gt;2020-12-01T00:00:00&lt;/DateTime&gt;\r\n &lt;idPeriodType&gt;1&lt;/idPeriodType&gt;\r\n &lt;Value&gt;28671555&lt;/Value&gt;\r\n &lt;ValueDetail&gt;4415&lt;/ValueDetail&gt;\r\n &lt;/vwHistoryDetail&gt;\r\n&lt;/ArrayOfVwHistoryDetail&gt;' 我仍然没有得到任何价值。
  • 看起来是正确的。 XML 不关心空格,例如 \r\n 这只是换行符

标签: python python-3.x xml xml-parsing urllib


【解决方案1】:

您当前的实施中出现了几个问题:

  • 您的 XML 包含一个默认命名空间xmlns="http://tempuri.org/",它要求您定义一个前缀以解析节点内容; findall 维护一个 namespaces 参数。
  • 您的路径表达式假定Value 是根的子级。您需要使用双斜杠路径 .//,因为 Value 是根的后代。
  • 您需要提取迭代器变量的text。否则,您将返回 &lt;Element ... &gt; 对象,该对象通常在最终使用需求中没有用处。

考虑调整

tree = ET.fromstring(data)
nmsp = {'doc': 'http://tempuri.org/'}                         # NAMESPACE PREFIX ASSIGNMENT
results = tree.findall('.//doc:Value', namespaces = nmsp)     # NAMESPACE PREFIX USE WITH './/' PATH 
for i in results:
  print(i.text)                                               # RETRIEVE TEXT VALUE

# 28671555
# 28675970
# 28680249
# 28684224

更好的是,返回 .Value 的字典及其具有列表/字典理解的兄弟(其中 split 删除字典键中的默认命名空间):

data_list_of_dicts = [{i.tag.split('}')[-1]: i.text for i in hd} 
                        for hd in tree.findall('.//doc:vwHistoryDetail', namespaces = nmsp)]

print(data_list_of_dicts)
# [{'idVariable': '2561', 'DateTime': '2020-12-01T00:00:00', 'idPeriodType': '1', 'Value': '28671555', 'ValueDetail': '4415'}, 
#  {'idVariable': '2561', 'DateTime': '2020-12-02T00:00:00', 'idPeriodType': '1', 'Value': '28675970', 'ValueDetail': '4279'}, 
#  {'idVariable': '2561', 'DateTime': '2020-12-03T00:00:00', 'idPeriodType': '1', 'Value': '28680249', 'ValueDetail': '3975'}, 
#  {'idVariable': '2561', 'DateTime': '2020-12-04T00:00:00', 'idPeriodType': '1', 'Value': '28684224', 'ValueDetail': '4236'}]

对于时间键值字典:

time_value_dict = {hd.find('doc:DateTime', namespaces=nmsp).text: 
                   hd.find('doc:Value', namespaces=nmsp).text 
                      for hd in tree.findall('.//doc:vwHistoryDetail', namespaces=nmsp)}

print(time_value_dict)
# {'2020-12-01T00:00:00': '28671555', 
#  '2020-12-02T00:00:00': '28675970', 
#  '2020-12-03T00:00:00': '28680249', 
#  '2020-12-04T00:00:00': '28684224'}

Online Demo

【讨论】:

  • 谢谢@Parfait,我尝试根据@balderman 的建议更改您的代码,以获得DataTime:值对:tree = ET.fromstring(data) nmsp = {'doc': 'http://tempuri.org/'} # NAMESPACE PREFIX ASSIGNMENT results = tree.findall('.//doc:Value', namespaces = nmsp) # NAMESPACE PREFIX USE WITH './/' PATH DataTimeValue_dict = [{i.find('DateTime').text: i.find('Value').text for i in hd} for hd in tree.findall('.//doc:vwHistoryDetail', namespaces = nmsp)]
  • 输出:文件“g:/My Drive/Projectos/Python/teste/get.py”,第 19 行,在 DataTimeValue_dict = [{i.find('DateTime').text : i.find('Value').text for i in hd} 文件“g:/My Drive/Projectos/Python/teste/get.py”,第 19 行,在 DataTimeValue_dict = [{i.find( 'DateTime').text: i.find('Value').text for i in hd} 文件“g:/My Drive/Projectos/Python/teste/get.py”,第 19 行,在 DataTimeValue_dict = [{i.find('DateTime').text: i.find('Value').text for i in hd} AttributeError: 'NoneType' object has no attribute 'text' PS G:\My Drive\Projectos\Python \teste>
  • @NunoFélix,您的字典理解不正确。试试d = {hd.find('./doc:DateTime', namespaces=nmsp).text: hd.find('./doc:Value', namespaces=nmsp).text for hd in tree.findall('.//doc:vwHistoryDetail', namespaces=nmsp)}
  • 您需要在.find 中传递namespace,就像您对.findall 所做的那样。查看编辑和演示更新。
  • 谢谢@Parfait,每个人都帮助了我,最后,它按照我想要的方式工作。我刚开始在 Udemy 学习 Python,所以我犯了很多新手错误。
【解决方案2】:
tree = ET.fromstring(data)
for detail in tree.findall('vwHistoryDetail'):
  v = detail.find('Value').text
  print(v)

你最好循环遍历一个对象并提取子元素,而不是直接抓取子元素,因为 Value 可能是在文档的不同部分重复使用的标签

【讨论】:

  • 我测试了您的建议,但仍然没有打印任何值。还是谢谢你
【解决方案3】:

见下文

import xml.etree.ElementTree as ET
import re

#
xml = '''<ArrayOfVwHistoryDetail xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xmlns="http://tempuri.org/">
   <vwHistoryDetail>
      <idVariable>2561</idVariable>
      <DateTime>2020-12-01T00:00:00</DateTime>
      <idPeriodType>1</idPeriodType>
      <Value>28671555</Value>
      <ValueDetail>4415</ValueDetail>
   </vwHistoryDetail>
   <vwHistoryDetail>
      <idVariable>2561</idVariable>
      <DateTime>2020-12-02T00:00:00</DateTime>
      <idPeriodType>1</idPeriodType>
      <Value>28675970</Value>
      <ValueDetail>4279</ValueDetail>
   </vwHistoryDetail>
   <vwHistoryDetail>
      <idVariable>2561</idVariable>
      <DateTime>2020-12-03T00:00:00</DateTime>
      <idPeriodType>1</idPeriodType>
      <Value>28680249</Value>
      <ValueDetail>3975</ValueDetail>
   </vwHistoryDetail>
   <vwHistoryDetail>
      <idVariable>2561</idVariable>
      <DateTime>2020-12-04T00:00:00</DateTime>
      <idPeriodType>1</idPeriodType>
      <Value>28684224</Value>
      <ValueDetail>4236</ValueDetail>
   </vwHistoryDetail>
</ArrayOfVwHistoryDetail>'''
xml = re.sub(' xmlns="[^"]+"', '', xml, count=1)
root = ET.fromstring(xml)
data = {v.find('DateTime').text: v.find('Value').text for v in root.findall('.//vwHistoryDetail')}
print(data)

输出

{'2020-12-01T00:00:00': '28671555', '2020-12-02T00:00:00': '28675970', '2020-12-03T00:00:00': '28680249', '2020-12-04T00:00:00': '28684224'}

【讨论】:

  • Running regex on XML?所有兼容的 DOM 库都应该处理默认命名空间,而不需要将其从树中删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
相关资源
最近更新 更多