[PYTHON] [First data science ⑥] J'ai essayé de visualiser le prix du marché des restaurants à Tokyo

Ravi de vous rencontrer. Je suis N.D., un étudiant universitaire de 4e année appartenant au Département de physique. L'expérience de Python est un peu touchée par moi-même. C'était ma première fois à gratter et à ramper.

La Division de la science des données de Kakko Co., Ltd., qui est actuellement stagiaire, a pour tâche de créer un robot d'exploration pendant la période d'essai pour collecter, traiter et visualiser les données, et discuter brièvement de ce qu'il a appris.

Tâche

thème

Visualisez et considérez les prix du marché des restaurants à travers Tokyo Acquérir également d'autres variables susceptibles d'être prises et les analyser en les comparant avec le budget.

_ Sous-thème _

Le thème étant abstrait, je voudrais mettre en place les situations spécifiques suivantes. situation Utilisez les données pour montrer objectivement aux amis qui viennent à Tokyo: "Quel est le prix du marché des restaurants à Tokyo et quel est le genre le plus sur ce marché?"

Autres événements découverts

Visualisez et montrez ce que vous avez appris pour comparer les budgets avec d'autres variables ainsi que des sous-thèmes.

politique

  1. Parcourez le site gourmand "Hot Pepper Gourmet" et récupérez l'url de la page de détail de chaque boutique
  2. Enregistrez le fichier html à partir de l'url de la page de détail de chaque boutique (nombre de boutiques = nombre de html enregistrés)
  3. Récupération de chaque variable du fichier HTML récupéré
  4. Visualisation / analyse des données
  5. Présentez la réponse au thème

_ Rampant _

Cette fois, l'exploration est le résultat de la recherche de "Boutiques qui peuvent faire des réservations en ligne dans tout Tokyo" sur le site gastronomique du piment. Acquisition de 16475 boutiques le mercredi 16 octobre 2019.

** Procédure d'exploration **

  1. Obtenez le nombre de boutiques de la première page avant d'explorer pour vérifier le résultat de l'acceptation plus tard
  2. [1ère page] Lisez l'url de la page de détail de chaque boutique (ci-après, l'URL de la boutique) et enregistrez-la dans la liste python.
  3. [Transition vers la page suivante] Obtenez l'URL de la page suivante à partir de la page nation et de la transition
  4. Lisez l'URL de la boutique sur la page de destination et enregistrez-la dans la liste python
  5. Répétez quelques étapes jusqu'à ce qu'il n'y ait plus de pages
  6. Accédez à l'URL du magasin enregistrée dans la liste et enregistrez le fichier html un par un
  7. Enfin, obtenez le nombre de boutiques en grattant à partir de la dernière page de la même manière que 1.

Le code d'exploration ressemble à ceci:

crawling.py


from bs4 import BeautifulSoup
import requests
import time
import os
# timer
t1 = time.time()

# function
# get number of shop
def get_num(soup):
    num = soup.find('p', {'class':'sercheResult fl'}).find('span', {'class':'fcLRed bold fs18 padLR3'}).text
    print('num:{}'.format(num))

# get url of shop
def get_shop_urls(tags):
    shop_urls = []
    # ignore the first shop because it is PR
    tags = tags[1:]
    for tag in tags:
        shop_url = tag.a.get('href')
        shop_urls.append(shop_url)
    return shop_urls

def save_shop_urls(shop_urls, dir_path=None, test=False):
    # make directry
    if test:
        if dir_path is None:
            dir_path = './html_dir_test'
    elif dir_path is None:
        dir_path = './html_dir'

    if not os.path.isdir(dir_path):
        os.mkdir(dir_path)

    for i, shop_url in enumerate(shop_urls):
        time.sleep(1)
        shop_url = 'https://www.hotpepper.jp' + shop_url
        r = requests.get(shop_url).text
        file_path = 'shop{:0>5}_url.html'.format(i)
        with open(dir_path + '/' + file_path, 'w') as f:
            f.write(r)
    # return last shop number
    return len(shop_urls)


start_url = 'https://www.hotpepper.jp/yoyaku/SA11/'
response = requests.get(start_url).text
soup = BeautifulSoup(response, 'html.parser')
tags = soup.find_all('h3', {'class':'detailShopNameTitle'})

# get last page number
last_page = soup.find('li', {'class':'lh27'}).text.replace('1/', '').replace('page', '')
last_page = int(last_page)
print('last page num:{}'.format(last_page))

