Web Scraping com Python



Web Scraping

Muitas vezes nos deparamos com a necessidade de obter informações de sites, sem conseguirmos acessar aos seus respectivos bancos de dados, o que pode ser um enorme problema, inclusive invalidar todo um projeto.
É para que tal fato não ocorre que existe o Web Scraping! 
Com poucas linhas de código e com uma inspeção minuciosa no site, conseguimos obter os tão desejados dados.
A linguagem de programação Python é largamente utilizada na comunidade de data science, e, portanto, tem um ecossistema de módulos e ferramentas que você pode usar em seus próprios projetos. Neste caso estaremos utilizando o pacote Beautiful Soup.

O algoritmo utilizado será descrito de forma separada e detalhada para um melhor entendimento de cada passo, e no final do tutorial será apresentado totalmente unificado e pronto para executar.

Python e o Beautiful Soup

É uma biblioteca Python que permite um retorno rápido em projetos de Web Scraping.
Biblioteca compativel com a versão 2.7 e 3 do Python, sua função é a criação de uma arvore a analise a partir de documentos HTML e XML, incluindo documentos com tags não fechadas.

Pré Requisitos

Recomendado a utilização de Python 3, e deve ter instaladas a bibliotecas "Requests" e "Beautiful Soup".

Conhecendo os Dados

O exemplo utilizado é comum no mundo Acadêmico, sendo utilizado dados do site oficial do National Gallery of Art nos Estados Unidos. O National Gallery é um museu de arte localizado no National Mall em Washington, D.C. Ele possui mais de 120.000 peças datadas desde o Renascimento aos dias atuais feitas por mais de 13.000 artistas.

O objetivo é de pesquisar o Índice de Artistas, que, no momento da atualização deste tutorial, estava disponível via Internet Archive’s Wayback Machine na seguinte URL:



A visão da página será esta:




Devido á dimensão dos dados que todo o site contém, e visto que o objetivo deste artigo é orientar nossos leitores no uso de Web Scraping, não existe a necessidade de obter todos os dados da pagina.
Assim sendo iremos limitar o escopo dos dados aos artistas cuja letra inicial do seu nome é Z, tal como pode ser verificado no link: 


Acessando ao mesmo, poderemos ter a seguinte visão:


É importante ter em atenção que na página não são listados todos os artistas, podemos verificar que no final da página um botão para mudar de página. No total existem 4 páginas com artistas de inicial Z, e após navegarmos para a última página obtemos o seguinte link:


Salientamos a atenção para este fato, pois mais adiante, iremos efetuar uma iteração de todas as 4 páginas de forma a obtermos completos de todas as páginas.

Para que possa entender melhor como uma página web é configurada, pode dar uma olhada no DOM ( Document Object Model) onde poderá ter um melhor esclarecimento de como é estruturado um HTML.

As Bibliotecas

No inicio de cada projeto, um passo rotineiro mas de suma importância é a importação das bibliotecas, neste caso iremos utilizar inicialmente a "Resquests" e " Beautiful Soup".

A biblioteca Requests lhe permite fazer uso do HTTP dentro dos seus programas Python em um formato legível, e o módulo Beautiful Soup é projetado para fazer web scraping rapidamente.

Vamos importar tanto o Requests quanto o Beautiful Soup com a declaração import. Para o Beautiful Soup iremos importá-lo do bs4, o pacote no qual o Beautiful Soup 4 é encontrado

# Importação de Bibliotecas
import requests
from bs4 import BeautifulSoup
Após a importação das bibliotecas, estamos prontos para coletar e analisar uma pagina Web

Coleta e análise da pagina Web

Inicialmente necessitamos de coletar a URL da primeira página web usando o "Requests", criamos uma variável de nome "pagina" utilizando o método requests.get()

# Efetuando a coleta de dados da primeira pagina dos artistas com inicial Z
page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

Em seguida, criamos um objeto (árvore de análise) BeautifulSoup, passando para o mesmo o argumento "pagina.text" do Requests que por sua vez efetua a análise do html.parser interno do Python

# Criação do objeto BeautifulSoup
sopa = BeautifulSoup(pagina.text, 'html.parser')

Agora com a página analisada e coletada. podemos iniciar a criação da parte do algoritmo que irá extrair os dados necessários.

Extração de texto da pagina Web

