Raspagem nos tribunais - Parte 3: extração com XPATH

Neste terceiro tutorial, eu mostro como extrair dados de páginas web baixadas dos tribunais usando uma ferramenta poderosa como xpath.

José de Jesus Filho https://github.com/jjesusfilho
2023-05-02

Introdução

Neste tutorial veremos como usar xpath para extrair dados de um html. Ele faz parte de uma série de tutorias voltados à raspagem de dados de tribuanis com R. Assim como expressões regulares, xpath requer tempo para dominá-lo. Para iniciar, falaremos de html, pois ele é a base sobre a qual xpath opera. Veja o exemplo abaixo.

Exemplo de html

x  <- '<html>
     <head>
         <meta charset="utf-8">
     </head>
     <body>
         <div class="julgado">
             <a href="https://link_para_documento"></a>
            <div class="processo">1234567-23.2022.8.26.0000</div>
            <div class="assunto">Dano moral</div>
            <div class="colegiado">
                  Relator: fulano de tal
                  <br>
                  Revisor: Sicrano
                  <br>
                  Presidente: Beltrano
            </div>
            <div class="classe processual">Procedimento Comum Cível</div>
         </div>
      </body>
  </html>'

O exemplo acima, o qual chamaremos de documento, inicia com o elemento ou tag <html> e termina com</html>. Essa é a sintaxe básica. O mesmo elemento que abre, fecha. Por vezes o encerramento é dispensável, quando entre os dois não vai nada. Por exemplo,<br> indica apenas quebra de linha.

Logo após o <html>, há outra tag <head> e dentro desta, outra tag. No mesmo nível do <head>, há a tag <body> e dentro desta pode haver um número indefinido de tags filhas. Toda tag que contêm outras é chamada de tag mãe ou pai e a tag contida chamada de filha. Uma tag pode ser mãe e filha.

Adicionalmente às tags, existem os atributos. Estes qualificam as tags e individualizam a informação. Essa individualização permite que uma outra linguagem, CSS, agrege aspectos estéticos ao documento. Os atributos vêm com um valor. Em um dos exemplos acima, o atributo classe contêm o valor “processo”.

Esses atributos e tags são marcadores sobre os quais navegamos, com a ajuda de xpath, para extrair as informações desejadas.

Noções de xpath

XPath é uma linguagem de consulta que permite navegar por documentos que usam marcadores, como os arquivos XML e HTML. XPath significa XML Path Language. Para extrar dados de um documento html, você precisa conhecer alguns metacaracteres que lhe permitam navegar. Veja abaixo:

Por exemplo, a expressão XPath /html/body/p seleciona todos os elementos <p> que são filhos do elemento <body> que é filho do elemento <html>. Esta expressão, por outro lado, indica um caminho absoluto, pois usa barra única. Isso significa que esta ordem é estrita, o primeiro elemento é html, logo abaixo dele tem o body e logo abaixo deste tem o p.

Para indicar caminho relativo, ou seja, procurar o elemento independentemente se ele é filho, neto, bisneto etc, usamos barras duplas //. Por exemplo, se fizermos //p, iremos nos posicionar em todos os elementos p que estejam no mesmo nível daquele foi encontrado pela primeira vez.

Além disso, xpath possui verbos e outros indicadores. Por vezes, o próprio conteúdo pode ser considerado um elemento. Mais adiante, iremos dar exemplos.

O R possui um pacote que permite navegar por html e extrair as informações desejadas. O nome dele é xml2. Vamos trabalhar no exemplo acima que foi associado ao objeto x.

library(xml2) ### Carregue o pacote xml2

doc <- x |> 
    read_html() ## importa para o R o html com a classe xml_document.

A primeira coisa a fazer é ler o html para o R usando a função read_html(). O objeto x era um mero texto, poderia ser um arquivo html. Ao lê-lo com read_html, convertemos em xml_document. Somente agora podemos aplicar xpath.

doc |> 
  xml_find_all("/*") |> ### Encontra o elemento 
  xml_text() |> ## Extrai o texto dentro deste elemento.
  cat() ## Usado apenas para uma visualização amigável.

         
             
            1234567-23.2022.8.26.0000
            Dano moral
            
                  Relator: fulano de tal
                  
                  Revisor: Sicrano
                  
                  Presidente: Beltrano
            
            Procedimento Comum Cível
         
      

