Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Raspador: Biblioteca em Python para extração de...

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

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

Fernando Macedo

October 04, 2013
Tweet

Other Decks in Programming

Transcript

  1. SOBRE MIM Desenvolvedor desde 2003 Conheci Python em 2009 Trabalho

    na NCR Corporation Na NCR, Python não é a linguagem primária
  2. Foi utilizado para extração de dados de Espelhos MFD Virou

    código de base do projeto f r o m r a s p a d o r i m p o r t h i s t o r y
  3. 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
  4. C N P J : 4 0 . 1 0

    0 . 2 8 0 / 0 0 0 1 - 2 5 I E : 6 0 0 0 2 0 0 6 0 0 0 1 I M : 3 6 / 3 3 7 2 1 8 / 0 1 / 2 0 1 3 1 1 : 0 7 : 0 4 C C F : 0 0 2 9 0 2 C O O : 0 0 7 4 9 0 C U P O M F I S C A L I T E M C Ó D I G O D E S C R I Ç Ã O Q T D . U N . V L U N I T R $ S T V L I T E M R $ 0 0 1 1 p r d 1 1 U N I 1 1 , 0 0 € 0 0 2 2 p r d 2 N i n c i d 1 U N N 1 2 , 0 0 € 0 0 3 9 9 9 9 9 9 9 9 9 9 9 9 1 P I Z Z A S 1 U N I 1 1 4 , 3 3 € S u b t o t a l R $ 1 7 , 3 3 A C R É S C I M O + 0 , 3 0 € T O T A L R $ 1 7 , 6 3 D i n h e i r o 1 7 , 6 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - M D 5 : A 3 B B E 7 3 B D 0 9 B 1 8 E C E 6 0 7 A 5 0 F 9 2 8 6 8 A 4 E 0 2 B 1 3 1 B 4 3 5 A 4 E F 5 9 0 0 0 B 6 5 9 5 0 4 C 7 2 A 1 E 0 6 6 9 F 0 2 7 E C F - I F V E R S Ã O : 0 1 . 0 1 . 0 0 E C F : 0 0 1 L j : B B B B B B B B B B A A B F C D E I 1 8 / 0 1 / 2 0 1 3 1 1 : 0 7 : 0 6 F A B : X X 0 0 0 0 0 0 0 0 0 0 0 0 2 0 7 0 5 3 B R
  5. { ' C O O ' : 7 4 9

    0 , ' C C F ' : 2 9 0 2 , ' T o t a l ' : 1 7 . 6 3 , ' A c r e s c i m o ' : 0 . 3 0 , ' C a n c e l a d o ' : F a l s e , ' C a n c e l a m e n t o ' : F a l s e , ' D a t a D e E m i s s a o ' : d a t e t i m e ( 2 0 1 3 , 0 1 , 1 8 , 1 1 , 7 , 4 ) , ' N u m e r o D e S e r i e ' : ' D R 0 5 1 0 B R 0 0 0 0 0 0 2 0 7 1 5 3 ' , ' N u m e r o D o E c f ' : 1 , ' I t e n s ' : [ { ' I t e m ' : 1 , ' C o d i g o ' : ' 1 ' , ' D e s c r i c a o ' : ' p r d 1 ' , ' Q t d ' : 1 , ' U n i d a d e ' : ' U N ' , ' P r e c o ' : 1 , ' T o t a l ' : 1 , ' C a n c e l a d o ' : F a l s e , ' A l i q u o t a ' : { ' C o d i g o ' : ' I 1 ' ,
  6. PROBLEMA Extrair dados em documentos de texto Texto sem marcação

    Arquivos grandes Pequenas variações entre arquivos Precisão na extração dos dados
  7. 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
  8. O que faz? r e s = [ ] f

    o r l i n h a i n e n t r a d a . s p l i t l i n e s ( ) : i f n o t l i n h a : c o n t i n u e i t e m = { } f o r p a r t e i n l i n h a . s p l i t ( ) : k , v = p a r t e . s p l i t ( ' : ' ) i t e m [ k ] = v r e s . a p p e n d ( i t e m ) Você entende o código, mas não tem significado.
  9. REGULAR EXPRESSIONS Some people, when confronted with a problem, think

    "I know, I'll use regular expressions." Now they have two problems. (Jamie Zawinski, 1997)
  10. I n [ ] : # O q u e

    i s s o f a z ? r e g e x = " ^ ( ( ( [ ! # $ % & ' * + \ - / = ? ^ _ ` { | } ~ \ w ] ) | ( [ ! # $ % & ' * + \ - / = ? ^ _ ` { | } ~ \ w ] [ ! # $ % & ' * + \ - / = ? ^ _ ` { | } ~ \ . \ w ] { 0 , } [ ! # $ % & ' * + \ - / = ? ^ _ ` { | } ~ \ w ] ) ) [ @ ] \ w + ( [ - . ] \ w + ) * \ . \ w + ( [ - . ] \ w + ) * ) $ " Email validation - RFC 2821, 2822 compliant
  11. pessoa_parser.py f r o m r a s p a

    d o r i m p o r t P a r s e r f r o m r a s p a d o r i m p o r t S t r i n g F i e l d , I n t e g e r F i e l d c l a s s P a r s e r D e I n f o r m a c o e s P e s s o a i s ( P a r s e r ) : N o m e = S t r i n g F i e l d ( r ' N o m e : ( . * ) ' ) I d a d e = I n t e g e r F i e l d ( r ' ( \ d + ) a n o s ' ) A definição de um atributo e o tipo de dado agregam semântica
  12. pessoa.txt Nome: Guido van Rossum Guido van Rossum é um

    programador de computadores dos Países Baixos que é mais conhecido por ser o autor da linguagem de programaçã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
  13. pessoa_utilizacao.py f r o m p e s s o

    a _ p a r s e r i m p o r t P a r s e r D e I n f o r m a c o e s P e s s o a i s p a r s e r = P a r s e r D e I n f o r m a c o e s P e s s o a i s ( ) w i t h o p e n ( ' p e s s o a . t x t ' ) a s f : f o r p e s s o a i n p a r s e r . p a r s e ( f ) : p r i n t ( p e s s o a . N o m e ) p r i n t ( p e s s o a . I d a d e ) Guido van Rossum 57
  14. # p a r s e r . p a

    r s e r e t o r n a u m g e n e r a t o r w i t h o p e n ( ' p e s s o a . t x t ' ) a s f : g = p a r s e r . p a r s e ( f ) p r i n t ( t y p e ( g ) ) p r i n t ( n e x t ( g ) ) < t y p e ' g e n e r a t o r ' > D i c t i o n a r y ( [ ( ' N o m e ' , ' G u i d o v a n R o s s u m ' ) , ( ' I d a d e ' , 5 7 ) ] )
  15. RASPADOR.ITEM c l a s s D i c t

    i o n a r y ( O r d e r e d D i c t ) : " " " D i c t i o n a r y t h a t e x p o s e s k e y s a s p r o p e r t i e s f o r e a s y r e a d a c c e s s . " " " d e f _ _ g e t a t t r _ _ ( s e l f , n a m e ) : i f n a m e i n s e l f : r e t u r n s e l f [ n a m e ] r a i s e A t t r i b u t e E r r o r ( " % s w i t h o u t a t t r ' % s ' " % ( t y p e ( s e l f ) . _ _ n a m e _ _ , n a m e ) )
  16. CAMPOS BUILT-IN f r o m r a s p

    a d o r i m p o r t ( B a s e F i e l d , I n t e g e r F i e l d , S t r i n g F i e l d , B o o l e a n F i e l d , F l o a t F i e l d , B R F l o a t F i e l d , D a t e F i e l d , D a t e T i m e F i e l d ) TODO: B R F l o a t F i e l d , definir sistema de localização.
  17. BASEFIELD search > > > s = " 0 2

    / 0 1 / 2 0 1 3 1 0 : 2 1 : 5 1 C O O : 0 2 2 7 3 4 " > > > f i e l d = B a s e F i e l d ( s e a r c h = r ' C O O : ( \ d + ) ' ) > > > f i e l d . p a r s e _ b l o c k ( s ) ' 0 2 2 7 3 4 '
  18. BASEFIELD input_processor > > > s = " 0 2

    / 0 1 / 2 0 1 3 1 0 : 2 1 : 5 1 C O O : 0 2 2 7 3 4 " > > > d e f d o u b l e ( v a l u e ) : . . . r e t u r n i n t ( v a l u e ) * 2 . . . > > > f i e l d = B a s e F i e l d ( r ' C O O : ( \ d + ) ' , . . . i n p u t _ p r o c e s s o r = d o u b l e ) > > > f i e l d . p a r s e _ b l o c k ( s ) # 4 5 4 6 8 = 2 x 2 2 7 3 4 4 5 4 6 8
  19. BASEFIELD is_list > > > s = " 0 2

    / 0 1 / 2 0 1 3 1 0 : 2 1 : 5 1 C O O : 0 2 2 7 3 4 " > > > f i e l d = B a s e F i e l d ( r ' C O O : ( \ d + ) ' , i s _ l i s t = T r u e ) > > > f i e l d . p a r s e _ b l o c k ( s ) [ ' 0 2 2 7 3 4 ' ] Por convenção, quando o campo retorna uma lista, os valores serão acumulados.
  20. DATEFIELD format_string > > > s = " 2 0

    1 3 - 0 1 - 0 2 T 1 0 : 2 1 : 5 1 C O O : 0 2 2 7 3 4 " > > > f i e l d = D a t e F i e l d ( r ' ^ ( \ d + - \ d + - \ d + ) ' , . . . f o r m a t _ s t r i n g = ' % Y - % m - % d ' ) > > > f i e l d . p a r s e _ b l o c k ( s ) d a t e t i m e . d a t e ( 2 0 1 3 , 1 , 2 )
  21. NEM TUDO QUE É TEXTO ... está em texto Dica:

    Mantém a estrutura do arquivo gerado próxima com o original. pdftotext p d f t o t e x t - l a y o u t < a r q u i v o . p d f >
  22. TESTES Testes automatizados com . Bibliotecas de terceiros para os

    testes são instaladas automaticamente no ambiente virtual da versão do Python: tox $ t o x n o s e = = 1 . 3 . 0 c o v e r a g e = = 3 . 6 f l a k e 8 = = 2 . 0