domingo, 28 de fevereiro de 2016

Detectando Encoding e Tipo de Arquivo com Python

Read this blog post in English.

O conteúdo desse artigo está disponível também em vídeo:

Alguns dos usuários da minha biblioteca rows me pediram funcionalidades de detecção de encoding e tipo de arquivo, então comecei a procurar alguma biblioteca Python simples e rápida que desse conta dessa tarefa. O problema: encontrei muitas bibliotecas e nenhuma delas me atraiu por alguns dos seguintes motivos:

  • Não tinha uma implementação pythônica;
  • Não estava disponível no Debian (se eu usasse o pacote rows no Debian iria quebrar);
  • Não estava sendo mantida atualmente;
  • Não funcionava corretamente.

Então percebi que não existe uma "solução de ouro" para esse problema em Python. Muitos pythonistas usam a biblioteca chardet, mas ela detecta muitas vezes com erros e é razoavelmente lenta (principalmente se você precisar fazer a detecção enquanto um usuário aguarda a resposta através de uma chamada de API via HTTP) -- veja mais detalhes sobre isso no vídeo.

But there should be one -- and preferably only one -- obvious way to do it.

Então, eu pensei: por que não usar o programa file, que já é bastante conhecido por hackers do mundo UNIX, é rápido e muito mais assertivo que todas as outras opções? Para minha surpresa não existia um bom binding de Python para a libmagic (algumas bibliotecas rodavam o comando file mas isso não era uma opção para mim pois dependia de mais um pacote do sistema e não era uma solução muito portável).

Depois de vasculhar o repositório de código do file eu encontrei um binding simples para Python, que na época não estava disponível no PyPI e não era tão pythônica como eu esperava.

A Solução

Como o código é software livre, eu criei uma issue no sistema de bugs do file para resolver o problema e o Christos Zoulas (atual mantenedor) me pediu um patch, que eu implementei, enviei e foi aceito. :-)

Fiquei muito feliz de poder colaborar com um software importante e que eu uso desde de meus primeiros passos no mundo GNU/Linux (2003? 2004?)! Durante minha busca eu descobri que o primeiro commit da versão livre do file é de 1987 (eu tinha menos de 4 meses de idade!) -- e o software ainda é mantido hoje.

Agora todos podemos usar binding oficial do file para Python: a nova biblioteca é chamada file-magic e pode ser instalada executando:

pip install file-magic

Ela disponibiliza várias funções e atributos, mas as mais importantes são bem simples e intuitivas: elas retornam uma namedtuple com os resultados da detecção. Vamos ao código!

>>> import magic

>>> # Você pode especificar diretamente um nome de arquivo
>>> filename_detected = magic.detect_from_filename('turicas.jpg')
>>> print(filename_detected)
FileMagic(mime_type='image/jpeg', encoding='binary',
          name='JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 842x842, frames 3')
>>> # E você pode acessar os atributos da `namedtuple`:
>>> print(filename_detected.mime_type)
image/jpeg

# E se você já tem o conteúdo do arquivo em memória:
>>> with open('data.html') as fobj:
...     data = fobj.read()
>>> content_detected = magic.detect_from_content(data)
>>> print(content_detected)
FileMagic(mime_type='text/html', encoding='utf-8',
          name='HTML document, UTF-8 Unicode text')
>>> print(content_detected.encoding)
utf-8

Algumas coisas ainda precisam ser melhoradas (como a documentação, rodar os testes em outras plataformas etc.), porém a biblioteca já pode ser instalada através do pip, é rápida e precisa. :-)

Espero que vocês tenham gostado! ;-)

Nenhum comentário:

Postar um comentário