如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>'
Posted
技术标签:
【中文标题】如何使用 Python XML findall 查找 \'<v:imagedata r:id="rId7" o:title="1-REN"/>\'【英文标题】:How to use Python XML findall to find '<v:imagedata r:id="rId7" o:title="1-REN"/>'如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>' 【发布时间】:2020-09-18 11:14:07 【问题描述】:我正在尝试从具有命名空间 xmlns:v="urn:schemas-microsoft-com:vml"
的 <v:imagedata r:id="rId7" o:title="1-REN"/>
的 Word 文档中查找所有内容,但我无法弄清楚语法到底是什么。
docs 仅涵盖了非常简单的案例,并且使用了 URN 和 VML 组合,我似乎无法让我在网上看到的任何示例都可以工作。有谁碰巧知道它是什么?
我正在尝试做这样的事情:
namespace = 'v': "urn:schemas-microsoft-com:vml"
results = ET.fromstring(xml).findall("imagedata", namespace)
for image_id in results:
print(image_id)
编辑:@aneroid 写的是 1000% 的正确答案并且非常有帮助。你应该赞成它。就是说,在了解了所有这些之后-我选择了BS4答案,因为它完全按照我的需要在两行中完成了整个工作????。如果您实际上并不关心命名空间,那似乎更容易。
【问题讨论】:
【参考方案1】:使用 Python 3.8 中的 ElementTree,您可以简单地为命名空间使用通配符 (*
):
results = ET.fromstring(xml).findall(".//*imagedata")
注意.//
部分,这意味着搜索整个文档(所有后代)。
【讨论】:
这也适用于.iter()
(可能还有所有与搜索相关的方法)。此外,.//
位是特定于问题的详细信息。不需要使用新的*
通配符。
通配符真的可以与iter()
一起使用吗?链接的 3.8 发行说明仅提及“.find*()
方法”。
是的,我已确认您的行为。 Documention on 'Supported XPath syntax' 现在还指定:*spam selects tags named spam in any (or no) namespace
和 Changed in version 3.8: Support for star-wildcards was added.
@Aaron:“这也适用于.iter()
”是什么意思?命名空间通配符不与iter()
一起使用。它适用于find()
、findall()
和findtext()
。
来自我的本地测试;新的通配符在iter()
中有效。简要回顾; source-code commit 似乎修改了用于确定标签是否匹配的比较逻辑,所以我假设即使没有记录,也可以说这适用于 .iter()
。然而,在有人添加适当的单元测试并更新文档之前,这只是一个假设。【参考方案2】:
ET.findall()
与 BS4.find_all()
:
findall()
不是递归的默认情况下*。它只会找到所提供节点的直接子节点。因此,在您的情况下,它仅在根元素下直接搜索图像节点。
* 根据下面的mzjn's comment,在match
参数(标签或路径)前加上".//"
将搜索该节点在树中的任何位置,因为它是@ 987654323@.
BeautifulSoup's find_all()
搜索所有后代。因此它会在树中的任何位置搜索“imagedata”节点。
但是,ElementTree.iter()
确实搜索所有后代。使用'working with namespaces' example in the docs:
>>> for char in root.iter('http://characters.example.comcharacter'):
... print(' |-->', char.text)
...
|--> Lancelot
|--> Archie Leach
|--> Sir Robin
|--> Gunther
|--> Commander Clement
遗憾的是,ET.iterfind()
将命名空间用作 dict(如 ET.findall),也不搜索后代,仅直接子代默认情况下*。就像 ET.findall。除了标签中的空字符串''
在命名空间中如何被处理,一个返回一个列表而另一个返回一个迭代器之外,我不能说ET.findall
和ET.iterfind
之间存在有意义的区别。
* 如上ET.findall()
,前缀".//"
使其搜索整个树(与任何节点匹配)。
当您使用带有 ET 的命名空间时,您仍然需要带有标签的 命名空间名称。结果行应该是:
namespace = 'v': "urn:schemas-microsoft-com:vml"
results = ET.fromstring(xml).findall("v:imagedata", namespace) # note the 'v:'
另外,'v'
不必是 'v'
,如果需要,您可以将其更改为更有意义的内容:
namespace = 'image': "urn:schemas-microsoft-com:vml"
results = ET.fromstring(xml).findall("image:imagedata", namespace)
当然,如果它们不是根的直接子元素,这仍然不一定会得到所有的 imagedata 元素。为此,您需要创建一个递归函数来为您执行此操作。请参阅this answer on SO 了解如何操作。请注意,虽然该答案会进行递归搜索,但如果后代深度太高,您可能会达到 Python 的递归限制...deep。
要获取树中任意位置的所有 imagedata 元素,请使用 ".//"
前缀:
results = ET.fromstring(xml).findall(".//v:imagedata", namespace)
【讨论】:
findall
可以找到所有imagedata
节点。只需使用findall(".//v:imagedata", namespace)
。
谢谢!我已经编辑并澄清了我的答案 wrt ET.findall()
,以及 ET.iterfind()
。【参考方案3】:
我将保留这个问题,但我目前使用的解决方法是使用 BeautifulSoup,它很乐意接受 v:
语法。
soup = BeautifulSoup(xml, "lxml")
results = soup.find_all("v:imagedata")
【讨论】:
以上是关于如何使用 Python XML findall 查找 '<v:imagedata r:id="rId7" o:title="1-REN"/>'的主要内容,如果未能解决你的问题,请参考以下文章
替换Groovy XML节点会导致后续findAll调用出现问题
idea的spring整合基于xml文件配置的mybatis报Invalid bound statement (not found): com.music.dao.MusicDao.findAll的问