32
RASPADOR Mini-biblioteca para extração de dados em documentos semi- estruturados

Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

Embed Size (px)

DESCRIPTION

Slides em HTML5: http://fgmacedo.github.io/talks/pybr9_raspador Palestra apresentada na PythonBrasil[9], em Brasília. Com aproximadamente 500 linhas de código (+testes), o raspador é uma mini-biblioteca para extração de dados em fontes semi-estruturadas. Está em produção utilizado como fundamento para extração de dados em Espelhos MFD de impressoras fiscais. A definição dos extratores é feita através de classes como modelos, de forma semelhante ao ORM do Django. Cada extrator procura por um padrão especificado por expressão regular, e a conversão para tipos primitidos é feita automaticamente a partir dos grupos capturados. O analisador é implementado como um gerador, onde cada item encontrado pode ser consumido antes do final da análise, caracterizando uma pipeline. A análise é foward-only, o que o torna extremamente rápido, e deste modo qualquer iterador que retorne uma string pode ser analisado, incluindo streams infinitos. Com uma base sólida e enxuta, é fácil construir seus próprios extratores. Além da utilidade da ferramenta, o raspador é um exemplo prático e simples da utilização de conceitos e recursos como iteradores, geradores, meta-programação e property-descriptors. http://2013.pythonbrasil.org.br/program/pb/other/raspador-uma-mini-biblioteca-tupiniquim-para-extracao-de-dados

Citation preview

Page 1: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

RASPADORMini-biblioteca para extração de dados em documentos semi-estruturados

Page 2: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

SOBRE MIMDesenvolvedor desde 2003Conheci Python em 2009Trabalho na NCR CorporationNa NCR, Python não é a linguagem primária

Page 3: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

Foi utilizado para extração de dados de Espelhos MFDVirou código de base do projeto

from raspador import history

Page 4: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

OUTRO PARSER?lxml (XPath, cssselectors)html5lib (html parser)BeautifulSoup (tree parser api)PyQuery (cssselectors)Scrapely (magia negra)Scrapy (crawler: request, responsing)pyparsing (grammar)NLTK (grammar)Plain Python + regex

Page 5: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

O QUE?Extrair dados de arquivos texto que não foram projetados paraisso.

Page 6: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

CNPJ: 40.100.280/0001-25 IE: 600020060001 IM: 36/3372 18/01/2013 11:07:04 CCF:002902 COO:007490 CUPOM FISCAL ITEM CÓDIGO DESCRIÇÃO QTD.UN.VL UNIT R$ ST VL ITEM R$ 001 1 prd1 1UN I1 1,00€ 002 2 prd2 Nincid 1UN N1 2,00€ 003 9999999999991 PIZZAS 1UN I1 14,33€Subtotal R$ 17,33ACRÉSCIMO +0,30€ TOTAL R$ 17,63Dinheiro 17,63------------------------------------------------MD5: A3BBE73BD09B18ECE607A50F92868A4E 02B 131B4 35A4E F59000 B6 59504C 72A1E 0669F 027ECF-IF VERSÃO:01.01.00 ECF:001 Lj: BBBBBBBBBBAABFCDEI 18/01/2013 11:07:06 FAB:XX000000000000207053 BR

Page 7: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

