Wymored Login

monitoramento de avaliações tripadvisor

14 de agosto de 2019 por Alexandre Miguel de Andrade Souza

modelo do banco de dados

instale o pydal como ferramenta de abstração do banco de dados, as instruções são para debian/ubuntu

sudo apt install pip3
pip3 install pydal

crie um arquivo db.py com o conteúdo:

from pydal import DAL, Field

db = DAL("postgres://postgres:senha@localhost:5432/tripadvisor",
        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')
)

db.define_table('tag',
    Field('membro', 'integer'),
    Field('tag', 'text'),
)


db.define_table('hotel',
    Field('cidade'),
    Field('nome'),
    Field('url'),
    Field('avaliacoes','integer'),
    Field('nota','integer'),
)
db.define_table('restaurante',
    Field('cidade'),
    Field('nome'),
    Field('url'),
    Field('avaliacoes','integer'),
    Field('nota','integer'),
)

instale as bibliotecas python requests e beautifulsoup

pip3 install bs4 requests

crie um arquivo tripadvisor_dal.py e execute-o com python3

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

import requests
from bs4 import BeautifulSoup as bs
import math
import datetime
#proxies = {'http': 'http://usuario:senha@proxy:8080/'}
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):
    try:
        return requests.get(url)
        #return requests.get(url,proxies=proxies)
    except:
        import time
        time.sleep(600)
        get_url(url)


def atualiza_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(row['id'],'-', avaliacoes,"avaliações")
        pages = 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 atrativos():
    from datetime import  datetime
    rows = db(db.atrativo.id < 232).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")

        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))


#atualiza_atrativos()
#relatorio()


### servicos

def hoteis():
    import csv
    #hoteis
    with open('servicos.csv','r') as m:
        reader = csv.DictReader(m)
        for row in reader:
            print(row['municipio'] )
            page = get_url(row['hoteis'])
            soup = bs(page.content, 'html.parser')
            hoteis = soup.find_all(class_='main_col')
            for hotel in hoteis:
                #print(hotel)
                try:
                    nota = hotel.find(class_='ui_bubble_rating')['class'][1][7:9]
                except:
                    nota = None
                title = hotel.find(class_='property_title')
                print(title.get_text())
                id = db.hotel.insert(cidade=row['municipio'],
                                nome=title.get_text(),
                                url= "https://www.tripadvisor.com.br"+title.get('href'),
                                avaliacoes=hotel.find(class_='review_count').get_text().split(' ')[0],
                                nota=nota,
                                )
                db.commit()
                print(id)



#hoteis()

def restaurantes():
    import csv
    #hoteis
    with open('servicos.csv','r') as m:
        reader = csv.DictReader(m)
        for line in reader:
            print(line['municipio'] )
            page = get_url(line['restaurantes'])
            soup = bs(page.content, 'html.parser')
            rows = soup.find_all(class_='listing')
            #print(rows)
            for row in rows:
                #print(row)
                try:
                    nota = row.find(class_='ui_bubble_rating')['class'][1][7:9]
                except:
                    nota = None
                title = row.find(class_='property_title')
                print(title.get_text().replace('\n',''))
                reviewCount = row.find(class_='reviewCount').a.get_text().split(' ')[0].replace('.','')
                print(reviewCount)
                id = db.restaurante.insert(cidade=line['municipio'],
                                nome=title.get_text().replace('\n',''),
                                url="https://www.tripadvisor.com.br"+title.get('href'),
                                avaliacoes=int(reviewCount),
                                nota=nota,
                                )
                db.commit()
                print(id)
                #break
            #break


atrativos()

análise de termos mais comuns

instale as bibliotecas

pip3 install python-slugify nltk

(há um procedimento adicional para instalar o corpus, é preciso ver na documentação do nltk. )

crie um arquivo nlp.py com o conteúdo abaixo e execute.

from db import db  # DAL connection

