Neste artigo vou explicar a lógica que utilizo para realizar scraping na internet com a linguagem Python, mais especificamente a versão 3.6.1, mas os códigos aqui utilizados
funcionam em qualquer outra versão.
Este será mais um tutorial longo aqui no blog. Nele haverá vários códigos de informações extremamente úteis, além de grandes informativos e ensinamentos.
Por este motivo, recomendo que salve este artigo nos favoritos do seu navegador, pois sempre que precisar relembrar dos códigos aqui passados ou pegar
alguma informação, tendo o mesmo nos favoritos, facilitará seu acesso.
Pré-requisitos
Para continuar a leitura deste artigo e ter um aproveito total de tudo que será passado aqui, recomendo que você possua um conhecimento básico na
linguagem Python e saiba também como manipular strings, textos e listas no Python.
Veja abaixo uma lista especial que preparei para este artigo:
-
Cursos de Python para iniciantes, todos gratuitos:
Recomendo muito o curso de Python do Gustavo Guanabara do canal Curso em Vídeo. Acesse o curso clicando aqui.
Aprendi Python com o Fernando Masanori em seu canal do youtube chamado Python para zumbis. Conheça o curso, clique aqui.
Acompanho também o canal eXcrpit do Cláudio R. Carvalho. Assista as aulas do curso, clique aqui.
-
Manipular strings em Python:
Este artigo irá trabalhar muito com Strings, e é essencial que você saiba manipula-las. Recomendo este link e este outro link para estudo caso precise.
Introdução
Como sempre, começamos o artigo explicando o que é o scraping, como utilizá-lo e porque fazê-lo, assim como suas aplicações e utilidades.
E como eu disse na lista anteriormente, aprendi Python com o mestre Fernando Masanori, então vou utilizar aqui a definição que o mesmo utilizou
na aula de
introdução ao scraping:
Podemos procurar informações na internet, isso também é chamado de scraping. Scraping é uma expressão do inglês que significa "raspar", seu símbolo
é uma espátula. Com isso, nós podemo raspar dados que estão por aí na Internet. Existem muitas aplicações para isso, como por exemplo, se você quer
ver o menor preço de uma passagem aérea, seu programa pode acessar os vários portais de companhias aéreas, e no mesmo dia, para a mesma origem
e destino, fazer uma cotação para ver qual é o menor preço das passagens.
Como é a aplicado?
Uma vez que tratamos com internet, sabemos que há várias e várias formas de se tratar o mesmo assunto. Neste artigo vamos aplicar a forma básica
de scraping na qual você poderá modela-la e finaliza-la da forma que preferir ou precisar.
Basicamente iremos obter uma url e extrair todo seu código fonte html. Exatamente como você faz quando clica com o botão direito em uma página web
e seleciona "inspecionar cóigo fonte". Utilizando o Python, extrairemos todo este código-fonte e vamos manipula-lo através de strings dentro de uma lista.
Aplicação prática
Como uso de exemplo, para este artigo, vou fazer um scraping no site
Shop2gether.
Neste site, irei pegar um catálogo
de roupas e armazenar seus dados como nome, descrição, preço e marca.
Todas as informações obtidas com o scraping você pode armazena-las onde quiser. Neste caso, como este artigo trata-se apenas de exemplos, vou
armazenar as informações destes produtos dentro de um arquivo .txt simples. Lembrando que você pode armazena-las onde quiser, seja em um arquivo
json, ou um arquivo .csv para que mais tarde seja armazenado dentro de uma tabela de um banco de dados.
Um ótimo exemplo de uso do scraping é o site do
Buscapé. O Buscapé é um site que compara preços na internet. Este
é um ótimo exemplo de uso do scraping, onde o usuário digita um nome na pesquisa e o site realiza as funções de scraping em vários sites da internet e
compara preços.
OBS: Não estou afirmando que o site Buscapé utiliza scraping em seus serviços, este é apenas
um exemplo de uso.
Há alguns anos eu fiz uma aplicação em Java onde utilizei também o scraping. Tratava-se de um jogo chamado
Tree of Savior onde eu fiz uma calculadora
em que você inseria alguns dados e o mesmo calculava os pontos que seu personagem ganhava. Através do scraping, eu extrai todas as informações dos
monstros do jogo que estavam no
banco de dados do site
ToS Database e os coloquei em um arquivo .txt que mais tarde passei para meu
banco de dados. Se sentir curiosidade em ver como ficou o programa, você o encontra
neste link.
Fazendo scraping de forma simples
Está pronto para começar? Espero que sim! Nesta parte vou apresentar a maneira mais simples de realizar o scraping. Lembrando que este artigo está
dividido em dois, sendo que o
segundo artigo aborda uma apresentação mais abrangente do scraping.
Como dito anteriormente, o scraping é a extração de informações da internet. E como também explicado em sua aplicação, vamos extrair o código fonte da
página web e manipula-las como string.
Agora, de inicio, ainda
não iremos trabalhar com o site da
Shop2gether. Vamos trabalhar com uma página html especial que eu desenvolvi, pois este arquivo html simples irá facilitar o
entendimento o scraping que utilizaremos.
Acessar Página de Teste
Mãos na massa. Para começar, no Python, é necessário utilizar o recurso
urlopen da biblioteca urllib para que possamos acessar
e pegar as informações do código fonte da página. Veja como utilizar esta biblioteca:
from urllib.request import urlopen
url = "http://blog.conradosaud.com.br/exemplos/scrapingSimples.html"
pagina = urlopen(url)
texto = pagina.read().decode('utf8')
texto = texto.replace("\t", "")
lista = texto.split("\n")
O que fizemos é basicamente atribuir à variável
pagina o conteúdo da url que passamos. Para que possamos ver todo o
conteúdo extraído desta url, é necessário utilizar o comando
read.
Se você executar
print(pagina) conseguirá ver todo o código fonte extraído. Não há problema printar esta
variavel, pois ela foi realizada em um código fonte simples e pequeno, mas caso você fizesse o mesmo em um site onde o código fonte fosse extenso, provavelmente
seu Shell iria travar dependendo da potência do processador do seu computador.
Tendo todo o conteúdo do html dentro de
pagina, podemos passá-lo para uma lista, utilizando o comando
split para separar a variável
pagina por cada
\n, pois se você observou,
quando lemos o código fonte, alguns aspéctos de textos são salvos, entre eles, para cada linha pulada temos a presença do \n. Retira-se também
todos os possíveis \t que são os espaçamentos causados pela tecla Tab.
Trabalhando a lógica e manipulando o html
Agora que temos todo o conteúdo do html, podemos começar a manipular o conteúdo da lista para pegar apenas as informações que queremos.
O que eu quero fazer é encontrar o nome do produto, a marca e os preços. Quando eu tiver todo esse conteúdo de um produto, irei armazena-lo em
um arquivo .txt simples.
Para isso, precisaremos analisar o código fonte e encontrar qual é o padrão de exibição do site. Sempre que um produto é exibido, ele tem algum
elemento no html que indica seu termino e também o início da exibição de um novo produto, e isso sempre é feita de forma padronizada.
Se analisarmos, existem vários elementos que se repetem e identificam a repetição de um produto, como por exemplo, todos os produtos começam
dentro de
<div class="row">, porém, apenas isto não é um identificador muito forte, pois em outras
partes do código também temos essa estrutura. Entretanto, se analisarmos que sempre depois do código anterior, logo em seguida temos
<p class="lead>, que também não é um identificador muito forte, pois em outros lugares deste html também
temos essa estrutura que não se trata do produto, mas em nenhum outro lugar do site temos essas duas divs
em sequencia, logo,
podemos deduzir que
em qualquer lugar do site onde entrarmos essas divs nós temos logo em seguida as informações do produto.
Como passamos isso para o Python?
Outra coisa a se observar, é que caso utilize o comando
len(lista) verás que o mesmo retornará exatamente
o número de linhas que temos no código fonte da página.
Se em cada item da nossa lista temos uma linha do código fonte, basta utilizarmos um loop onde verificamos linha por linha e verificarmos se
a linha atual e a linha seguinte correspondem ao código html que esperamos.
Veja o código:
htmlLinha1 = '<div class="row">'
htmlLinha2 = '<p class="lead">'
contador = 0
while contador < len(lista):
if lista[contador] == htmlLinha1:
if lista[contador+1].startswith(htmlLinha2):
print("Foi encontrado um produto na linha ",contador)
contador+=1
Pronto! Nossa lógica para encontrar produtos está feita.
Neste bloco de código, armazenamos nas váveis
htmlLinha1 e
htmlLinha2 as duas linhas
que, em sequencia, identificam o início de um produto. Fazemos um loop dentro da lista e verificamos se a linha atual corresponde ao
htmlLinha1 e caso seja igual, precisamos verificar se na próxima linha do html irá conter o
htmlLinha2, e caso sim, saberemos que logo em seguida vem o produto que estamos procurando. Perceba que utilizei o
comando
startswith. Existem vários comandos que podemos utilizar para encontrar um caractere ou string dentro de uma
lista ou dentro de outra string. Neste artigo eu usei vários exemplos diferentes. Esta função retorna
true caso a
string passada como parâmetro seja igual ao começo da string que estamos verificando.
Contudo, ainda temos que montar a lógica para encontrar o nome do produto, sua marca e os preços, para assim conseguirmos armazena-los
em um arquivo .txt simples. Vamos entender primeiro como criar, adicionar, alterar e salvar arquivos no Python.
Manipulando arquivos com Python
Esta é a hora de manipularmos arquivos, no caso, vamos trabalhar com arquivos simples .txt para inserirmos neles todos os dados que pegaremos
do scraping. A função que vamos utilizar é a função
open que é bem simples:
open(
nome_do_arquivo.extensao,
modificador)
No primeiro parâmetro inserimos o nome do arquivo e em qual diretório ele está (caso ele não esteja no mesmo diretório que seu arquivo .py que está
sendo executado), sendo que o segundo parâmetro será a forma que iremos trabalhar com o arquivo que está sendo aberto, ou seja, se iremos apenas ler,
escrever nele, se iremos ler e escrever, etc. Há vários tipos de modificadores, você pode acessar
este site que tem um guia completo de como manipular
arquivos com Python e também todos os modificadores possíveis, aqui no artigo iremos abordar este tema de forma bem superficial.
Apenas como uma explicação bem simples, analise o código abaixo:
# manipulando arquivos em python:
arquivo = open('texto.txt', 'w')
texto = "Um texto pequeno."
arquivo.write(texto)
arquivo.close()
arquivo = open('texto.txt', 'r')
texto = arquivo.read()
print(texto)
arquivo.close()
É necessário utilizar a função
close() sempre que terminar de manipular um arquivo. O modificador
w
que utilizei no exemplo apenas escreve no arquivo, o que não inclui alteração, isto é, toda vez que você rodar este código que passei, invés dele
acrescentar no arquivo texto.txt o texto "Um texto pequeno.", ele apenas reescreverá este texto no arquivo inteiro. Para apenas adicionar um novo
conteúdo no arquivo, você pode usar invés do modificador
w, usar o modificador
a, onde toda vez que você chamar a função
write() ele escreverá seu texto sempre ao final do arquivo.
Como dito anteriormente, esta é uma explicação breve sobre a manipulação de arquivos. Caso queira se aprofundar, eu recomendo
este guia escrito, ou se preferir,
este guia em
vídeo. Bons estudos.
Encontrando e armazenando o nome do produto, sua marca e o preço
Anteriormente foi feito o scraping da página por completo e já é possível identificar onde está localizado o produto, contudo, ainda não foi possível
pegar as reais informações do produto, como seu nome, sua marca e também os preços do mesmo. Faremos isso agora.
Vamos conseguir utilizando o último código demonstrado para localizar onde está o produto.
Clique aqui para voltar no código se
for preciso.
Podemos identificar que encontramos um produto sempre que o html
<div class="row">
vier seguido de
<p class="lead">, coincidentemente, justamente na tag p que contem a class lead é a tag
que envolve o nome do produto, então um dos itens já sabemos como capturar.
Sabendo que após a tag p com class lead envolve o nome do produto, podemos realizar o seguinte comando para obter o nome do produto:
while contador < len(lista):
if lista[contador] == htmlLinha1:
if lista[contador+1].startswith(htmlLinha2):
#print(lista[contador+1])
nEncontrado1 = 1 + (lista[contador+1].index(">"))
nEncontrado2 = lista[contador+1].index("</p>")
nomeProduto = lista[contador+1][nEncontrado1:nEncontrado2]
#print(nomeProduto)
contador+=1
Na primeira linha de código comentada você pode executá-la para ver o que o Python está trazendo. Neste print você deve ver a linha completa que
o scraping trouxe, com as tags e tudo mais, agora é necessário filtrá-las.
A variável
nEncontrado1 usa a função
index(string) que retorna em qual posição está a
primeira
string que você passou como parâmetro. Como nesta linha a primeira tag que se fecha é do próprio parágrafo, não precisamos filrar mais do que isto,
mas no caso de uma linha com mais obstáculos (que veremos mais para frente) não faria mal pesquisar pela tag completa no index. Agora com o valor
retornado de onde está o final da tag, eu já adicionei
+1 neste valor, pois queremos apenas o que vem após a tag e não a tag em si.
Na variável
nEncontrado1 já temos o começo do nome do produto, agora iremos colocar na variável
nEncontrado2 onde termina o nome dele. Como o nome do produto está sendo fechado apenas por uma tag
p que finaliza o nome do produto, podemos procurar ela por completo na função index, que retornará o valor contendo o final do nome.
Sabendo onde começa o nome e onde termina fica fácil, basta apenas armazenar em outra variável na qual dei o nome de
nomeProduto o nome do produto usando como delimitador as variáveis
nEncontrado1 que
indica o início do nome e a variável
nEncontrado2 que indica o final do mesmo. Utilize o print para verificar.
Ótimo, a cada loop temos o nome do produto. Agora precisamos encontrar a marca dele.
Novamente, precisamos analisar o padrão do html, e o padrão para que a marca apareça é:
<p><strong>MARCA</strong></p>
Como dito anteriormente, uma maneira fácil de localizar o padrão é encontrando em qual parte a marca aparece, e após isso, verificar quais são
as tags estão circuncidando ele. Para ter certeza de que estas tags em sequência envolvem apenas a marca, é necessário usar os atalhos de
Ctrl+F para pesquisar textos no navegador e confirmar se o padrão está presente apenas onde aparece a marca do produto. Lembrando
que o importante é localizar onde o padrão se inicia, pois uma vez que localizamos o inicio do nome da marca, provavelmente o final do nome da marca
será o fechamento das tags.
Agora que foi feito a captura do nome fica mais fácil de entender como funciona, basta apenas repetir o processo.
Você pode aproveitar o loop do while que localiza o nome produto para localizar a marca, o que economizaria memória do seu computador, ou
fazer em um while separado.
Volto a repetir, para este exemplo simples com poucas linhas de código (apenas 249) é bem fácil para qualquer computador processar e encontrar
os textos citados, pois também são poucas informações (apenas 8 produtos e 32 informações), entretanto, fazer um scraping em um site onde o html
é extenso e você irá fazer vários loops atrás de milhares de informações, isso pode travar sua máquina.
Vou utilizar um while separado para isso. No final do artigo, eu disponibilizo a lógica completa em um só while.
Segue a lógica para pegar a marca do produto:
htmlInicio = '<p><strong>'
htmlFim = '</strong></p>'
while contador < len(lista):
if lista[contador].startswith(htmlInicio):
#print(lista[contador])
nEncontrado1 = len(htmlInicio) + (lista[contador].index(htmlInicio))
nEncontrado2 = lista[contador].index(htmlFim)
nomeMarca = lista[contador][nEncontrado1:nEncontrado2]
#print(nomeMarca)
contador+=1
A lógica é a mesma. Neste while, verificamos se em algum momento aquela linha começa com conteúdo da variável
htmlInicio que contém o padrão que foi identificado anteriormente. O resto é exatamente igual
fizemos com o nome do produto, a única diferença é que para localizar o inicio do nome da marca, eu optei por fazer
nEncontrado1 = len(htmlInicio) + (lista[contador].index(htmlInicio)). O index retornado da variável
nEncontrado1 será zero, porque será a primeira coisa que ele irá encontrar na linha, então é necessário
somar o número de caracteres da variável com o valor encontrado no index. Neste exemplo, como o index retornará zero não é necessário usá-lo,
mas optei por colocá-lo aí como exemplo, pois na maioria das vezes existirá mais coisas que virão antes.
Para terminar, agora é necessário encontrar os preços, e novamente, os preços também seguem um padrão de exibição. No caso, os preços possuem
uma peculiaridade que os tornam muito mais fáceis de serem encontrados do que os itens anteriores, porém mais trabalhoso.
Se observarmos bem no padrão html dos preços,
existem uma classe no CSS chamada
precoVista e uma outra classe chamada
precoParcelado, que servem para
personalizar a exibição dos campos. Essas classes usadas unicamente onde os preços são exibidos, o que facilita mais ainda na hora de encontrar
onde começa a exibição destas informações no scraping, porém essas classes aparecem bem no meio do html e estão na mesma linha, o que dificulta
encontrar onde cada uma delas terminam.
Veja o código:
htmlVista = 'precoVista">'
htmlParcelado = 'precoParcelado">'
while contador < len(lista):
if lista[contador].find(htmlVista) != -1:
#print(lista[contador])
nEncontrado1 = len(htmlVista) + (lista[contador].find(htmlVista))
nEncontrado2 = 0
contadorAux = nEncontrado1
while(contadorAux < len(lista[contador])):
if(lista[contador][contadorAux:contadorAux+1] == "</"):
nEncontrado2 = contadorAux
break
contadorAux += 1
precoVista = lista[contador][nEncontrado1:nEncontrado2]
# ----------------- #
nEncontrado1 = len(htmlParcelado) + (lista[contador].find(htmlParcelado))
nEncontrado2 = 0
contadorAux = nEncontrado1
while(contadorAux < len(lista[contador])):
if(lista[contador][contadorAux:contadorAux+1] == "</"):
nEncontrado2 = contadorAux
break
contadorAux += 1
precoParcelado = lista[contador][nEncontrado1:nEncontrado2]
precoAmbos = "vista: "+precoVista+" | praso: "+precoParcelado
print(precoAmbos)
contador+=1
O código é bem mais extenso, porém ele se repete bastante, nada que uma função não resolva. No final das contas, usamos a mesma lógica
para encontrar o início do preço a vista e do preço parcelado.
Então no começo verificamos se aquela linha retorna algo que não seja
-1, isto é, se ele encontrou o valor desejado. Eu usei
um comentário como divisão para ficar mais fácil de entender. Antes da divisão comentada estamos pegando o valor do preço à vista, e após, o preço
parcelado. Usamos a mesma lógica para encontrar o início do preço, porém como dito, tanto o preço à vista quanto o parcelado estão na mesma linha,
o que torna mais fácil de identificarmos onde estará o fechamento da tag que finaliza o preço que queremos. Para resolver isso, eu fiz um while
que irá rodar a partir de onde encontramos o início do preço à vista na variável
nEncontrado1 até o final da
linha, e assim que encontrarmos o primeiro "</", que é o início de onde a tag está sendo fechada, e consequentemente é o final do preço,
armazenamos o valor do contador na variável
nEncontrado2 para cortarmos a string e salvar na variável
precoVista.
Após a divisão do comentário é feito exatamente a mesma coisa, mas procurando o preço parcelado, e então temos as duas strings juntas.
Finalizando o arquivo Python
Então agora que foi passado como é realizado a lógica em whiles diferentes para localizar e pegar todas as informações que precisamos, já podemos
juntá-los em um while só para fechar esta parte do artigo.
Também é importante pensarmos em como será formatada a string final com as informações completas de um produto. Para isso é necessário decidir
o que será feito com essas informações.
Como desde o início do artigo eu venho comentando como exemplo o armazenamento das informações do scraping em um banco de dados, eu vou formatar
todas as informações dos produtos em formato CSV, para que mais tarde possa ser importado em qualquer tabela de um banco.
Para quem não sabe, o padrão CSV é o padrão .csv que alguns programas usam para ler arquivos separados por colunas e linhas, como por exemplo o
Excel e o Calc do LibreOffice. Várias outras ferramentas usam este padrão para o mesmo fim, como por exemplo, exportar e importar tabelas no
MySQL, PostgreSQL etc. Veja um exemplo de padrão CSV:
Nome do produto; marca do produto; preço a vista; preço a prazo;
Alguns arquivos CSV podem ser divididos com
; ou com
, isso não importa muito, pois na hora de abrí-los no Excel
ou importá-los para o banco você pode definir o separador de colunas que quiser.
Mostrar/esconder código completo
#importa a funcao de acessar e ler o html da pagina
from urllib.request import urlopen
url = "http://blog.conradosaud.com.br/exemplos/scrapingSimples.html"
pagina = urlopen(url)
texto = pagina.read().decode('utf8')
#remove os espacamentos da tecla TAB
texto = texto.replace("\t", "")
#remove a quebra de linha
lista = texto.split("\n")
#cria variaveis globais para criar csv
nomeProduto = ""
nomeMarca = ""
precoVista = ""
precoParcelado = ""
csv = []
#padrao para encontrar o nome do produto
htmlNomeProduto1 = '<div class="row">'
htmlNomeProduto2 = '<p class="lead">'
#padrao para encontrar o nome da marca
htmlInicioMarca = '<p><strong>'
htmlFimMarca = '</strong></p>'
#padrao para encontrar os precos
htmlVista = 'precoVista">'
htmlParcelado = 'precoParcelado">'
contador = 0
while contador < len(lista):
#encontra nome do produto
if lista[contador] == htmlNomeProduto1:
if lista[contador+1].startswith(htmlNomeProduto2):
nEncontrado1 = 1 + (lista[contador+1].index(">"))
nEncontrado2 = lista[contador+1].index("</p>")
nomeProduto = lista[contador+1][nEncontrado1:nEncontrado2]
#encontra nome da marca
if lista[contador].startswith(htmlInicioMarca):
nEncontrado1 = len(htmlInicioMarca) + (lista[contador].index(htmlInicioMarca))
nEncontrado2 = lista[contador].index(htmlFimMarca)
nomeMarca = lista[contador][nEncontrado1:nEncontrado2]
#encontra precos
if lista[contador].find(htmlVista) != -1:
#preco a vista
nEncontrado1 = len(htmlVista) + (lista[contador].find(htmlVista))
nEncontrado2 = 0
contadorAux = nEncontrado1
while(contadorAux < len(lista[contador])):
if(lista[contador][contadorAux:contadorAux+1] == "<"):
nEncontrado2 = contadorAux
break
contadorAux += 1
precoVista = lista[contador][nEncontrado1:nEncontrado2]
#preco parcelado
nEncontrado1 = len(htmlParcelado) + (lista[contador].find(htmlParcelado))
nEncontrado2 = 0
contadorAux = nEncontrado1
while(contadorAux < len(lista[contador])):
if(lista[contador][contadorAux:contadorAux+1] == "<"):
nEncontrado2 = contadorAux
break
contadorAux += 1
precoParcelado = lista[contador][nEncontrado1:nEncontrado2]
#verifica se todas variaveis foram preenchidas para adicionar ao csv
if len(nomeProduto) > 1 and len(nomeMarca) > 1 and len(precoVista) > 1 and len(precoParcelado) > 1:
csvAux = nomeProduto+";"+nomeMarca+";"+precoVista+";"+precoParcelado+";"
csv.append(csvAux)
#zera variaveis para que a proxima verificacao seja valida
nomeProduto = ""
nomeMarca = ""
precoVista = ""
precoParcelado = ""
contador+=1
#csv criado com sucesso!
for item in csv:
print(item)
Este foi o conceito básico de scraping que a partir dele você consegue ter uma boa base de como realizar o mesmo na internet. É através dessa lógica
aqui apresentada que eu realizo meus serviços de scraping, e nunca tive muitos problemas para realizá-los. Obviamente, existem várias outras formas de
se realizar o scraping, esta foi a que eu desenvolvi para adequar as minhas exigências.
Eu realmente espero que você tenha gostado deste artigo, e embreve eu trarei uma "segunda parte" deste artigo, onde faremos o scraping no site Shop2gether
aplicando um exemplo real de uso.
Salve o blog nos favoritos do seu navegador, assim você poderá visitá-lo toda semana para ver os novos artigos que
são publicados aqui.
Nos últimos artigos eu tenho recebido poucos comentários a respeito da opinião de você, leitor, o que me deixa preocupado com o conteúdo que é apresentado aqui,
por isso, me ajudaria bastante dizer o que você achou deste artigo utilizando a caixa de comentários do Facebook abaixo. É rápido! Você não precisa
criar nenhuma conta para comentar, basta estar online no Facebook! Eu agradeceria bastante.
Obrigado pela leitura!