# get the number of shops before crawling
get_num(soup)

# first page crawling
start_shop_urls = get_shop_urls(tags)

# from 2nd page
shop_urls = []
# last page(test)
last_page = 10 # test
for p in range(last_page-1):
    time.sleep(1)
    url = start_url + 'bgn' + str(p+2) + '/'
    r = requests.get(url).text
    soup = BeautifulSoup(r, 'html.parser')
    tags = soup.find_all('h3', {'class':'detailShopNameTitle'})
    shop_urls.extend(get_shop_urls(tags))
    # how speed
    if p % 100 == 0:
        percent = p/last_page*100
        print('{:.2f}% Done'.format(percent))

start_shop_urls.extend(shop_urls)
shop_urls = start_shop_urls

t2 = time.time()
elapsed_time = t2 - t1
print('time(get_page):{:.2f}s'.format(elapsed_time))
print('num(shop_num):{}'.format(len(shop_urls)))

# get the url of shop
last_num = save_shop_urls(shop_urls) # html_dir

# get the number of shops after crawling
get_num(soup)

t3 = time.time()
elapsed_time = t3 - t1
print('time(get_html):{:.2f}s'.format(elapsed_time))
print('num(shop_num):{}'.format(last_num))

Grattage

Voici les variables grattées cette fois. scraping_var.png

procédure

  1. Gratter les 9 variables ci-dessus pour chaque magasin
  2. Lorsque toutes les variables sont disponibles, ajoutez-les en tant qu'enregistrements au DataFrame des pandas.
  3. Vérifiez si le nombre d'enregistrements est cohérent avec le nombre de boutiques acquises par l'exploration

Le code de scraping ressemble à ceci:

scraping.py


from bs4 import BeautifulSoup
import glob
import requests
import time
import os
import pandas as pd
from tqdm import tqdm
import numpy as np


def get_shopinfo(category, soup):
    shopinfo_th = soup.find('div', {'class':'shopInfoDetail'}).find_all('th')
    # get 'category' from 'shopinfo_th'
    category_value = list(filter(lambda x: category in x , shopinfo_th))
    if not category_value:
        category_value = None
    else:
        category_value = category_value[0]
        category_index = shopinfo_th.index(category_value)
        shopinfo_td = soup.find('div', {'class':'shopInfoDetail'}).find_all('td')
        category_value = shopinfo_td[category_index].text.replace('\n', '').replace('\t', '')
    return category_value

# judge [] or in
def judge(category):
    if category is not None:
        category = category.text.replace('\n', '').replace('\t', '')
    else:
        category = np.nan
    return category

# judge [] or in
def judge_atag(category):
    if category is not None:
        category = category.a.text.replace('\n', '').replace('\t', '')
    else:
        category = np.nan
    return category

# judge [] or in
def judge_ptag(category):
    if category is not None:
        category = category.p.text.replace('\n', '').replace('\t', '')
    else:
        category = np.nan
    return category

# judge [] or in
def judge_spantag(category):
    if category is not None:
        category = category.span.text.replace('\n', '').replace('\t', '')
    else:
        category = 0
    return category

# available=1, not=0
def available(strlist):
    available_flg = 0
    if 'disponible' in strlist:
        available_flg = 1
    return available_flg

# categorize money
def category2index(category, range):
    if category in range:
        category = range.index(category)
    return category