No exemplo acima, eu usei “/*“, ou seja, posicione no documento inteiro. Depois disso, eu usei xml_text() para extrair todo o conteúdo textual do documento. Usei cat() apenas para uma visualização agradável.

Agora vamos extrair apenas o número do processo:

doc |> 
  xml_find_all("//div[@class='processo']") |> 
  xml_text()
[1] "1234567-23.2022.8.26.0000"

Note que eu usei o caminho relativo “//div”, ou seja, saltei diretamente para o primeiro div do documento. No entanto, há mais divs irmãos desse div, então eu preciso informar que quero apenas o div que contenha o processo. Para tanto, uso [@class='processo'], ou seja, vá para a div que contenha o atributo class cujo valor é processo.

Agora vamos pegar o assunto:

doc |> 
  xml_find_all("//div[@class='assunto']") |> 
  xml_text()
[1] "Dano moral"

Por vezes, não há um atributo que distinga claramente um conteúdo. Vamos supor que não tenha nenhum atributo indicando o assunto, apenas indicando o processo. Podemos informar ao xpath que queremos o conteúdo do div que vem logo depois do div do processo:

doc |> 
  xml_find_all("//div[@class='processo']/following-sibling::div[1]") |>  
  xml_text()
[1] "Dano moral"

Note que eu fui até o div do processo e, em seguida, usei following-sibling::div, ou seja, pegue o irmão seguinte que se chama div.

Agora vamos supor que eu queira todos as categorias, ou seja, os valores dos atributos: processo, assunto, colegiado e classe processual, e não o conteúdo:

doc |> 
  xml_find_all("//div/div") |> 
  xml_attr("class")
[1] "processo"          "assunto"           "colegiado"        
[4] "classe processual"

Note que eu fui até o div filho de um div e, em vez de usar xml_text(), usei xml_attr(“class”) para indicar que quero o atributo e não o conteúdo.

Outra maneira de atingir o mesmo objetivo é:

doc |> 
  xml_find_all("//div/div/@class") |> 
  xml_text()
[1] "processo"          "assunto"           "colegiado"        
[4] "classe processual"

No exemplo acima, eu converti o atributo em elemento e extraí o conte údo dele com xml_text().

Há situações em que eu quero caminhar para trás. por exemplo, vamos pegar a irmã anterior do assunto, ou seja, processo:

doc |> 
  xml_find_all("//div[@class='assunto']/preceding-sibling::div") |> 
  xml_text()
[1] "1234567-23.2022.8.26.0000"

Note que usei preceding-sibling para atingir meu objetivo.

Por vezes queremos obter o conteúdo do pai de um elemento. Por exemplo, podemos extrair o link do inteiro teor desta forma, sem recorrer ao pai:

doc |> 
  xml_find_all("//div/a") |> 
  xml_attr("href")
[1] "https://link_para_documento"

Outra maneira de fazer isso, é recorrer ao indicador pai:

doc |> 
  xml_find_all("//div[@class='processo']/../a") |> 
  xml_attr("href")
[1] "https://link_para_documento"

Note que eu fui até a div do processo e usei .. (dois pontinhos) para voltar à mãe desse div e caminhar até a tag a.

Outra forma:

doc |> 
  xml_find_all("//div[@class='processo']/parent::div/a") |> 
  xml_attr("href")
[1] "https://link_para_documento"

Esta forma é mais precisa, especialmente quando queremos um avô ou bisavô mais distante.

Por fim, vamos dar um exemplo em que queremos extrair apenas o relator, ou seja o primeiro texto do colegiado.

doc |> 
  xml_find_all("//div[@class='colegiado']/text()[following-sibling::br][1]") |> 
  xml_text()
[1] "\n                  Relator: fulano de tal\n                  "

Veja que eu usei o texto como como tag. Encontrei todo os textos dentro de colegiado seguidos de quebra de linha <br>, mas retive apenas o primeiro.

Se eu quiser o revisor;

doc |> 
  xml_find_all("//div[@class='colegiado']/text()[following-sibling::br][2]") |> 
  xml_text()
[1] "\n                  Revisor: Sicrano\n                  "

Isso já não funcionaria com o presidente, pois ele não é seguido de br. Para tanto, precido pensar num artifício. Vou até o texto que tem duas quebras de linha antes dele:

doc |> 
  xml_find_all("//div[@class='colegiado']/text()[preceding-sibling::br][2]") |> 
  xml_text()
[1] "\n                  Presidente: Beltrano\n            "

Em todos os casos acima, eu usei xml_find_all porque geralmente estou buscando várias ocorrência numa mesma página. No entanto, como havia uma só ocorrência, eu deveria ter usado xml_find_first.

Por fim, nos últimos exemplos, o resultado veio com quebra de linha e espaços extras, eu posso eliminá-los usando o argumento trim = TRUE.

doc |> 
  xml_find_all("//div[@class='colegiado']/text()[preceding-sibling::br][2]") |> 
  xml_text()
[1] "\n                  Presidente: Beltrano\n            "

Citation

For attribution, please cite this work as

Filho (2023, May 2). Jurimetria: Raspagem nos tribunais - Parte 3: extração com XPATH. Retrieved from https://direitoemdados.consudata.com.br/posts/2023-05-02-extracaocomxpath/

BibTeX citation

@misc{filho2023raspagem,
  author = {Filho, José de Jesus},
  title = {Jurimetria: Raspagem nos tribunais - Parte 3: extração com XPATH},
  url = {https://direitoemdados.consudata.com.br/posts/2023-05-02-extracaocomxpath/},
  year = {2023}
}