Para nosso exemplo, iremos coletar somente os nomes dos artistas da página web, porém inúmeras informações poderiam ser coletadas, desde que disponíveis na página. Para quaisquer dados que você queira coletar, você precisa descobrir como ele é descrito pelo DOM da página web.

Para tal, clique com o botão do lado direito do seu mouse em cima do primeiro nome da lista, e após isso selecione a opção Inspecionar Elemento. Com essa ação as opções de desenvolvedor web do seu navegador serão habilitadas o que permitirá procurar pela classe e as tags associadas aos nomes dos artistas nessa lista.


Veremos primeiro que a tabela de nomes está dentro de tags <div> onde class="BodyText". É importante observar isso, para que só procuremos texto nessa seção da página web. Também notamos que o nome Zabaglia, Niccola está em uma tag de link, já que o nome faz referência a uma página web que descreve o artista. Então, vamos querer referenciar a tag <a> para links. O nome de cada artista é uma referência a um link.

Para fazer isso, iremos utilizar os métodos find() e find_all() do Beautiful Soup a fim de extrair o texto dos nomes dos artistas do BodyText <div>.


# Obter todo o texto da div BodyText
lista_nomes = sopa.find(class_='BodyText')
# Obter o texto de todas as instâncias da tag <a> dentro da div BodyText
lista_nomes_items = lista_nomes.find_all('a')
De forma a iterar todos os nomes de artistas a  seguir, na parte inferior do nosso arquivo de programa, criaremos um loop for para iterar todos os nomes de artistas que acabamos de colocar na variável lista_nomes_items.

Vamos imprimir esses nomes com o método prettify() para transformar a árvore de análise do Beautiful Soup em uma string Unicode bem formatada.

# Criar loop para imprimir todos os nomes de artistas
for nome_artista in lista_nomes_items:
print(nome_artista.prettify())
Se executarmos o algoritmo até agora obteremos o seguinte output:

Output
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630">
Zabaglia, Niccola
</a>...
Zao Wou-Ki
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427">
</a>
<a href="/web/20121007172955/https://www.nga.gov/collection/anZ3.htm">
<a href="/web/20121007172955/https://www.nga.gov/collection/anZ2.htm">
Zas-Zie </a> Zie-Zor </a>
</a>
<a href="/web/20121007172955/https://www.nga.gov/collection/anZ4.htm">
<strong> next <br/> page
</strong>

Podemos reparar que existe informação que não queremos, pois o objetivo é obter os nomes. Atualmente temos um texto completo dentro de tags encontradas na <dis class="BodyText"> da primeira página. Para obtermos dados mais limpos, temos de proceder á remoção de dados não desejados.

Higienização dos Dados

Um passo importante já foi dados, a coleta de dados de texto do link dentro da secção <div> da página web. Porém não queremos ter os links inferiores que não fazem parte da referência aos nomes dos artistas. Assim sendo, uma higienização de dados tem de ser efetuada.

Para remover os links inferiores da página, necessitamos de inspecionar novamente a pagina para ver que os links na parte inferior da secção <div class="BodyText"> estão contidos em uma tabela HTML: <table class="AlphaNav">.



Podemos, portanto, usar o Beautiful Soup para encontrar a classe AlphaNav e usar o método decompose() para remover uma tag da árvore de análise e depois destruí-la juntamente com seu conteúdo.

Usaremos a variável last_links para fazer referência a esses links inferiores e adicioná-los ao arquivo do programa:

# Remover links inferiores
last_links = soup.find(class_='AlphaNav')
last_links.decompose()
Se executarmos o algoritmo agora, obteremos o seguinte output:

Output
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630">
Zabaglia, Niccola
</a>
Zaccone, Fabian
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202">
</a> ...
</a>
<a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11631">
Zanotti, Giampietro
</a>
<a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3427">
Zao Wou-Ki

Agora podemos verificar que a saída de dados, não inclui mais os links da parte inferior da página web e exibe os links associados a cada nome do artista

Porém, ainda temos os dados das tags, que não necessitamos para nossa análise, é preciso que a higienização de dados continue e seja mais aprimorada.

Obtendo o conteúdo de uma Tag


Para podermos acessar somente aos nomes reais dos artistas é necessário focar no conteúdo das tags <a> ao invés de imprimir toda a tag de link. Fazemos isso, usando o ".contents" do BeautifulSoap, que por sua vez retornará a tag filha com um tipo de dados para Python.

