Python Iluminado

XML

XML se refere a "Extensible Markup Language", é uma markup language que define um conjunto de regras para a codificação de documentos em um formato tanto legível por humanos quanto por máquina.

Com similaridades ao famoso HTML, sua proposta principal é guardar e transportar dados, sendo uma linguagem auto-descritiva. XML funciona como uma estrutura de árvore que é fácil de interpretar e suporta hierarquia.

Os objetivos de design do XML enfatizam a simplicidade, generalidade e usabilidade na Internet. É um formato de dados textual com forte suporte via Unicode para diferentes idiomas humanos. Embora o design de XML se concentre em documentos, a linguagem é amplamente utilizada para a representação de estruturas de dados arbitrárias, como aquelas usadas em serviços web.

Documentos XML possuem seções chamadas de elementos, sendo definidos por uma tag de abertura e fechamento. Tag é um constructo de markup que começa com < e termina com >. Os caracteres entre a tag de abertura e fechamento, caso tenha algum, é o conteúdo do elemento.

Elementos podem conter markup, incluindo outros elementos, que nesse caso, são chamados de filhos. O elemento de maior nível é chamado de root, do inglês, raiz em português, esse seria o elemento que engloba todos os outros. Atributos são pares de nome-valor que existem dentro de uma tag de abertura ou uma tag de elemento vazio. Um atributo XML só pode ter um único valor e cada atributo pode aparecer no máximo uma vez em cada elemento.

Para entendermos melhor o XML, vamos utilizar o arquivo livros.xml como exemplo:

<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Orwell, George</author>
<title>Nineteen Eighty-Four: A Novel</title>
<genre>Dystopian</genre>
<publish_date>1949-06-08</publish_date>
<description>Thematically, Nineteen Eighty-Four centres on the consequences of totalitarianism, mass surveillance, and repressive regimentation of persons and behaviours within society.</description>
</book>
<book id="bk102">
<author>Huxley, Aldous</author>
<title>Brave New World</title>
<genre>Dystopian</genre>
<publish_date>1932-12-16</publish_date>
<description>Largely set in a futuristic World State, whose citizens are environmentally engineered into an intelligence-based social hierarchy, the novel anticipates huge scientific advancements in reproductive technology, sleep-learning, psychological manipulation and classical conditioning that are combined to make a dystopian society which is challenged by only a single individual: the story's protagonist.</description>
</book>
<book id="bk103">
<author>Huxley, Aldous</author>
<title>The Doors of Perception</title>
<genre>Psychology</genre>
<publish_date>1954-11-17</publish_date>
<description>The Doors of Perception provoked strong reactions for its evaluation of psychedelic drugs as facilitators of mystical insight with great potential benefits for science, art, and religion.</description>
</book>
<book id="bk104">
<author>Gibson, William</author>
<title>Neuromancer</title>
<genre>Cyberpunk</genre>
<publish_date>1984-07-01</publish_date>
<description>Set in the future, the novel follows Henry Case, a washed-up computer hacker who is hired for one last job, which brings him up against a powerful artificial intelligence.</description>
</book>
<book id="bk105">
<author>Erickson, Jon</author>
<title>Hacking: The Art of Exploitation</title>
<genre>Computer Security</genre>
<publish_date>2003-09-10</publish_date>
<description>Hacking: The Art of Exploitation is a book by Jon "Smibbs" Erickson about computer security and network security.</description>
</book>
<book id="bk106">
<author>Chollet, François</author>
<title>Deep Learning with Python</title>
<genre>Machine Learning</genre>
<publish_date>2017-11-02</publish_date>
<description>Deep Learning with Python introduces the field of deep learning using the Python language and the powerful Keras library. Written by Keras creator and Google AI researcher François Chollet, this book builds your understanding through intuitive explanations and practical examples.</description>
</book>
<book id="bk107">
<author>Kant, Immanuel</author>
<title>Critique of Pure Reason</title>
<genre>Philosophy</genre>
<publish_date>1787-11-02</publish_date>
<description>The Critique of Pure Reason is a book by the German philosopher Immanuel Kant, in which the author seeks to determine the limits and scope of metaphysics.</description>
</book>
<book id="bk108">
<author>Schopenhauer, Arthur</author>
<title>The World as Will and Representation</title>
<genre>Philosophy</genre>
<publish_date>1818-12-06</publish_date>
<description>Taking the transcendental idealism of Immanuel Kant as his starting point, Schopenhauer argues that the world we experience around us—the world of objects in space and time and related in causal ways—exists solely as 'representation' (Vorstellung) dependent on a cognizing subject, not as a world that can be considered to exist in itself (independently of how it appears to the subject’s mind).</description>
</book>
</catalog>

Como podemos ver, ele é fácil de compreender e legível, assim como os arquivos JSON, agora vamos começar a trabalhar com ele em Python, para isso vamos usar o módulo xml.etree.ElementTree, porém esteja atento que esse módulo não é seguro contra dados maliciosos, então esteja atento na segurança dos dados que você vai processar.