{ 'COO': 7490, 'CCF': 2902, 'Total': 17.63, 'Acrescimo': 0.30, 'Cancelado': False, 'Cancelamento': False, 'DataDeEmissao': datetime(2013, 01, 18, 11, 7, 4), 'NumeroDeSerie': 'DR0510BR000000207153', 'NumeroDoEcf': 1, 'Itens': [ { 'Item': 1, 'Codigo': '1', 'Descricao': 'prd1', 'Qtd': 1, 'Unidade': 'UN', 'Preco': 1, 'Total': 1, 'Cancelado': False, 'Aliquota': { 'Codigo': 'I1',

Page 8: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

PROBLEMAExtrair dados em documentos de texto

Texto sem marcaçãoArquivos grandes

Pequenas variações entre arquivosPrecisão na extração dos dados

Page 9: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

OPÇÕES?lxml (XPath, cssselectors)html5lib (html parser)BeautifulSoup (tree parser api)PyQuery (cssselectors)Scrapely (magia negra)Scrapy (crawler: request, responsing)pyparsing (grammar)NLTK (grammar)Plain Python + regex

Page 10: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

PLAIN PYTHON + REGEXFácil de escreverDifícil de manter

Write only code

Page 11: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

O que faz?

res = []for linha in entrada.splitlines(): if not linha: continue item = {} for parte in linha.split(): k, v = parte.split(':') item[k] = v res.append(item)

Você entende o código, mas não tem significado.

Page 12: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

REGULAR EXPRESSIONSSome people, when confronted with a problem,think "I know, I'll use regular expressions." Nowthey have two problems. (Jamie Zawinski, 1997)

Page 13: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

In []:

# O que isso faz?regex = "̂((([!#$%&'*+\-/=?̂_̀{|}~\w])|([!#$%&'*+\-/=?̂_{̀|}~\w][!#$%&'*+\-/=?̂_̀{|}~\.\w]{0,}[!#$%&'*+\-/=?̂_̀{|}~\w]))[@]\w+([-.]\w+)*\.\w+([-.]\w+)*)$"

Email validation - RFC 2821, 2822 compliant

Page 14: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

Não exagere

(Jeff Atwood)I love regular expressions

Page 15: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

OBJETIVOSReduzir complexidade

Incluir semânticaFavorecer composição

Código testável

Page 16: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

pessoa_parser.py

from raspador import Parserfrom raspador import StringField, IntegerField

class ParserDeInformacoesPessoais(Parser): Nome = StringField(r'Nome: (.*)') Idade = IntegerField(r'(\d+) anos')

A definição de um atributo e o tipo de dado agregam semântica

Page 17: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

pessoa.txt

Nome: Guido van Rossum

Guido van Rossum é um programador decomputadores dos Países Baixos que é maisconhecido por ser o autor da linguagem deprogramação Python. Wikipédia

Nascimento: 31 de janeiro de 1956 (57 anos),Países Baixos

Cônjuge: Kim Knapp (desde 2000)

Educação: Universidade de Amsterdã (1982)

Filho: Orlijn Michiel Knapp-van Rossum

Irmão: Just van Rossum

Page 18: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

pessoa_utilizacao.py

from pessoa_parser import ParserDeInformacoesPessoais

parser = ParserDeInformacoesPessoais()

with open('pessoa.txt') as f: for pessoa in parser.parse(f): print(pessoa.Nome) print(pessoa.Idade)

Guido van Rossum

57

Page 19: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

# parser.parse retorna um generatorwith open('pessoa.txt') as f: g = parser.parse(f) print(type(g)) print(next(g))

<type 'generator'>

Dictionary([('Nome', 'Guido vanRossum'), ('Idade', 57)])

Page 20: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

RASPADOR.ITEMclass Dictionary(OrderedDict): """ Dictionary that exposes keys as properties for easy read access. """ def __getattr__(self, name): if name in self: return self[name] raise AttributeError( "%s without attr '%s'" % (type(self).__name__, name))

Page 21: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

CAMPOS BUILT-INfrom raspador import ( BaseField, IntegerField, StringField, BooleanField, FloatField, BRFloatField, DateField, DateTimeField)

TODO: BRFloatField, definir sistema de localização.

Page 22: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

BASEFIELDsearch

>>> s = "02/01/2013 10:21:51 COO:022734">>> field = BaseField(search=r'COO:(\d+)')>>> field.parse_block(s)'022734'

Page 23: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

BASEFIELDinput_processor

>>> s = "02/01/2013 10:21:51 COO:022734">>> def double(value):... return int(value) * 2...>>> field = BaseField(r'COO:(\d+)', ... input_processor=double)>>> field.parse_block(s) # 45468 = 2 x 2273445468

Page 24: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

BASEFIELDis_list

>>> s = "02/01/2013 10:21:51 COO:022734">>> field = BaseField(r'COO:(\d+)', is_list=True)>>> field.parse_block(s)['022734']

Por convenção, quando o campo retorna uma lista, os valoresserão acumulados.

Page 25: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

DATEFIELDformat_string

>>> s = "2013-01-02T10:21:51 COO:022734">>> field = DateField(r'̂(\d+-\d+-\d+)', ... format_string='%Y-%m-%d')>>> field.parse_block(s)datetime.date(2013, 1, 2)

Page 26: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

PARSERResponsável por conduzir a iteraçãoPodem ser alinhados

Page 27: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

NEM TUDO QUE É TEXTO... está em texto

Dica:

Mantém a estrutura do arquivo gerado próxima com o original.

pdftotext

pdftotext -layout <arquivo.pdf>

Page 28: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

REGULAR EXPRESSIONSDebuggex: visualize suas REs

Aurélio

https://www.debuggex.com/

Expressões regulares, uma abordagem divertida

Page 29: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

COMPATIBILIDADECPython 2.6+

2.6: pip install ordereddictCPython 3.2+PyPy

Page 30: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

TESTESTestes automatizados com .

Bibliotecas de terceiros para os testes são instaladasautomaticamente no ambiente virtual da versão do Python:

tox

$ tox

nose==1.3.0 coverage==3.6 flake8==2.0

Page 32: Raspador: Biblioteca em Python para extração de dados em texto semi-estruturado

OBRIGADO!Fernando Macedo

(Slides)

@fgmacedo

fgmacedo.com

[email protected]

http://code.fgmacedo.com/talks