Wymored Login

Curso de web scraping em python 3 - Parte 3 - capturar os dados

31 de janeiro de 2019 por Alexandre Miguel de Andrade Souza

Como já preparamos o ambiente na parte 1 e identificamos os dados que queremos na parte2, agora começamos a entrar na parte de como capturar os dados com python.

Crie um arquivo scraper.py e insira o texto abaixo:

#!./venv/bin/python
import requests
from bs4 import BeautifulSoup as bs

Requests é uma biblioteca elegante e simples para requisições HTTP, 'contruída para seres humanos'.

Do site do Beautiful Soup:

Você não escreveu aquela página horrível. Você está apenas tentando obter alguns dados. Beautiful Soup está aqui para ajudar. Desde 2004, vem economizando horas ou dias de programadores em projetos de captura rápida de dados de sites.

Até aqui estamos apenas importando as bibliotecas do python para trabalharmos. Agora vamos usar alguns comandos.

#lemos a página usando a biblioteca requests
url = 'https://www.tripadvisor.com.br/Attraction_Review-g2441398-d4297489-Reviews-Parque_Nacional_Serra_do_Cipo-Serra_do_Cipo_Santana_do_Riacho_State_of_Minas_Ger.html'
pagina = requests.get(url)
#soup é a página tratada pelo Beautiful Soup
soup = bs(page.content, 'html.parser')
#mandamos imprimir no terminal a página formatada pelo bs
print(sopa.prettify())

Vamos executar o arquivo e ver o resultado:

python scraper.py

O código html da página será impresso no terminal. Você pode ver o código da página no navegador clicando com o botão direito e então 'Ver código-fonte' Se o código fonte for impresso, então estamos acessando corretamente.

A documentação do Beautiful Soup está aqui

Agora que já tivemos uma prévia de como capturar as informações com o bs4 e requests, vamos usar um banco de dados armazenar as informações

4) Armazenar os dados

Crie um arquivo db.py e cole o conteúdo abaixo:

from pydal import DAL, Field

db = DAL("sqlite://tripadvisor.db",
        fake_migrate_all=True,
        migrate=True,
        )

db.define_table('atrativo',
    Field('url', 'text'),
    Field('nome', 'text'),
    Field('avaliacoes', 'integer'),
    Field('quando', 'datetime'),
)




db.define_table('avaliacao',
    Field('atrativo', 'integer'),
    Field('membro', 'integer'),
    Field('nota','integer'),
    Field('titulo','text'),
    Field('avaliacao','text'),
    Field('quando', 'date'),
)

db.define_table('membro',
    Field('membro', 'text'),
    Field('nome', 'text'),
    Field('cidade', 'text'),
    Field('cidade1', 'text'),
    Field('estado', 'text'),
    Field('pais', 'text'),
    Field('since', 'text'),
    Field('data_cadastro', 'text'),
    Field('sexo', 'text'),
    Field('faixa_etaria', 'text'),
    Field('votos_uteis', 'integer'),
    Field('pontos', 'integer'),
    Field('nivel', 'integer'),
    Field('avaliacoes', 'integer'),
    Field('tags', 'text')
)

Agora crie um arquivo chamado tripadvisor.py e cole o conteúdo abaixo:

# -*- coding: utf-8 -*-
#executar com python3

import requests
from bs4 import BeautifulSoup as bs
import math
import datetime

import locale
locale.setlocale(locale.LC_ALL, 'pt_BR.utf8')
from slugify import slugify
last_update = '2018-05-30'
from db import db


def get_url(url):
    import time
    try:

        time.sleep(2) #para ser gentil, não sobrecarregando o servidor, 2 é o número de segundos entre uma chamada e outra
        proxies = {'http': 'http://usuario:senha@servidor:porta/'}
        return requests.get(url,proxies=proxies)
    except:
        time.sleep(300)
        get_url(url)




