Detectando Encoding e Tipo de Arquivo com Python
Autor: Álvaro Justen "Turicas" | Publicado: 2016-02-28T15:08:00-03:00

Read this blog post in English.
O conteúdo desse artigo está disponível também em vídeo:
<iframe width="100%" height="400"
        src="https://www.youtube.com/embed/BTMj5bDXByc" frameborder="0"
        allowfullscreen>

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 &quot;solução de ouro&quot; 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!
&gt;&gt;&gt; import magic

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

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