domingo, 21 de fevereiro de 2016

Utilizando argumentos no framework scrapy e usando tesseract com python

Olá meninos e meninas, hoje vou mostrar um pouco do framework scrapy, eu tenho desenvolvido um crawler para um amigo (Balestra, to terminando hehehe) e cada vez mais tenho usado o scrapy para poupar tempo no desenvolvimento de crawlers, pela facilidade de interação e de escrita de código.

Hoje eu vou compartilhar com vocês algo que achei interessante durante o desenvolvimento do crawler que tenho estudado, é a passagem de parâmetros que achei bem simples e uma questão nova que tenho visto que é a utilização do tesseract com python para processar textos de imagens.

Primeiro, vamos iniciar um projeto no scrapy(Caso não saiba como, leia esse artigo).

Eu iniciei da seguinte forma:

(sim, vou sacanear e colocar os comandos em imagens, quanto mais você digitar mais você aprende)


De acordo com os comandos da imagens, eu startei o projeto e criei um spider da olx, agora vamos começar o coding do spider.

Como disse em outro post, a estrutura de arquivos é a seguinte:

#:> tree olx_crawl/
olx_crawl/
├── olx_crawl
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   ├── settings.pyc
│   └── spiders
│       ├── __init__.py
│       ├── __init__.pyc
│       └── olx_spider.py
└── scrapy.cfg

Vamos editar o arquivo "olx_spider.py" que fica dentro da pasta spiders, como pode ver na árvore de diretório acima.

Inicialmente ele vem nessa estrutura:


Certo, nesse caso eu quero demonstrar para vocês como usar argumentos no scrapy, vamos modificar o arquivo padrão para que ele aceite argumentos passados na hora de realizar o crawler, primeiro eu entrei no site da OLX e fiz uma pesquisa qualquer na sessão de carros para ver como ele interpretava minha consulta, percebi que ele passa a query por GET da seguinte forma http://sp.olx.com.br/veiculos/carros?q=%22Golf+GTI%22 , onde "?q=" é a variável da pesquisa e o %22Golf+GTI%22 é o parâmetro que queremos pesquisar.

Desse modo vou editar o arquivo padrão e incluir um método __init__ para receber os parâmetros de pesquisa, ficando da seguinte forma:


Agora vamos fazer o teste?

Se ficar pequena a imagem, clique nela para expandir.
Podemos ver na imagem como foi feio o teste passando o parâmetro depois de ter editado o código, repare no comando:


Eu fiz uma pesquisa pelo termo exato(adicionando aspas duplas ele pesquisa pelas palavras exatas no titulo do anúncio, pode ver mais search tips aqui http://www.olx.com.br/search_tips.htm) de Golf GTI, por isso a pesquisa com aspas simples e aspas duplas.
Voltando ao comando, para utilizar o parâmetro basta colocar "-a" e o termo que vai pesquisar que foi inserido no código, pode ver na linha 8 do código, eu atribui a variável como "pesquisa", mas poderia ter colocado a variável como "piricutisco" e usado da seguinte forma:
 
#:> scrapy crawl olx_spider -a piricutisco='"Golf GTI"'

Sacaram? 

- Poxa mano, mas e se minha pesquisa tiver N argumentos?
R: Poderá fazer desse modo:


#:> scrapy crawl olx_spider -a piricutisco='"Golf GTI"' -a Goku="majin boo"

Note que cada vez que adiciona um parâmetro a chamada(-a) do parâmetro tem que ser usada.(Exemplo acima é meramente ilustrativo, ele não funciona na pagina da OLX)

E podemos verificar que o parâmetro foi passado corretamente no LOG, reparem na imagem abaixo:



Certo, voltando ao código original que estávamos desenvolvendo, vamos começar a implementar a inteligência do robô, nesse exemplo, como quero demonstrar o uso também do tesseract, vamos fazer o download e a salvar o telefone de contato do anunciante, caso não tenham reparado, o telefone tem duas proteções contra crawlers:

  1. Ele está atrás de um javascript.
  2. O texto do telefone está como imagem, para evitar o salvamento automático do telefone ou sei lá o que.

Mas, como somos trakinas, vamos realizar um bypass.

Primeiro, vamos entender a pagina onde vamos realizar o parser. Assim que realizamos a pesquisa ele nos leva para a pagina onde ficam os resultados, nosso robô tem que entrar em anuncio por anuncio e passar a pagina e assim por diante até acabar os resultados.

Vou usar como referência o artigo do Gileno[1] para realizar o parser das paginas da OLX, mas para entender melhor e não ter que ficar pulando de artigo p/ artigo, vou explicar como realizar o parser aqui.

Abrindo a pagina de pesquisa (estou usando o chrome), eu abri a ferramenta de desenvolvedor do chrome para analisar o código fonte, como na imagem abaixo eu procuro a tag html responsável pela lista de carros, observe:

Se ficar pequena a imagem, clique nela para expandir.
No caso da OLX a tag que eu procurava é a div class="section_OLXad-list " que contém a listagem dos carros, agora vamos pegar a tag li class="item" que contém o anuncio do carro, dessa forma vamos montar uma consulta de xpath para adicionar no código do crawler pra quele possa realizar o parser.

//div[contains(@class,"section_OLXad-list")]//li[contains(@class,"item")]

Acho que o entendimento é bem simples, mas vou tentar explicar da forma que aprendi, caso esteja errado, por favor me corrijam.

Antes, vou explicar sobre os operadores // e / do xpath, eles são utilizados para definir a pesquisa dentro do texto HTML.

/div - usado para fazer a pesquisa da tag div dentro da raiz do html, caso não tenha entendido a questão da raiz, ela funciona da seguinte forma, o html ele tem uma estrutura, assim como uma estrutura de diretórios ou a indentação de um código, o xpath ele faz essa distinção, quando usamos apenas uma barra ele faz a procura penas no "diretório" raiz do HTML.

//div - usado para fazer a pesquisa da tag div em todo o código HTML, seguindo o exemplo acima, seria como pesquisa de forma recursiva dentro de todos os diretórios, trazendo tudo que ele encontrar com a tag div.

Ok, entendido isso, acho que fica mais fácil explicar o xpath acima, ele procura em todo o código pela tag div que contenha o parâmetro class que seja igual a "section_OLXad-list" e assim que ele achar, ele pesquisa dentro de todos esses "divs" encontrados a tag li que contenha a classe "item".

Não sei se consegui ser claro, mas qualquer dúvida que fique, postem nos comentários que vou tentar responder o quanto antes.

Vamos montar o código agora no nosso spider, apenas seguindo o que o Gileno fez, segue o código abaixo com parte do que o Gileno desenvolveu no artigo dele, a princípio esse código servirá como base para coletarmos o telefone e alguns dados a mais.

Caso queira entender como foi realizado as outras linhas de código, visite o artigo do Gileno.


Certo, agora vamos para a parte que interessa, vou mostrar como usar o tesseract junto com o python, fazendo ele reconhecer a imagem a partir da pagina web.

No site da OLX, na pagina do anuncio, podemos ver que tem um telefone de contato como mostra na imagem abaixo:


O telefone está "protegido" por um javascript, dificultando a ação de crawlers, porém ele foi mal implementado, podemos notar no código fonte da pagina que a imagem original do telefone se encontra dentro das tags noscript, veja na imagem abaixo:


Repare que dentro da tag noscript tem a imagem original dentro da tag img, nesse caso eu implementei da seguinte forma o código para a leitura da imagem:


Dessa forma eu extraio o link da imagem original, assim consigo acessar a imagem com o telefone e usar o tesseract para transformar a imagem em texto, pode observar no código abaixo que fiz alguns imports novos e add mais 7 linhas no final do código.


Agora, vamos rodar o código para ver se está tudo OK.



Ao som de Astrix:


REFERÊNCIAS:
[1] http://www.gilenofilho.com.br/usando-o-scrapy-e-o-rethinkdb-para-capturar-e-armazenar-dados-imobiliarios-parte-i/
[2] Livro Web Scraping com Python - Capítulo 11 - Processamento de imagens e reconhecimento de texto - Pag 207
[3] https://pypi.python.org/pypi/pytesseract

4 comentários:

  1. Esses mano tao hackudo memo ein tnc kkkkkk

    ResponderExcluir
  2. O Scrapy é assíncrono por padrão, não parando os processos para esperar as respostas HTTP. Mas o `requests` é síncrono e faz o processo todo parar até que a requisição HTTP e a resposta HTTP sejam concluídas.

    Uma alternativa mais nativa para o Scrapy seria usar os Image Pipelines para baixar as imagens e depois emendar um novo pipeline para processar o reconhecimento de caracteres (OCR).

    ResponderExcluir
  3. sempre tem uma trilha sonora! :P

    ResponderExcluir