def scraping(html, df, price_range):
    soup = BeautifulSoup(html, 'html.parser')
    dinner = soup.find('span', {'class':'shopInfoBudgetDinner'})
    dinner = judge(dinner)
    dinner = category2index(dinner, price_range)
    lunch = soup.find('span', {'class':'shopInfoBudgetLunch'})
    lunch = judge(lunch)
    lunch = category2index(lunch, price_range)
    genre_tag = soup.find_all('dl', {'class':'shopInfoInnerSectionBlock cf'})[1]
    genre = genre_tag.find('p', {'class':'shopInfoInnerItemTitle'})
    genre = judge_atag(genre)
    area_tag = soup.find_all('dl', {'class':'shopInfoInnerSectionBlock cf'})[2]
    area = area_tag.find('p', {'class':'shopInfoInnerItemTitle'})
    area = judge_atag(area)
    rating = soup.find('div', {'class':'ratingInfo'})
    rating = judge_ptag(rating)
    review = soup.find('p', {'class':'review'})
    review = judge_spantag(review)
    f_meter = soup.find_all('dl', {'class':'featureMeter cf'})
    # if 'f_meter' is nan, 'size'='customer'='people'='peek'=nan
    if f_meter == []:
        size = np.nan
        customer = np.nan
        people = np.nan
        peek = np.nan
    else:
        meterActive = f_meter[0].find('span', {'class':'meterActive'})
        size = f_meter[0].find_all('span').index(meterActive)
        meterActive = f_meter[1].find('span', {'class':'meterActive'})
        customer = f_meter[1].find_all('span').index(meterActive)
        meterActive = f_meter[2].find('span', {'class':'meterActive'})
        people = f_meter[2].find_all('span').index(meterActive)
        meterActive = f_meter[3].find('span', {'class':'meterActive'})
        peek = f_meter[3].find_all('span').index(meterActive)
    credits = get_shopinfo('carte de crédit', soup)
    credits = available(credits)
    emoney = get_shopinfo('Monnaie électronique', soup)
    emoney = available(emoney)
    data = [lunch, dinner, genre, area, float(rating), review, size, customer, people, peek, credits, emoney]
    s = pd.Series(data=data, index=df.columns, name=str(i))
    df = df.append(s)
    return df

columns = ['budget(Le midi)', 'budget(Nuit)', "Genre", "zone", 'Évaluation', 'Nombre d'avis', 'Taille de la boutique'
           , 'Clientèle', 'Nombre de personnes/ensemble', 'Heures de pointe', 'carte de crédit', 'Monnaie électronique']
base_url = 'https://www.hotpepper.jp/SA11/'
response = requests.get(base_url).text
soup = BeautifulSoup(response, 'html.parser')
# GET range of price
price_range = soup.find('ul', {'class':'samaColumnList'}).find_all('a')
price_range = [p.text for p in price_range]
# price_range = ['~ 500 yens', '501 à 1 000 yens', '1001 à 1500 yens', '1501 à 2000 yens', '2001 à 3000 yens', '3001 à 4000 yens', '4001 à 5000 yens'
#             , '5001 à 7000 yens', '7001-10000 yens', '10001 à 15000 yens', '15001 à 20000 yens', '20001 ~ 30000 yens', '30001 yens ~']

num = 16475  # number of data
# num = 1000 # test
df = pd.DataFrame(data=None, columns=columns)

for i in range(num):
# for i in tqdm(lis):
    html = './html_dir/shop{:0>5}_url.html'.format(i)
    with open(html,"r", encoding='utf-8') as f:
        shop_html = f.read()

    df = scraping(shop_html, df, price_range)
    if i % 1600 == 0:
        percent = i/num*100
        print('{:.3f}% Done'.format(percent))

df.to_csv('shop_info.csv', encoding='shift_jis')

_ Résultat d'acceptation _

Les résultats d'acceptation sont les suivants. スクリーンショット 2019-11-26 11.41.30.png

L'exploration a pris un peu moins d'une heure, le site a donc été mis à jour pendant cette période. Vous pouvez voir qu'il existe une différence entre le nombre de magasins qui étaient initialement et le nombre de magasins après l'exploration.

Résultats pour les sous-thèmes

_ Confirmer le sous-thème _

"Visualisez le prix du marché des restaurants à Tokyo, Précisez quel genre de magasins est le plus populaire dans cette gamme de prix. "

Conclusion pour le sous-thème

Les données sous-jacentes sont présentées ci-dessous dans l'ordre.

_ Devis budgétaire _

Nous avons visualisé le prix du marché du budget séparément pour le dîner et le déjeuner. スクリーンショット 2019-11-26 11.56.37.png

Genre par gamme de prix

À partir des résultats ci-dessus, nous avons trouvé un prix de marché approximatif pour les restaurants à Tokyo, alors visualisons les genres par gamme de prix. スクリーンショット 2019-11-27 15.07.47.png スクリーンショット 2019-11-27 15.09.30.png

** Genres inclus dans "Autre" ** Pour le dîner et le déjeuner, les genres suivants avec un petit nombre sont inclus dans "Autre". [Oshiyaki / Monja / Café / Bonbons / Ramen / Coréen / International / Occidental / Créatif / Autre gastronomique]

Je pensais que le "Tavern" dans la fourchette de prix de "500-1000 yens" était trop bon marché pour le déjeuner, donc je vais creuser plus profondément ici.