def atrativos():
    from datetime import  datetime
    rows = db(db.atrativo.id > 0).select(orderby=~db.atrativo.id)

    print('{} atrativos'.format(len(rows)))
    for row in rows:
        atrativo = row['id']
        url = row['url']
        page = get_url(url)
        soup = bs(page.content, 'html.parser')
        avaliacoes = soup.find(class_="reviews_header_count").get_text().replace('.', '').replace('(', '').replace(')',
                                                                                                                '')
        print(avaliacoes,"avaliações")
        pages = int(math.ceil(int(avaliacoes) / 10))
        print(pages,"páginas")
        usuarios(url, atrativo)
        next_page(url, atrativo, pages)
        #membros()
        row.update_record(avaliacoes=avaliacoes,quando=datetime.utcnow)
        db.commit()


def avalia(atrativo, membro, titulo, nota, avaliacao, quando):

    row = db((db.avaliacao.atrativo==atrativo)
            & (db.avaliacao.membro==membro)
            & (db.avaliacao.quando == quando)
            ).select().first()
    if not row:

        db.avaliacao.insert(atrativo=atrativo,membro=membro,quando=quando,
                            titulo=titulo,nota=nota,avaliacao=avaliacao)
        db.commit()

def usuarios(url, atrativo):
    page = get_url(url)
    soup = bs(page.content, 'html.parser')
    users = soup.find_all(class_='review-container')
    for usuario in users:
        #user_data = usuario.find('hsx_review')
        u = usuario.find(class_='info_text')
        nome = u.find('div').get_text()
        print('---------')
        print(nome)
        q = usuario.find(class_='ratingDate').get('title')
        print(q)
        q = datetime.datetime.strptime(q, '%d de %B de %Y')

        nota = usuario.find(class_='ui_bubble_rating')['class'][1][7]
        #.span['class'][1][7]
        print(nota)
        avaliacao_usuario = usuario.find(class_='partial_entry')
        mais = avaliacao_usuario.find('span')
        t = usuario.find(class_='quote')
        titulo_avaliacao = t.find(class_='noQuotes').get_text()
        link = t.find('a').get('href')
        if mais:

            url = "https://www.tripadvisor.com.br/"+link
            page = get_url(url)
            soupb = bs(page.content, 'html.parser')
            avaliacao_texto = soupb.find(class_='partial_entry').get_text()

        else:
            avaliacao_texto = avaliacao_usuario.get_text()

        quando = q.strftime('%Y-%m-%d')


        try:
            cidade = u.find(class_='userLoc').get_text().replace(', ', '-').strip()
        except:
            cidade = ''
        try:
            b = usuario.find_all(class_='badgetext')
            avaliacoes = b[0].get_text()
            try:
                uteis = b[1].get_text()
            except:
                uteis = 0
        except:
            avaliacoes = 0
            uteis = 0
        cidade = slugify(cidade, separator=" ")
        print('usuario:', nome, 'cidade:', cidade, avaliacoes, uteis)
        print(q, nota, titulo_avaliacao, avaliacao_texto)

        if not ' ' in nome:
            row = db(db.membro.membro == nome).select().first()
            if not row:
                print('sem registros')
                membro = db.membro.insert(membro=nome,nome=nome,cidade=cidade,avaliacoes=avaliacoes,uteis=uteis)

            else:
                membro = row.id
                print('já tem')

        else:

            row = db((db.membro.nome == nome)
                    & (db.membro.cidade==cidade)).select().first()

            # print(row[0])
            if not row:
                print('sem registros')
                membro = db.membro.insert(nome=nome, cidade=cidade, avaliacoes=avaliacoes, uteis=uteis)

            else:
                membro = row.id
                print('já tem')
        print('atrativo:',atrativo,'membro=',membro,'quando=',quando,
                            'titulo=',titulo_avaliacao,'nota=',nota,'avaliacao=',avaliacao_texto)
        if quando > last_update:
            avalia(atrativo, membro, titulo_avaliacao, nota, avaliacao_texto, quando)
        else:
            return True
    return False

