Как найти XML-элемент по тегу в Python

#python #xml #ubuntu #xpath

Вопрос:

У меня возникли проблемы с определением правильного элемента в Python. На самом деле я хочу увидеть последний доступный файл в недавно использованном файле.xbel. Поэтому я хочу перебрать каждый файл, чтобы найти файл с последними изменениями или последними посещениями.Вот как выглядит XML-файл.

 <?xml version="1.0" encoding="UTF-8"?>
<xbel version="1.0"
      xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
      xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
>
  <bookmark href="file:///tmp/google-chrome-stable_current_amd64.deb" added="2021-09-14T12:09:05Z" modified="2021-09-14T12:09:05Z" visited="2021-09-15T09:12:13Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/vnd.debian.binary-package"/>
        <bookmark:applications>
          <bookmark:application name="Firefox" exec="amp;apos;firefox %uamp;apos;" modified="2021-09-14T12:09:05Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/Git/testprog" added="2021-09-15T09:12:13Z" modified="2021-09-15T09:12:13Z" visited="2021-09-15T09:12:13Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="inode/directory"/>
        <bookmark:applications>
          <bookmark:application name="code" exec="amp;apos;code %uamp;apos;" modified="2021-09-15T09:12:13Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/.local/share/recently-used.xbel" added="2021-09-15T09:51:57Z" modified="2021-09-15T09:51:57Z" visited="2021-09-15T09:51:57Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/x-xbel"/>
        <bookmark:applications>
          <bookmark:application name="code" exec="amp;apos;code %uamp;apos;" modified="2021-09-15T09:51:57Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///tmp/slack-desktop-4.19.2-amd64.deb" added="2021-09-15T11:45:49Z" modified="2021-09-15T11:45:49Z" visited="2021-09-16T13:26:26Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/vnd.debian.binary-package"/>
        <bookmark:applications>
          <bookmark:application name="Firefox" exec="amp;apos;firefox %uamp;apos;" modified="2021-09-15T11:45:49Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/Downloads/google-chrome-stable_current_amd64.deb" added="2021-09-15T11:52:39Z" modified="2021-09-15T11:52:39Z" visited="2021-09-16T13:26:26Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/vnd.debian.binary-package"/>
        <bookmark:applications>
          <bookmark:application name="Firefox" exec="amp;apos;firefox %uamp;apos;" modified="2021-09-15T11:52:39Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/Documents/libretest" added="2021-09-15T11:58:53Z" modified="2021-09-15T11:58:53Z" visited="2021-09-16T13:26:26Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/octet-stream"/>
        <bookmark:applications>
          <bookmark:application name="LibreOffice 6.4" exec="amp;apos;soffice %uamp;apos;" modified="2021-09-15T11:58:53Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/Documents/libretest.odt" added="2021-09-15T11:58:53Z" modified="2021-09-15T15:42:04Z" visited="2021-09-16T13:26:26Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="application/vnd.oasis.opendocument.text"/>
        <bookmark:applications>
          <bookmark:application name="LibreOffice 6.4" exec="amp;apos;soffice %uamp;apos;" modified="2021-09-15T15:42:04Z" count="12"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
  <bookmark href="file:///home/test/Git/node-socket" added="2021-09-16T13:26:25Z" modified="2021-09-16T13:26:49Z" visited="2021-09-16T13:26:26Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="inode/directory"/>
        <bookmark:applications>
          <bookmark:application name="code" exec="amp;apos;code %uamp;apos;" modified="2021-09-16T13:26:49Z" count="2"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
</xbel>
 

В моем коде я пытаюсь получить доступ bookmark:applications , но безуспешно.

     home = str(Path.home())
    root = ET.parse(home   '/.local/share/recently-used.xbel').getroot()
    print(root)
    print('lower')
    for bookmark in root.iter('bookmark'):
        print(bookmark)
        for applications in bookmark.find('applications'):
            print(applications)
 

Каков был бы правильный способ доступа bookmark:applications и поиска последнего посещенного?

