【问题标题】:Parse an html page with XML in python在 python 中使用 XML 解析 html 页面
【发布时间】:2013-03-06 18:05:14
【问题描述】:

我正在尝试让 python 从 HTML 页面中解析这个 XML 代码:

<weather>
    <loc mobiurl="http://foreca.mobi/?lon=-8.6110&lat=41.1496&source=navi/" url="http://foreca.com/?lon=-8.6110&lat=41.1496&source=navi/">
        <obs station="Porto / Pedras Rubras" dist="11 km NW" dt="2013-03-06 17:00:00" t="14" tf="14" s="d320" wn="S" ws="8" p="997" rh="94" v="5000"/>
        <fc dt="2013-03-07" tx="16" tn="11" s="d220"/>
        <fc dt="2013-03-08" tx="15" tn="10" s="d220"/>
        <fc dt="2013-03-09" tx="15" tn="10" s="d220"/>
    </loc>
</weather>

我想获取有关drstxtn 字段的信息,但我不知道如何使用 XML 函数。我尝试读取 HTML 文件,然后在前面所说的路径之后创建和箭头来存储内容,但我无法让它工作。

有没有简单的方法用python获取数据?

【问题讨论】:

  • 我对你的问题有点困惑。该数据是来自 XML 文档还是 HTML 文档?如果它来自HTML文档,它是如何嵌入的?
  • 本例中没有tr 属性,是不是拼写错误?
  • 它不是格式良好的 xml。 & 字符 (&amp;amp;) 必须转义为 &amp;amp;
  • @MartijnPieters 这是一个错字。我的意思是“dt”,而不是“tr”……
  • @Robᵩ xml 文件在此网页上:link

标签: python html xml parsing


【解决方案1】:

使用该库的 makeHTMLTags 方法可以通过 pyparsing 轻松完成一些 HTML 抓取(makeHTMLTags 返回一对表达式,用于开始和结束标签,但在您的示例中,只需要开始标签):

from pyparsing import makeHTMLTags

fcTag = makeHTMLTags("fc")[0]
tagAttrs = 'dt s tx tn'.split()

for match in fcTag.searchString(htmltext):
    print ' '.join("%s:%s" % (attr,match[attr]) for attr in tagAttrs)

打印:

dt:2013-03-07 s:d220 tx:16 tn:11
dt:2013-03-08 s:d220 tx:15 tn:10
dt:2013-03-09 s:d220 tx:15 tn:10

这使得将这个片段解析器与 pyparsing 的其他功能(例如运行时解析操作、语义检查等)结合起来变得很容易。

编辑

如果您希望所有 dt、s 等在它们各自的列表中(在 Python 中,我们称它们为“列表”,而不是“向量”),请执行以下操作:

dtArray = []
sArray = []
txArray = []
tnArray = []
for match in fcTag.searchString(htmltext):
    dtArray.append(match.dt)
    sArray.append(match.s)
    txArray.append(match.tx)
    tnArray.append(match.tn)
    print ' '.join("%s:%s" % (attr,match[attr]) for attr in tagAttrs)

我以前见过这样的代码,这是一种糟糕的数据结构模式。您可以通过获取dtArray[i]sArray[i] 等来访问原始表的第 i 个条目的值。

请考虑使用 Python 提供的几种结构化类型之一。您有多种选择:

A.使用字典。

fcArray = []
for match in fcTag.searchString(htmltext):
    fcArray.append(dict((attr,match[attr]) for attr in tagAttrs))

现在要获取第 i 个条目,只需获取 fc = fcArray[i],然后从该字典中访问 fc['dt']fc['s'] 等值。

B.使用命名元组。

from collections import namedtuple
FCData = namedtuple("FCData", tagAttrs)

fcArray = []
for match in fcTag.searchString(htmltext):
    fcArray.append(FCData(*(match[attr] for attr in tagAttrs)))