Revisando o loop For, podemos incluir no mesmo a impressão da lista de tags filhas (ou seja o nome completo dos artistas).

# Utilizando o .contents para obter as tags <a> filhas
for artist_name in artist_name_list_items:
nomes = nome_artista.contents[0]
print(nomes)
Reparem que está sendo iterada a lista acima, efetuando uma camada do número de índice de cada item. Agora se executarmos o algoritmo, obtemos o seguinte output:

Output
Zabaglia, Niccola
Zaccone, Fabian
...
Zadkine, Ossip
Zanotti, Giampietro
Zanini-Viola, Giuseppe
Zao Wou-Ki

Recebemos de volta uma lista de todos os nomes dos artistas disponíveis na primeira página da letra Z.

Porém, se quisermos obter também as URLs associadas a cada artista, podemos extrair as mesmas encontradas dentro de tags <a> utilizando o método "get('href') do BeautifulSoup.

Através do output dos links acima, conseguimos saber que a URL inteira não está sendo capturada, dessa forma, necessitamos concatenar a string do link com o início da string da URL (neste caso https://web.archive.org/).

Assim sendo necessitamos adicionar estas linhas ao loop For:

...
for nome_artista in lista_nomes_items:
nomes = nome_artista.contents[0]
links = 'https://web.archive.org' + nome_artista.get('href')
print(nomes)
print(links)

Quando executarmos o algoritmo, receberemos tanto os nomes dos artistas quanto as URLs para os links que nos dizem mais sobre eles:

Output
Zabaglia, Niccola
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630
Zaccone, Fabian
...
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11631
Zanotti, Giampietro
Zao Wou-Ki
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427

Embora estejamos a receber informações do site, essas informações estão sendo impressas, somente em variáveis no nosso terminal, ficando somente disponíveis na memoria do computador.
Assim sendo necessitamos de gravar esses dados em um arquivos, de forma a podermos guardar e utilizar os mesmos quando necessário.


Armazenando dados em arquivo CSV


De nada serve obter dados e manter os mesmo na memoria do sistema através do uso de variáveis, pois uma vez encerrada a execução do algoritmo, os dados são perdidos.
Dessa forma, é extremamente útil a exportação desses dados em um arquivo, neste caso usaremos o formato ".csv", que nos permite armazenar dados tabulares em um arquivo de texto plano, além de que é um formato comumente usado para planilhas e bancos de dados.

Mas para podermos efetuar essa exportação, devemos antes de tudo, importar a biblioteca "csv" do Python. Essa chamada da biblioteca deve ficar junto com a secção de código inicial onde são importadas as outras bibliotecas.
import csv

Após a importação da biblioteca "csv", vamos criar e abrir um arquivo chamado "resultado_web_scraping" para que possa ser gravado nele os dados obtidos pelo algoritmo (iremos usar a variável "f" para o arquivo) e utilizando o modo "w". OS cabeçalhos da primeira linha podem ser escritos : Nome e Link que iremos passar para o método "writerow()" como uma lista.

f = csv.writer(open('resultado_web_scraping.csv', 'w'))
f.writerow(['Nome', 'Link'])
Por fim, dentro do loop For, anteriormente criado, vamos escrever cada linha com nomes dos artistas e seus links associados:

f.writerow([nomes, links])
Quando o algoritmo for executado, nenhuma saída será retornada para sua janela de terminal. Em vez disso, um arquivo será criado no diretório em que você está trabalhando, chamado resultado_web_scraping.csv.

Dependendo do que você usa para abri-lo, ele deve ser algo assim:

Nome,Link
"Zabaglia, Niccola",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11630
"Zaccone, Fabian",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=34202
...
"Zadkine, Ossip",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3475w
Agora o arquivo pode ser usado para trabalhar com dados de maneiras mais significativas, uma vez que as informações coletadas estão armazenadas no disco do computador.


Recuperação de Paginas Relacionadas

Até agora criamos um algoritmo que extraíra dados da primeira página da lista de artistas, cujos sobrenomes começão com a letra Z. Porém existem mais 4 páginas além dessa inicial contendo artistas com letra Z e que estão disponíveis no site.

Para conseguirmos coletar essas paginas, necessitamos executar mais iterações com loops For. Isso irá revisar a maior parte do código que escrevemos até agora, mas certamente que serão empregados conceitos semelhantes.

Para iniciar, vamos criar uma lista para armazenar as paginas:

paginas = []
Vamos preencher essa lista inicializada com o seguinte loop For:

for i in range(1, 5):
url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
paginas.append(url)
Anteriormente foi observado que devemos prestar máxima atenção ao número total de páginas que contém nomes de artistas começados com a letra Z (ou qualquer outra referência que estiver sendo utilizada). Visto existirem 4 páginas para a letra Z, o loop For, acima criado, com um intervalo de de 1 a 5, para que ele vá iterar através de cada uma das 4 páginas.


PAra este site especificamente, as URLs começam com a string "https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZe" e são seguidas com um número de página (que será o inteiro "i" do loop For que será convertido para uma string) e terminam com ".htm". Dessa forma iremos concatenar essas strings e depois acrescentar o resultado á lista "paginas".

Para além desse loop, é necessário haver um segundo loop que passará por cada uma das páginas acima. Esse loop será semelhante com o que criamos até agora, uma vez que está executando a tarefa que completamos para a primeira página dos artistas com a letra Z para cada uma das 4 páginas do total. Repare que, como colocamos o algoritmo original no segundo loop For, agora temos o loop original com um loop aninhado contido nele.

Os dois loops ficaram assim:

paginas = []
for i in range(1, 5):
url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
paginas.append(url)
for item in pages:
pagina = requests.get(item)
sopa = BeautifulSoup(pagina.text, 'html.parser')
last_links = soup.find(class_='AlphaNav')
last_links.decompose()
lista_nomes = sopa.find(class_='BodyText')
lista_nomes_items = lista_nomes.find_all('a')
for nome_artista in lista_nomes_items:
nomes = nome_artista.contents[0]
links = 'https://web.archive.org' + nome_artista.get('href')
f.writerow([nomes, links])


No código acima, você deve ver que o primeiro loop for está iterando nas páginas e o segundo loop for está extraindo dados de cada uma dessas páginas e, em seguida, adicionando os nomes e links dos artistas, linha por linha, em cada linha de cada página.

Estes dois loops For estão abaixo das declarações "import", da criação e escrita do arquivo CSV (com a linha para a escrita dos cabeçalhos do arquivo), e a inicialização da variável "paginas" (atribuída a uma lista).

Dentro de um contexto macro do arquivo de programação, o código completo se parece com isto:


import requests
import csv
from bs4 import BeautifulSoup

f = csv.writer(open('resultado_web_scraping.csv', 'w'))
f.writerow(['Nome', 'Link'])

paginas = []

for i in range(1, 5):
url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
paginas.append(url)

for item in paginas:
pagina = requests.get(item)
sopa = BeautifulSoup(pagina.text, 'html.parser')

last_links = sopa.find(class_='AlphaNav')
last_links.decompose()

lista_nomes = sopa.find(class_='BodyText')
lista_nomes_items = lista_nomes.find_all('a')

for nome_artista in lista_nomes_items:
nomes = nome_artista.contents[0]
links = 'https://web.archive.org' + nome_artista.get('href')

f.writerow([nomes, links])


Cuidados a considerar

Ao fazer scraping em páginas web, é importante manter-se cuidadoso com os servidores dos quais você está pegando informações.

Verifique se o site tem termos de serviço ou termos de uso relacionados ao web scraping. Além disso, verifique se o site tem uma API que permite coletar dados antes de você mesmo fazer scraping.

Certifique-se de não acessar continuamente os servidores para coletar dados. Depois de coletar o que você precisa de um site, execute scripts que vasculhem pelos dados localmente, em vez de sobrecarregar os servidores de outra pessoa.


Desejamos que este projeto acadêmico seja útil para nossos leitores, permitindo que os conceitos e técnicas utilizadas, possam ser empregadas nos nossos projetos.

Muito obrigado e bons estudos!







O código completo pode ser acessado no Git Hub: https://github.com/NelsonZyon/Data-Science/blob/master/Web_Scraping_Python



Referências:

How To Work with Web Data Using Requests and Beautiful Soup with Python 3 - https://www.digitalocean.com/community/tutorials/introduction-to-the-dom

Comentários

Postagens mais visitadas deste blog

Tutorial de Machine Learning com Python - Iniciantes - Parte 1

Regressão Múltipla com R

Tutorial de Machine Learning com Python - Iniciantes - Parte 2