Комментарии:

1. попробуйте использовать xmltodict

2. bookmark:applications Элемент привязан к http://www.freedesktop.org/standards/desktop-bookmarks пространству имен (посредством xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks" ). Видишь docs.python.org/3/library/…

3. Если вам нужна только информация о последней измененной закладке, вы можете сделать это, используя измененный атрибут тега закладки, как измененный атрибут закладки и измененный атрибут закладки:приложение имеют одинаковое значение.

Ответ №1:

Это будет полезно для доступа к закладке:приложения и фрейм данных помогут вам получить последнюю посещенную/измененную закладку с именем приложения.

 import xml.etree.ElementTree as ET
import pandas as pd

root = ET.parse('/content/sample.xml').getroot()
lst = []

for bookmark in bookmarklist:
  bookmark_lst = []
  print(bookmark.attrib)
  bookmark_lst.append(bookmark.attrib['href'])
  bookmark_lst.append(bookmark.attrib['modified'])
  bookmark_lst.append(bookmark.attrib['visited'])
  for ele in list(bookmark.iter()) :
    if 'application' in ele.tag:
      if 'name' in ele.attrib:
        bookmark_lst.append(ele.attrib['name'])
  lst.append(bookmark_lst)

df = pd.DataFrame(lst,columns ['href','modified','visited','application_name'])

df['modified'] = pd.to_datetime(df['modified'])
df['visited'] = pd.to_datetime(df['visited'])

least_recent_date = df['visited'].min()
most_recent_date = df['visited'].max()

 

Комментарии:

1. В чем была проблема в этом вопросе? Не просто отправляйте код без объяснения причин. В вопросе нет ничего о пандах.

2. Я бы на самом деле хотел элемент и все его атрибуты. Не только дата. Это покажет мне только самую старую и самую новую дату. Я хотел бы выбрать элемент с самой высокой датой и проверить другие атрибуты.

3. Я довольно хорошо справляюсь с частью с пандами. столбцы не определены.

Ответ №2:

 from lxml import etree

NS = {"n": "http://www.freedesktop.org/standards/desktop-bookmarks"}

root = etree.parse("book.xml")
bookmarks = root.xpath("//bookmark")
most_recent_bookmark = max(
    bookmarks,
    key=lambda bmark: bmark.xpath(
        "string(.//n:application/@modified)",
        namespaces=NS,
    ),
)

print("Most recent href: "   most_recent_bookmark.xpath("string(@href)"))
print(
    "Most recent modified: "
      most_recent_bookmark.xpath("string(.//n:application/@modified)", namespaces=NS)
)
 

Выход:

 Most recent href: file:///home/test/Git/node-socket
Most recent modified: 2021-09-16T13:26:49Z
 

Проблема, с которой вы столкнулись, заключается в указании пространства имен, представленного bookmark: в исходном xml и n: в примере кода. Все xpath() find() функции , findall() и позволяют предоставить словарь пространств имен.

https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.Элементтри.Элемент.найди все

Если, как вы говорите, пространство имен может измениться, вы можете использовать XPath .//*[local-name() = 'application']/@modified вместо .//n:application/@modified параметра без пространства имен. (Однако я был бы удивлен, если бы производитель произвольно изменил пространство имен, потому что он просто просит, чтобы все, что потребляет ваши данные, было нарушено. URL-адрес является такой же частью имени узла, как и «приложение».)

Комментарии:

1. Моя проблема в том, что я не всегда буду знать пространство имен. Если это изменится, моя реализация не будет работать. Неужели нет способа получить его из файла? Я бы не хотел его жестко кодировать.

2. Я считаю .//*:application/@modified , что должно соответствующим образом соответствовать любому пространству имен. Проверю, когда у меня будет свободная минутка.

3. Тем не менее, я был бы очень удивлен, если бы пространство имен произвольно изменилось.

4. Переписали функцию, чтобы было понятнее, и предоставили способ обойти пространство имен переменных.