O módulo xml.etree.ElementTree implementa uma API simples e eficiente para analisar e criar dados XML.

Para trabalharmos com este módulo, é necessário apenas importá-lo, sem a necessidade de instalação. Vamos nos referir a ele apenas como et para ficar mais simples para trabalharmos:

import xml.etree.ElementTree as et

Para carregar o nosso arquivo, vamos utilizar o método parse():

arquivo = et.parse('livros.xml')
print(arquivo)
# <xml.etree.ElementTree.ElementTree object at 0x7fb4985b0050>

Observe que nos é retornado um objeto ElementTree, que representa respectivamente uma árvore de elementos XML. Para obtermos a raiz (root) desta árvore, podemos usar o método getroot():

raiz = arquivo.getroot()
print(raiz) # <Element 'catalog' at 0x7fb4984ab530>
print(raiz.tag) # 'catalog'

Como podemos ver, temos um objeto Element com o nome 'catalog', que representa o nosso catálogo de livros, a raiz de nossa árvore, com o atributo tag podemos acessar este valor.

Uma vez que temos a raiz, podemos por exemplo iterar sob os children nodes, que são as tags filhas respectivamente:

for filhas in raiz:
print(filhas.tag, filhas.attrib)
# book {'id': 'bk101'}
# book {'id': 'bk102'}
# book {'id': 'bk103'}
# book {'id': 'bk104'}
# book {'id': 'bk105'}
# book {'id': 'bk106'}
# book {'id': 'bk107'}
# book {'id': 'bk108'}

Neste exemplo específico, estamos obtendo o nome das tags filhas e seus respectivos atributos.

As tags filhas são encadeadas, e para acessarmos seus valores específicos por índice, usamos esta notação:

print(raiz[0][0].text) # Orwell, George
print(raiz[1][1].text) # Brave New World

Agora vamos utilizar o método findall() que nos permite procurar por tags que são filhas diretas do elemento atual que estamos usando. Além disso, temos também o método find() que procura o primeiro filho com uma tag particular e Element.text que acesso o conteúdo de texto do elemento.

for filhas in raiz.findall('book'):
autor = filhas.find('author').text
titulo = filhas.find('title').text
print(f'Autor: {autor} | Título: {titulo}')
# Autor: Orwell, George | Título: Nineteen Eighty-Four: A Novel
# Autor: Huxley, Aldous | Título: Brave New World
# Autor: Huxley, Aldous | Título: The Doors of Perception
# Autor: Gibson, William | Título: Neuromancer
# Autor: Erickson, Jon | Título: Hacking: The Art of Exploitation
# Autor: Chollet, François | Título: Deep Learning with Python
# Autor: Kant, Immanuel | Título: Critique of Pure Reason
# Autor: Schopenhauer, Arthur | Título: The World as Will and Representation

Também podemos usar o método iter() que nos ajuda a iterar sob a árvore XML. Por exemplo:

descricoes = [d.text for d in raiz.iter('description')]
print(descricoes[0])
# Thematically, Nineteen Eighty-Four centres on the consequences of totalitarianism, mass surveillance, and repressive regimentation of persons and behaviours within society.

ElementTree fornece uma maneira simples de construir documentos XML e gravá-los em arquivos. O método write() serve a esse propósito.

Uma vez criado, um objeto Element pode ser manipulado alterando diretamente seus campos (como Element.text), adicionando e modificando atributos (método set()), bem como adicionando novos filhos (por exemplo, com append()).

Imagine que queremos atualizar o gênero do livro Neuromancer para Science Fiction, podemos fazê-lo com o auxílio dos métodos find() e findall() e com um simples if testamos pela existência do gênero atual e o substituímos pelo novo, e através do método set() nós adicionamos um atributo atualizado para esta tag <genre>.

for genre in raiz.findall('book'):
genero = genre.find('genre')
if genero.text == 'Cyberpunk':
genero.text = 'Science Fiction'
genero.set('atualizado','sim')
print(genero.text)
arquivo.write('novo_livros.xml')

Finalmente, com o método write() salvamos os novos dados em um novo arquivo.

Também podemos remover elementos usando o método remove(). Se quisermos por exemplo remover o elemento <publish_date>, podemos usar o método findall() para iterar sob todos os elementos livros e buscar especificamente o elemento publish_date e removê-lo com o método remove(). Novamente podemos salvar as alterações em um novo arquivo com write().

for book in raiz.findall('book'):
publish_date = book.find('publish_date')
book.remove(publish_date)
arquivo.write('new_livros.xml')

Essa foi uma introdução básica ao XML com Python, embora já não esteja tão popular como no passado por conta do poderoso JSON, é necessário que entendamos como funciona a estrutura do arquivo XML e se necessário como manipulá-lo.