_ Qu'est-ce qu'une taverne "500-1000 yens"?

Comme indiqué ci-dessous, on peut constater que le menu du midi est proposé pendant la journée, même s'il s'appelle "Izakaya". スクリーンショット 2019-11-26 12.10.38.png

Autres événements découverts

Conclusion

―― La clientèle des magasins dans la fourchette de prix de «** 7 000 yens à » pour le dîner a tendance à être plus masculine que les femmes, et le dîner et le déjeuner sont de « 1 000 à 3 000 yens **». La clientèle des magasins de la gamme de prix a tendance à être plus féminine que masculine.

―― Plus la fourchette de prix pour le dîner et le déjeuner est élevée, plus la note est élevée.

-Dans la ** gamme de prix haut **, de nombreux magasins acceptent ** les cartes de crédit **

――Les magasins dans la fourchette de prix "** 2000-4000 yens **" pour le dîner ont tendance à avoir une large ** capacité **.

Voici les données à l'appui.

Clients par gamme de prix

Nous avons comparé la fourchette de prix par clientèle. スクリーンショット 2019-11-26 12.17.30.png

De là, on peut dire que la clientèle des magasins dans la fourchette de prix de "7 000 yens" a tendance à être plus masculine que les clientes au dîner, et le prix de "1000-3000 yens" pour le dîner et le déjeuner. Il a été constaté que la clientèle de la boutique obi a tendance à avoir plus de femmes que de clients masculins.

Evaluation par gamme de prix

Nous tracerons les notes pour chacune des gammes de prix du dîner et du déjeuner. À cette époque, il y a beaucoup de magasins avec la même évaluation dans la même gamme de prix, nous avons donc adopté le jittering et intentionnellement décalé l'intrigue. Le résultat du test t est indiqué sous le graphique. ** Définition du test t ** Dîner: groupe par magasins à moins de 4000 yens et magasins à plus de 4000 yens Déjeuner: groupe par magasins à moins de 2000 yens et magasins à plus de 2000 yens スクリーンショット 2019-11-26 12.31.33.png スクリーンショット 2019-11-26 12.32.18.png Vous pouvez voir que plus la fourchette de prix pour le dîner et le déjeuner est élevée, plus la note a tendance à être élevée. D'après le résultat du test t, on peut dire qu'il existe une différence d '«** évaluation **» entre la fourchette de prix élevée et la fourchette de prix bas.

_ Utilisation des cartes de crédit par fourchette de prix _

Nous avons comparé le statut d'utilisation des cartes de crédit par fourchette de prix. スクリーンショット 2019-11-27 15.22.17.png

Encore une fois, conformément à mon intuition, j'ai constaté qu'un grand pourcentage de magasins de la ** gamme de prix élevés ** acceptent ** les cartes de crédit **. De plus, la fourchette de prix «10 000 yens ~» pour le déjeuner n'est pas affichée car le nombre de caisses n'était pas suffisant pour être évalué.

Taille du magasin par gamme de prix

Nous avons comparé les tailles des magasins évalués sur une échelle de 5 points par gamme de prix. Comme je ne pourrais conclure que le dîner ici, il ne sera affiché que là-bas. Plus le bleu est foncé, plus la boutique est large. スクリーンショット 2019-11-26 13.04.13.png Vous pouvez voir que les magasins dans la gamme de prix de «2000-4000 yens» ont tendance à avoir une grande capacité pour le dîner. Étant donné que le ratio de tavernes est important dans cette gamme de prix, on pense que les tavernes de grande capacité sont grandes.

en conclusion

Point de réflexion

Je me suis rendu compte à quel point il est difficile de «collecter les informations obtenues en grattant et de les visualiser afin que la conclusion puisse être transmise à l'autre partie». ** Si vous recommencez ** Définissez un objectif clair pour l'analyse avant d'écrire du code et calculez à nouveau pour planifier le processus

_Ce que j'ai appris des commentaires _

** A reçu un examen de code ** J'ai reçu les points suivants, et je voudrais l'améliorer par la suite.

** Après la revue de l'annonce ** C'était un processus de "comment montrer le graphique pour le rendre plus facile à transmettre". Nous avons reçu des commentaires selon lesquels il est important de créer un «graphique intuitif», par exemple en disant que plus la fourchette de prix est élevée, plus la fourchette de prix est élevée et que la densité est exprimée par la gigue. J'ai également appris que montrer une conclusion semblable à une histoire mène à la compréhension de l'autre partie. Je passerai ma future vie d'analyse tout en sachant comment relier les résultats obtenus à des problèmes réels.

