Mostrando postagens com marcador scrapy. Mostrar todas as postagens
Mostrando postagens com marcador scrapy. Mostrar todas as postagens

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

quarta-feira, 2 de dezembro de 2015

Gerando wordlist de username com python e scrapy

Fala galera suave?
Faz um tempo que não posto nada não é verdade?

Bom, agora estou de "férias"(sem trampo rsrsrs) e vou tentar me dedicar mais ao blog, pra falar a verdade, tenho 2 artigos para finalizar, a preguiça e o Elder Scrolls não estão deixando kkkk

Um deles eu quero passar o básico para se examinar um aplicativo mobile, desde o tráfego de dados até o source code, com foco em apk(Android), algo bem howto mesmo, eu penei um pouco pra achar bons tutos que explicam desde o decompilar até o recompilar com assinatura e tal, enfim.
Outro que quero mostrar é algo que estou criando com o scrapy, mesmo framework que vou demonstrar nesse artigo, enfim, vamos ao que interessa certo?

Agora na madruga eu estava fazendo um pentest e precisava de nomes de usuários para fazer uma wordlist para enumerar um ssh, porém, queria nomes de pessoas do BR, a solução que achei foi procurar no google por nomes de pessoas brasileiras, então eu achei esse site: http://www.dicionariodenomesproprios.com.br/

Certo, nele existe uma lista com nomes masculinos e nomes femininos, o que vou mostrar é como automatizar isso para que se pegue todos os nomes do site de forma rápida e fácil.

Primeiro, caso não tenha instalado o python e/ou scrapy, leia sobre aqui.

Caso já tenha instalado, prosseguiremos. (Obs: não pretendo explicar o que cada comando faz nesse tutorial, estou preparando outro para isso, no final do artigo vou colocar uns posts que me ajudaram a aprender sobre o scrapy caso alguém queira)

Em seu terminal, digite:

~$ scrapy startproject nomes_galera

Será criada uma pasta com o nome de "nomes_galera", agora digite a sequencia de comandos abaixo:

~$ cd nomes_galera
~$ scrapy genspider masc_nome dicionariodenomesproprios.com.br

Vejamos, você criou o projeto dando um start nele e atribuindo o nome "nomes_galera", em seguida você entrou na pasta e criou um robo(spider) que vai coletar os dados a partir do domínio dicionariodenomesproprios.com.br com o nome do robo(spider) de masc_nome.

Certo, espero que até aqui você tenha entendido, caso não, ligue 190 e peça ajuda aos universitários rsrsrs briks, qualquer coisa deixa nos comentários que eu ajudo galera.

Vamos entrar na pasta onde se encontra o spider, antes de entrar, segue a árvore do diretório só para terem uma noção.

#:> tree
.
├── nome_galera
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   ├── settings.pyc
│   └── spiders
│       ├── __init__.py
│       ├── __init__.pyc
│       ├── masc_nome.py
│       └── masc_nome.pyc
└── scrapy.cfg

Entre na pasta spiders com o comando abaixo:

~$ cd nome_galera/spiders/

Vamos editar o arquivo que acabamos de criar, no caso, o robo com o nome de masc_nome.py

A princípio o arquivo é criado com um layout padrão, dessa maneira:


Note que na linha 9 ele mostra apenas o site certo? Não é isso que queremos, vamos analisar o site e ver onde estão os nomes masculinos.

Se você entrar nesse link: http://www.dicionariodenomesproprios.com.br/nomes-masculinos/
Vai ver que tem apenas nomes masculinos certo? Nosso desafio é pegar todos esses nomes e de todas as paginas. (Desculpem se não estiver sendo bem claro, estou com um pouco de sono)

Pra fazer isso, vamos olhar o código fonte da pagina.
Abaixo do Top10 de nomes, veja que começa a lista de nomes do site, em desordem alfabética(acho que inventei esse termo agora) o primeiro nome que aparece é o Davi, usando o chrome, abra a ferramenta de desenvolvedor e inspecione o objeto com o nome do Davi assim como na imagem abaixo:



Vejam a tag e o atributo do HTML onde tem o nome do davi, viu?

Ok, vou colocar aqui.

Repare, temos a tag "a" com o atributo 'class="lista-nome"', se você der um procurar na pagina, você vai ver que todos os nomes listados tem a tag e o atributo em comum, se pensarmos de maneira lógica, temos uma lista de nomes(Dãhr, o próprio nome da class já diz animal). Piadinhas a parte, muitas vezes, por mais obvia que é a questão, deixamos passar na hora de desenvolver, pensamos de forma automática e não reparamos em coisas obvias que nos ajudam a montar mentalmente o código.

Ok, let's go.

Vamos ao código agora.

Leia o código, não continue a ler esse texto, vamos fazer um teste de observação.

Diga mentalmente qual é a URL que o robô vai dar o start na busca.

.
.
.
.
.
.
.
.

Achou?
Certo, muito bom garoto(a), linha 8 do código ali de cima, certo? Que por sua vez, é o código base do robo, agora vamos mudar isso, afinal, começamos a procurar do link que passei sobre nomes masculinos não é mesmo? Ainda lembra dele? Pega ele ali em cima vai, não vou escrever de novo.

Altere a linha 9 com o link da pagina onde contem os nomes masculinos.

ficando dessa maneira:



Agora vamos a parte que interessa, vamos ao parser do "bagulho", vamos coletar os nomes.
Primeiro, vamos entender um pouco o que vamos fazer, lembra-se da tag onde contem o nome do Davi? Vamos fazer ela de base para pegar todos os nomes, vamos usar xpath para gerar uma query generica.

Vou postar o código aqui já pronto e vou explicar as linhas alteradas no próprio source "serto" galera?
[ATUALIZACAO] Galera, recebi uma dica do Lynneker Sales Santos sobre o fechamento do arquivo, eu estava abrindo e fechando no for, eu troquei isso. Vlw mano





Agora é só fazer isso para a pagina de nomes de mulheres e pronto, faça isso você mesmo para ver se entendeu, qualquer dúvida, postem aqui nos comentários que ajudo.

Aqui vai uns posts que me ajudaram com scrapy e que me tiram dúvidas até hoje.

http://www.gilenofilho.com.br/usando-o-scrapy-e-o-rethinkdb-para-capturar-e-armazenar-dados-imobiliarios-parte-i/

http://www.gilenofilho.com.br/usando-o-scrapy-e-o-rethinkdb-para-capturar-e-armazenar-dados-imobiliarios-parte-ii/


Post a base de CBJR