Conrado Saud Programador

Escrevo neste blog, códigos e teorias de programação que desenvolvo e são úteis para mim. Pelo mesmo, espero que seja útil para você. Todo conteúdo aqui é público e gratuito.

contato@conradosaud.com.br

Conheça meu trabalho

Como fazer Web Scraping utilizando Python de forma simples (e poderosa)

Escrito por Conrado Saud

contato@conradosaud.com.br

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


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!

Este artigo lhe ajudou? Você pode retribuir o favor curtindo minha página no Facebook:
Tags: scraping, python, web

Voltar ao início
Publicado em   29/07/2017

Topo

Faça um comentário a respeito deste artigo!