def next_page(url, atrativo, pages):
    u = url.split('Reviews-')
    for page in range(1, pages):
        url = '{}Reviews-or{}0-{}'.format(u[0], page, u[1])
        print("----------------atrativo {}-----------------".format(atrativo))
        print('-------------------------------page {}/{}------------------------'.format(page, pages))
        stop = usuarios(url, atrativo)
        if stop:
            break

def membros():


    rows = db((db.membro.membro != None)
            &(db.membro.pontos < 1)
            ).select()
    total = len(rows)
    i = 1
    for row in rows:
        print("------------------------------------{}/{}---{}%------------------------------".format(i, total,
                                                                                                    i / total * 100))
        i += 1
        print(row['membro'])
        membro = row['id']
        print(membro)
        url = "https://www.tripadvisor.com.br/members/{}".format(row['membro'])
        print(url)
        page = get_url(url)
        soup = bs(page.content, 'html.parser')
        since = ''
        sex = ''
        faixa_etaria = ''
        nome = soup.find(class_='nameText')

        sexAge = soup.find(class_='ageSince').text
        if sexAge:
            # sexAge = sexAge.get_text().split('\n')
            since = sexAge
            'Desde fev de 2013 masculino de 35-49 anos'
            try:
                if len(sexAge) > 17:
                    sex = sexAge[17:].strip().lower()
                    if 'de' in sex:
                        x = sex.split('de')
                        faixa_etaria = x[1].split('anos')[0].strip()
                        sex = x[0].strip().lower()
            except:
                pass

        tags = soup.find_all(class_='unclickable')
        t = []
        for tag in tags:
            # print(tag.get_text())
            try:
                t.append(tag.get_text())
                # print('tag ok')
            except:
                pass
                # print(tag)
        tags = '|'.join(sorted(t))
        pontos = soup.find(class_='points').get_text().replace('.', '')
        try:
            nivel = soup.find(class_='level').get_text().split('nível')[1]
        except:
            nivel = 0
        try:
            cidade = soup.find(class_='hometown').get_text().replace(',', '-')
        except:
            cidade = ''
        print(cidade, since, sex, faixa_etaria, pontos, nivel, tags, membro)
        row = db.membro(membro)
        row.update_record(cidade=cidade, since=since, sex=sex, faixa_etaria=faixa_etaria,
                        pontos=pontos, nivel=nivel, tags=tags)
        for tag in t:
            print(tag)
            db.tag.insert(membro,tag)


def reset():
    db.execute("""
        delete from membro;
        delete from avaliacao;
        delete from tag;
        update atrativo
        set avaliacoes=null,quando=null;
        update sqlite_sequence
        set seq=1
        where name='avaliacao';
        update sqlite_sequence
        set seq=1
        where name='membro';
        update sqlite_sequence
        set seq=1
        where name='tag';""")
    c.commit()


def relatorio():
    rows = db.executesql('''select a.id, a.nome as atrativo,
                        t.tag, count(t.tag) as bag
                from atrativo a,
                    avaliacao av,
                    tag t
                where a.id=av.atrativo
                    and av.membro = t.membro
    GROUP BY a.id, a.nome,t.tag
    order by a.id, count(t.tag) DESC ''')
    #rows = db.fetchall()
    for row in rows:
        print(tuple(row))



#relatorio()



atrativos()

Agora, insira algumas urls de atrativos na tabela atrativos e execute o script:

python tripadvisor.py

Caso esteja em uma rede corporativa e não esteja aceitando a configuração de proxy acima, abra um terminal no VSCode no modo bash e digite, adaptando para sua configuração

export=http_proxy='http://usuario:senha@servidor:porta/'
export=https_proxy='http://usuario:senha@servidor:porta/'

Ative o venv(note que no modo bash é diferente:

source venv/Scripts/activate

e execute o script:

python tripadvisor.py

Voltar à parte 2