"""
(select a.atrativo, at.nome,a.nota,a.kudos, a.kudos*100/aa.ta as pc
from (select atrativo, nota, count(id) kudos
from avaliacao

group by atrativo, nota
order by atrativo desc, nota desc) as a,
(select atrativo, count(id) ta
        from avaliacao
        group by atrativo) as aa,
atrativo at
where a.atrativo = aa.atrativo
and at.id = a.atrativo  
and at.id = 246
order by nota desc)
union 
(
select atrativo, 'total' as  nome, 0 as nota, count(id) kudos, 100 as pc
from avaliacao 
where atrativo = 246     
group by atrativo
order by atrativo desc, nota desc)
order by nota desc

"""
from collections import Counter
from slugify import  slugify
from nltk.corpus import stopwords

#adapte as stopwords para o seu caso
stw = stopwords.words('portuguese') + ['é', 'de', ',','pra','pois','nao','lugar','local','la','desde','ja','mercado','central','praca','igreja','igrejas',
                                    'parque','museu','estadio','mineirao','cidade','historico','vista','pampulha','lagoa','ainda','cultura','teatro','bh',
                                    'cristo','teleferico','cachoeira','agua','aguas','fonte','pocos','chafariz','eventos','predio','edificio','passeio',
                                    'minas','mina','pedra','vista','centro','ibitipoca','shopping','cidade','gruta','trilha','casa','governador','valadares',
                                    'ibituruna','prefeitura','diamantina','biribiri','rua','oscar','niemeyer','governo','cachoeiras','porem','hotel','trilhas',
                                    'planetario','sao','joao','tiradentes','uberaba','presepio','voce','vila','santa','lourenco','basilica','espaco','topazio',
                                    'minascentro','mineirinho','estacao','itacolomi','lambari','juiz','brasil']


def bag(atrativo):
    #atrativo = 247
    a = db.atrativo(atrativo)
    print(" ")
    print(a.nome)
    print(a.url)
    print(" ")
    sw = stw + slugify(a.nome,separator=" ").split()
    sw = set(sw)
    #print('nota',"\t","avaliacoes")
    rows =  db((db.avaliacao.atrativo == atrativo)).select()
    total = len(rows)
    print("Total de avaliações", total,'\n')
    for nota in range(5,0,-1):
        rows = db((db.avaliacao.atrativo == atrativo)
                  & (db.avaliacao.nota == nota)
                  ).select()
        avaliacoes = len(rows)
        titulo = ""
        avaliacao = ""
        for row in rows:
            titulo += row.titulo+" "
            avaliacao += row.avaliacao + " "





        #prepare data
        titulo = slugify(titulo,separator=" ").split()
        titulo = [w for w in titulo if not w in sw]
        avaliacao = slugify(avaliacao,separator=" ").split()
        avaliacao = [w for w in avaliacao if not w in sw]


        #bag of words

        titulo = Counter(titulo)
        avaliacao = Counter(avaliacao)
        print('nota', "\t", "avaliacoes","\t","percentual")
        print(nota, "\t\t", avaliacoes,"\t\t\t",avaliacoes*100/total,"%")
        print("titulos", "\t\t", titulo.most_common(10))
        print("avaliacoes", "\t\t", avaliacao.most_common(10))
        print("    ")
    print("------------------------------------------------------------------------------")