Recommended Posts

[First data science ⑥] J'ai essayé de visualiser le prix du marché des restaurants à Tokyo
J'ai essayé de visualiser les informations spacha de VTuber
[Première science des données ⑤] J'ai essayé d'aider mon ami à trouver la première propriété par analyse de données
[Python] J'ai essayé de visualiser la relation de suivi de Twitter
J'ai essayé de visualiser les données de course du jeu de course (Assetto Corsa) avec Plotly
[Traitement du langage naturel] J'ai essayé de visualiser les remarques de chaque membre de la communauté Slack
J'ai essayé de trouver la tendance du nombre de navires dans la baie de Tokyo à partir d'images satellites.
Utilisez PyCaret pour prédire le prix des appartements d'occasion à Tokyo!
J'ai essayé de visualiser la condition commune des téléspectateurs de la chaîne VTuber
J'ai essayé de résoudre la première question de l'examen d'entrée en mathématiques 2019 de l'Université de Tokyo avec python sympy
J'ai essayé d'adapter la fonction exponentielle et la fonction logistique au nombre de patients positifs au COVID-19 à Tokyo
J'ai essayé de visualiser la tranche d'âge et la distribution des taux d'Atcoder
J'ai essayé de visualiser le texte du roman "Weather Child" avec Word Cloud
J'ai essayé d'afficher la valeur d'altitude du DTM dans un graphique
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé de corriger la forme trapézoïdale de l'image
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé de visualiser facilement les tweets de JAWS DAYS 2017 avec Python + ELK
J'ai essayé de récupérer les données de l'ordinateur portable en le démarrant sur Ubuntu
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé de prédire le match de la J League (analyse des données)
J'ai essayé d'utiliser l'API de Sakenowa Data Project
J'ai essayé d'effacer la partie négative de Meros
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé de résumer les opérations de chaîne de Python
Python pratique 100 coups J'ai essayé de visualiser l'arbre de décision du chapitre 5 en utilisant graphviz
J'ai essayé de visualiser les caractéristiques des nouvelles informations sur les personnes infectées par le virus corona avec wordcloud
J'ai essayé de visualiser les paroles de GReeeen, que j'écoutais de façon folle dans ma jeunesse mais que je ne l'écoutais plus.
J'ai essayé de trouver l'entropie de l'image avec python
Essayez de gratter les données COVID-19 Tokyo avec Python
[Courses de chevaux] J'ai essayé de quantifier la force du cheval de course
[Première API COTOHA] J'ai essayé de résumer l'ancienne histoire
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de résumer le code souvent utilisé dans Pandas
J'ai essayé d'illustrer le temps et le temps du langage C
J'ai essayé de résumer les commandes souvent utilisées en entreprise
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
[TF] J'ai essayé de visualiser le résultat de l'apprentissage en utilisant Tensorboard
[Apprentissage automatique] J'ai essayé de résumer la théorie d'Adaboost
[Célébration: valeur marchande de 2 billions de dollars] J'ai essayé de visualiser le brevet d'Apple
[Python] J'ai essayé de collecter des données en utilisant l'API de wikipedia
J'ai essayé de combattre le minimum local de la fonction Goldstein-Price
Ce que j'ai vu en analysant les données du marché des ingénieurs
J'ai essayé d'implémenter le blackjack du jeu Trump en Python
J'ai envoyé les données de Raspberry Pi à GCP (gratuit)
J'ai essayé de visualiser la consommation électrique de ma maison avec Nature Remo E lite
[Traitement du langage naturel] J'ai essayé de visualiser les sujets d'actualité cette semaine dans la communauté Slack
J'ai essayé d'analyser les données du tournoi de football de la Coupe du monde de football en Russie avec l'action de football
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
J'ai essayé de mettre HULFT IoT (Edge Streaming) dans la passerelle Rooster de Sun Electronics
Depuis que le stock a plongé en raison de l'influence du nouveau virus corona, j'ai essayé de visualiser les performances de ma fiducie d'investissement avec Python.
J'ai essayé de décrire le trafic en temps réel avec WebSocket
[Linux] J'ai essayé de résumer les commandes de confirmation des ressources