您再次使用fc = fcArray[i] 获取第i 个条目,但现在您使用fc.dtfc.s 等访问值。我发现这种形式比dict 形式看起来更干净,但是是一些限制。所有的标签名称都必须是合法的 Python 标识符,所以如果你有一个标签“rise/run”,那么你就不能使用命名元组。此外,namedtuples 是不可变的 - 您不能使用现有的 FCData fc 并使用 fc.dt = "new datetime value" 分配到其 dt 字段。另一方面,dicts 将允许这样做。

C.使用对象。最简单的是创建空对象实例的“bag”类型对象,您可以通过简单的赋值或 setattr 调用为其添加属性:

class FCData(object): pass

fcArray = []
for match in fcTag.searchString(htmltext):
    fc = FCdata()
    for attr in tagAttrs:
        setattr(fc, attr, match[attr])
    fcArray.append(fc)

您使用fc = fcArray[i] 获得第i 个条目,并且像namedtuple 一样,您使用fc.dt 获得属性等等。但如果需要,您也可以修改属性,分配fc.dt = "new datetime value" 将起作用。

D.只需使用 pyparsing 的 searchString 方法创建的对象即可。

fcArray = fcTag.searchString(htmltext)

pyparsing 返回ParseResults,它结合了字典和命名元组的行为。就像在使用 fc = fcArray[i] 访问第 i 个条目之前一样。您可以使用fc.dtfc['dt'] 读取dt 属性。你可以阅读fc.dt,但你不能给它赋值,就像namedtuple一样。您可以分配给fc['dt'],就像字典一样。

【讨论】:

  • 显然这是有效的。现在我将对其进行改造并尝试在 Raspberry Pi 上进行调整。非常感谢:D
  • 我正在尝试将每个值“保存”在不同的向量中,但我被卡住了......我想要每个日期一个向量,每个“s”另一个向量,等等,但我'我只保存每个元素的最后一行。任何人都可以帮助我吗? :(
  • 感谢保罗的帮助。但是现在,当我运行脚本时,我得到了这个错误:“tnAray.append(match.tn) NameError: name 'tnAray' is not defined”...我也导入了数组模块。
  • 编辑:忘记这个! “数组”一词中缺少一个“r”,哈哈,非常感谢保罗! :D 这适用于第一个也是更糟糕的选项:D
  • 哈,修正了错字,现在你也学会了一些关于自己修复错误的知识!
【解决方案2】:

如果你可以轻松提取天气标签,你可以使用 Python 自带的xml.etree.ElementTree API

import xml.etree.ElementTree as ET
tree = ET.fromstring(weatherdata)

for fcelem in tree.findall('.//fc'):
    print fcelem.attrib['tx'], fcelem.attrib['tn']

如果你想从 HTML 文档中提取它,那么这取决于 HTML 的格式是否正确。如果是 XHTML 文档,ElementTree API 可以很好地处理。

否则,您需要改用 HTML 解析器。你可以安装lxml library;该库支持相同的 ElementTree API,但包含专用的 HTML 解析器。

您还可以将BeautifulSoup 用于备用HTML API。事实上,lxmlBeautifulSoup 可以协同工作,为您的任务提供 API 选择;使用对你来说更容易的那个。

lxmlBeautifulSoup 都是外部库。

【讨论】:

  • .fromstring() 返回一个元素,而不是树。 .findall('fc') 在这里不起作用。您可以改用.findall('.//fc').iter('fc')
  • 或者python发行版中有一个非常简单的HTMLParser。
  • @ondra:有,但它简单了。请参阅docs.python.org/2/library/htmlparser.html,但我不能推荐它。它的 API 很繁琐,并且不能很好地处理损坏的 HTML。
猜你喜欢
  • 2017-03-19
  • 2023-04-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 2014-06-01
  • 2012-09-27
  • 2012-12-11
  • 2014-07-05
相关资源
最近更新 更多