def bag_year(atrativo):
    #atrativo = 247
    a = db.atrativo(atrativo)
    print(" ")
    print(a.nome)
    print(a.url)
    print(" ")
    sw = stw + slugify(a.nome,separator=" ").split()
    sw = set(sw)
    #print('nota',"\t","avaliacoes")

    rows =  db((db.avaliacao.atrativo == atrativo)).select()
    total = len(rows)
    print("Total de avaliações", total,'\n')

    for ano in range(2018,2010,-1):

        rows =  db((db.avaliacao.atrativo == atrativo)
                  &(db.avaliacao.quando.year()==ano)).select()
        total_ano = len(rows)
        p = total_ano*100/total if total_ano > 0 else 0.0
        print("Total de avaliações de", ano, ": ", total_ano, '\n')
        for nota in range(5,0,-1):
            rows = db((db.avaliacao.atrativo == atrativo)
                      & (db.avaliacao.quando.year() == ano)
                      & (db.avaliacao.nota == nota)
                      ).select()
            avaliacoes = len(rows)
            titulo = ""
            avaliacao = ""
            for row in rows:
                titulo += row.titulo+" "
                avaliacao += row.avaliacao + " "





            #prepare data
            titulo = slugify(titulo,separator=" ").split()
            titulo = [w for w in titulo if not w in sw]
            avaliacao = slugify(avaliacao,separator=" ").split()
            avaliacao = [w for w in avaliacao if not w in sw]


            #bag of words

            titulo = Counter(titulo)
            avaliacao = Counter(avaliacao)
            print('nota', "\t", "avaliacoes","\t","percentual")
            p1 = avaliacoes*100/total_ano if avaliacoes > 0 else 0
            print(nota, "\t\t", avaliacoes,"\t\t\t",p1 ,"%")
            print("titulos", "\t\t", titulo.most_common(10))
            print("avaliacoes", "\t\t", avaliacao.most_common(10))
            print("    ")
        print("##############################")
    print("-------------------------------------------------------------------------------------")


qualitativos = ['otimo','otima','excelente','preco','melhor','ruim','pior','vale a pena','lindo','linda','local','lugar']


stw = stopwords.words('portuguese') + ['é']

def qualy(atrativo):
    a = db.atrativo(atrativo)
    print(" ")
    print(a.nome)
    print(a.url)
    print(" ")

    rows =  db((db.avaliacao.atrativo == atrativo)).select()
    total = len(rows)
    print("Total de avaliações", total,'\n')


    for nota in range(5,0,-1):
        rows = db((db.avaliacao.atrativo == atrativo)
                  & (db.avaliacao.nota == nota)
                  ).select()
        avaliacoes = len(rows)
        #titulo = ""
        #avaliacao = ""
        ba = {q:dict() for q in qualitativos}
        aa = {q:dict() for q in qualitativos}
        for row in rows:
            #titulo = row.titulo
            avaliacao = slugify(row.avaliacao,separator=" ")
            for q in qualitativos:
                t = avaliacao.split(q)
                if len(t)==2:
                    #antes
                    b = t[0].split()
                    i = -1
                    w = b[i] if len(b) > 0 else None
                    if w:
                        while w in stw:
                            i += -1
                            w = b[i] if len(b) > abs(i) else None
                    if w:
                        try:
                            v = ba[q][w]+ 1
                        except:
                            v = 1
                        ba[q][w]    = v

                    #depois
                    b = t[1].split()
                    i = 0
                    w = b[i] if len(b) > 0 else None
                    if w:
                        while w in stw:
                            i += 1
                            w = b[i] if len(b) > abs(i) else None                    
                    if w:
                        try:
                            v = aa[q][w]+ 1
                        except:
                            v = 1
                        aa[q][w] = v

        print('nota',nota)
        print('------------------------------------------------')
        print('\npalavra', "\t", "qualitativo","\t","quantidade")
        for q in qualitativos:
            try:
                for w in aa[q]:
                    print(w, "\t\t", q,"\t\t",aa[q][w])
            except:
                pass
        print('------------------------------------------------')
        print('qualitativo', "\t", "palavra","\t","quantidade")
        for q in qualitativos:
            try:
                for w in ba[q]:
                    print(q, "\t\t", w,"\t\t",ba[q][w])
            except:
                pass

        print("    ")
    print("------------------------------------------------------------------------------")



"""
print(stw)
for atrativo in range(1,248):

    #bag(atrativo)
    bag_year(atrativo)
"""
qualy(247)

ah, o comando para execução do arquivo acima é

python3 nlp.py > bags.txt