[PYTHON] [Erste Datenwissenschaft ⑥] Ich habe versucht, den Marktpreis von Restaurants in Tokio zu visualisieren

Freut mich, dich kennenzulernen. Ich bin N.D., ein Universitätsstudent im 4. Jahr, der zum Fachbereich Physik gehört. Die Erfahrung von Python ist von mir ein wenig berührt. Es war mein erstes Mal, dass ich kratzte und krabbelte.

Die Data Science Division der Kakko Co., Ltd., die derzeit ein Praktikant ist, hat die Aufgabe, während des Testzeitraums einen Crawler zu erstellen, um die Daten zu sammeln, zu verarbeiten, zu visualisieren und kurz zu diskutieren, was sie gelernt haben.

Aufgabe

Thema

Visualisieren und berücksichtigen Sie die Marktpreise von Restaurants in ganz Tokio Erfassen Sie auch andere Variablen, die wahrscheinlich verwendet werden, und analysieren Sie sie, während Sie sie mit dem Budget vergleichen.

_ Unterthema _

Da das Thema abstrakt ist, möchte ich die folgenden spezifischen Situationen einrichten. Lage Verwenden Sie Daten, um Freunden, die nach Tokio kommen, objektiv zu zeigen: "Was ist der Marktpreis für Restaurants in Tokio und welches Genre ist das größte auf diesem Markt?"

Andere entdeckte Ereignisse

Visualisieren und zeigen Sie, was Sie gelernt haben, um Budgets mit anderen Variablen und Unterthemen zu vergleichen.

Politik

  1. Durchsuchen Sie die Gourmet-Website "Hot Pepper Gourmet" und rufen Sie die URL der Detailseite jedes Shops ab
  2. Speichern Sie die HTML-Datei über die URL der Detailseite jedes Shops (Anzahl der Shops = Anzahl der gespeicherten HTML-Dateien).
  3. Scraping jeder Variablen aus der abgerufenen HTML-Datei
  4. Visualisierung / Datenanalyse
  5. Präsentieren Sie die Antwort auf das Thema

_ Krabbeln _

Dieses Mal ist Crawlen das Suchergebnis von "Geschäfte, die Online-Reservierungen in ganz Tokio vornehmen können" auf der Gourmet-Website für Paprika. Erwerb von 16475 Geschäften am Mittwoch, 16. Oktober 2019.

** Crawling-Verfahren **

  1. Rufen Sie vor dem Crawlen die Anzahl der Shops auf der ersten Seite ab, um das Akzeptanzergebnis später zu überprüfen
  2. [1. Seite] Lesen Sie die URL der Detailseite jedes Shops (im Folgenden: Shop-URL) und speichern Sie sie in der Python-Liste.
  3. [Übergang zur nächsten Seite] Ruft die URL der nächsten Seite von der Seitennation und dem Übergang ab
  4. Lesen Sie die Shop-URL von der Zielseite und speichern Sie sie in der Python-Liste
  5. Wiederholen Sie einige Schritte, bis keine Seiten mehr vorhanden sind
  6. Wechseln Sie zu der in der Liste gespeicherten Store-URL und speichern Sie die HTML-Datei nacheinander
  7. Ermitteln Sie abschließend die Anzahl der Geschäfte, indem Sie auf die gleiche Weise wie mit 1 von der letzten Seite kratzen.

Der Crawler-Code sieht folgendermaßen aus:

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('Seite', '')
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))

Scraping

Das Folgende sind die Variablen, die dieses Mal abgekratzt wurden. scraping_var.png

Verfahren

  1. Verschrotten Sie die obigen 9 Variablen für jedes Geschäft
  2. Wenn alle Variablen verfügbar sind, fügen Sie sie als Datensätze zum DataFrame von Pandas hinzu.
  3. Überprüfen Sie, ob die Anzahl der Datensätze mit der Anzahl der durch Crawlen erhaltenen Shops übereinstimmt

Der Scraping-Code sieht folgendermaßen aus:

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 'verfügbar' 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('Kreditkarte', soup)
    credits = available(credits)
    emoney = get_shopinfo('Elektronisches Geld', 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(Mittag)', 'Budget(Nacht)', "Genre", "Bereich", 'Auswertung', 'Anzahl der Bewertungen', 'Ladengröße'
           , 'Kundenbasis', 'Anzahl der Personen/einstellen', 'Stoßzeiten', 'Kreditkarte', 'Elektronisches Geld']
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 Yen', '501-1000 Yen', '1001-1500 Yen', '1501-2000 Yen', '2001-3000 Yen', '3001-4000 Yen', '4001-5000 Yen'
#             , '5001 ~ 7000 Yen', '7001-10000 Yen', '10001-15000 Yen', '15001 ~ 20000 Yen', '20001 ~ 30000 Yen', '30001 Yen ~']

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

_ Akzeptanzergebnis _

Die Akzeptanzergebnisse sind wie folgt. スクリーンショット 2019-11-26 11.41.30.png

Das Crawlen dauerte etwas weniger als eine Stunde, daher wurde die Site während dieser Zeit aktualisiert. Sie können sehen, dass es einen Unterschied zwischen der Anzahl der Geschäfte, die ursprünglich vorhanden waren, und der Anzahl der Geschäfte nach dem Crawlen gibt.

Ergebnisse für Unterthemen

_ Unterthema bestätigen _

"Visualisieren Sie den Marktpreis von Restaurants in Tokio, Klären Sie, welches Genre von Geschäften in dieser Preisklasse am beliebtesten ist. ""

Fazit für Unterthema

Die zugrunde liegenden Daten werden unten in der angegebenen Reihenfolge angezeigt.

_ Budgetangebot _

Wir haben den Marktpreis des Budgets für Abendessen und Mittagessen separat visualisiert. スクリーンショット 2019-11-26 11.56.37.png

Genre nach Preisspanne

Aus den obigen Ergebnissen haben wir einen ungefähren Marktpreis für Restaurants in Tokio ermittelt. Lassen Sie uns also die Genres nach Preisklassen visualisieren. スクリーンショット 2019-11-27 15.07.47.png スクリーンショット 2019-11-27 15.09.30.png

** Genres in "Sonstige" enthalten ** Sowohl zum Abendessen als auch zum Mittagessen sind die folgenden Genres mit einer kleinen Anzahl in "Andere" enthalten. [Oshiyaki / Monja / Cafe / Süßigkeiten / Ramen / Koreanisch / International / Westlich / Kreativ / Anderer Gourmet]

Ich dachte, dass die "Taverne" in der Preisklasse von "500-1000 Yen" zu billig für das Mittagessen war, also werde ich hier tiefer graben.

_ Was ist eine "500-1000 Yen" Taverne?

Wie unten gezeigt, ist zu sehen, dass das Mittagsmenü tagsüber angeboten wird, obwohl es "Izakaya" heißt. スクリーンショット 2019-11-26 12.10.38.png

Andere entdeckte Ereignisse

Fazit

――Der Kundenstamm von Geschäften in der Preisspanne von "** 7.000 Yen bis " für das Abendessen ist tendenziell männlicher als für weibliche Kunden, und sowohl das Abendessen als auch das Mittagessen betragen " 1000 bis 3000 Yen **". Der Kundenstamm von Geschäften in der Preisklasse ist eher weiblich als männlich.

―― Je höher die Preisspanne für Abendessen und Mittagessen ist, desto höher ist tendenziell die Bewertung.

-In der ** hohen Preisspanne ** gibt es viele Geschäfte, die ** Kreditkarten ** akzeptieren

――Läden in der Preisklasse von "** 2000-4000 Yen **" zum Abendessen haben in der Regel eine große ** Kapazität **.

Das Folgende sind die unterstützenden Daten.

Kundenbasis nach Preisspanne

Wir haben die Preisspanne nach Kundenstamm verglichen. スクリーンショット 2019-11-26 12.17.30.png

Daraus lässt sich ableiten, dass der Kundenstamm von Geschäften in der Preisspanne von "7.000 Yen" beim Abendessen eher männlich als weiblich ist und der Preis für Abendessen und Mittagessen "1000-3000 Yen" beträgt. Es wurde festgestellt, dass der Kundenstamm des Obi-Shops tendenziell mehr weibliche als männliche Kunden hat.

Bewertung nach Preisspanne

Wir zeichnen die Bewertungen für jede Preisklasse für Abendessen und Mittagessen auf. Zu dieser Zeit gibt es viele Geschäfte mit der gleichen Bewertung in der gleichen Preisklasse, daher haben wir Jittering angewendet und die Handlung absichtlich verschoben. Das Ergebnis des t-Tests ist unter der Grafik dargestellt. ** Definition von t Test ** Abendessen: Gruppieren Sie nach Geschäften unter 4000 Yen und Geschäften über 4000 Yen Mittagessen: Gruppiert nach Geschäften unter 2000 Yen und Geschäften über 2000 Yen スクリーンショット 2019-11-26 12.31.33.png スクリーンショット 2019-11-26 12.32.18.png Sie können sehen, dass die Bewertung tendenziell umso höher ist, je höher die Preisspanne für Abendessen und Mittagessen ist. Aus dem Ergebnis des t-Tests kann gesagt werden, dass es einen Unterschied in der "** Bewertung **" zwischen der hohen Preisspanne und der niedrigen Preisspanne gibt.

_ Kreditkartennutzung nach Preisspanne _

Wir haben den Nutzungsstatus von Kreditkarten nach Preisklassen verglichen. スクリーンショット 2019-11-27 15.22.17.png

Entsprechend meiner Intuition stellte ich wieder fest, dass ein großer Prozentsatz der Geschäfte in der ** hohen Preisklasse ** Kreditkarten ** akzeptiert. Außerdem wird die Preisspanne von "10.000 Yen ~" für das Mittagessen nicht angezeigt, da die Anzahl der Fälle nicht ausreichte, um sie zu bewerten.

Store Größe nach Preisklasse

Wir haben die Größe der Geschäfte auf einer 5-Punkte-Skala nach Preisspanne verglichen. Da ich hier nur das Abendessen abschließen konnte, wird es nur dort veröffentlicht. Je dunkler das Blau, desto breiter der Laden. スクリーンショット 2019-11-26 13.04.13.png Sie können sehen, dass Geschäfte in der Preisklasse von "2000-4000 Yen" tendenziell eine große Kapazität für das Abendessen haben. Da das Verhältnis der Tavernen in dieser Preisklasse groß ist, wird angenommen, dass Tavernen mit einer großen Kapazität groß sind.

abschließend

Reflexionspunkt

Ich erkannte selbst, wie schwierig es ist, "die durch Schaben erhaltenen Informationen zu sammeln und zu visualisieren, damit die Schlussfolgerung an die andere Partei weitergegeben werden kann". ** Wenn du es noch einmal machst ** Legen Sie einen klaren Zweck für die Analyse fest, bevor Sie Code schreiben, und rechnen Sie zurück, um den Prozess zu planen

Was ich aus dem Feedback gelernt habe

** Codeüberprüfung erhalten ** Ich habe die folgenden Punkte erhalten und möchte sie danach verbessern.

--Schreiben Sie Code, während Sie die Python-Code-Konvention pep8 kennen

** Nach der Ankündigung Überprüfung ** Es war ein Prozess, "wie man das Diagramm zeigt, um es einfacher zu vermitteln". Wir haben die Rückmeldung erhalten, dass es wichtig ist, ein "intuitives Diagramm" zu erstellen, z. B. je höher die Preisspanne und je höher die Dichte und je dichter es ist. Ich habe auch gelernt, dass das Zeigen einer geschichtenähnlichen Schlussfolgerung zum Verständnis der anderen Partei führt. Ich werde mein zukünftiges Analyseleben damit verbringen, mir bewusst zu sein, wie ich die erzielten Ergebnisse mit echten Problemen in Verbindung bringen kann.

Recommended Posts

[Erste Datenwissenschaft ⑥] Ich habe versucht, den Marktpreis von Restaurants in Tokio zu visualisieren
Ich habe versucht, die Spacha-Informationen von VTuber zu visualisieren
[Erste Datenwissenschaft ⑤] Ich habe versucht, meinem Freund zu helfen, die erste Eigenschaft durch Datenanalyse zu finden
[Python] Ich habe versucht, die folgende Beziehung von Twitter zu visualisieren
Ich habe versucht, die Laufdaten des Rennspiels (Assetto Corsa) mit Plotly zu visualisieren
[Verarbeitung natürlicher Sprache] Ich habe versucht, die Bemerkungen jedes Mitglieds in der Slack-Community zu visualisieren
Ich habe versucht, den Trend der Anzahl der Schiffe in der Bucht von Tokio anhand von Satellitenbildern zu ermitteln.
Verwenden Sie PyCaret, um den Preis von Gebrauchtwohnungen in Tokio vorherzusagen!
Ich habe versucht, den allgemeinen Zustand der VTuber-Kanalbetrachter zu visualisieren
Ich habe versucht, die erste Frage der Mathematik-Aufnahmeprüfung 2019 der Universität Tokio mit Python Sympy zu lösen
Ich habe versucht, die Exponentialfunktion und die Logistikfunktion an die Anzahl der COVID-19-positiven Patienten in Tokio anzupassen
Ich habe versucht, die Altersgruppe und die Ratenverteilung von Atcoder zu visualisieren
Ich habe versucht, den Text des Romans "Wetterkind" mit Word Cloud zu visualisieren
Ich habe versucht, den Höhenwert von DTM in einem Diagramm anzuzeigen
Ich habe versucht, die Daten mit Zwietracht zu speichern
Ich habe versucht, die Trapezform des Bildes zu korrigieren
Ich habe versucht, die Texte von Hinatazaka 46 zu vektorisieren!
Ich habe versucht, die Tweets von JAWS DAYS 2017 mit Python + ELK einfach zu visualisieren
Ich habe versucht, die Daten des Laptops durch Booten unter Ubuntu zu retten
Ich habe versucht, die Grundform von GPLVM zusammenzufassen
Ich habe versucht, das Spiel in der J League vorherzusagen (Datenanalyse)
Ich habe versucht, die API von Sakenowa Data Project zu verwenden
Ich habe versucht, den negativen Teil von Meros zu löschen
Ich habe versucht, die Stimmen der Sprecher zu klassifizieren
Ich habe versucht, die String-Operationen von Python zusammenzufassen
Python-Übung 100 Schläge Ich habe versucht, den Entscheidungsbaum von Kapitel 5 mit graphviz zu visualisieren
Ich habe versucht, die Eigenschaften der neuen Informationen über mit dem Corona-Virus infizierte Personen mit Wordcloud zu visualisieren
Ich habe versucht, die Texte von GReeeen zu visualisieren, die ich in meiner Jugend verrückt gehört habe, aber nicht mehr gehört habe.
Ich habe versucht, die Entropie des Bildes mit Python zu finden
Versuchen Sie, COVID-19 Tokyo-Daten mit Python zu kratzen
[Pferderennen] Ich habe versucht, die Stärke des Rennpferdes zu quantifizieren
[Erste COTOHA-API] Ich habe versucht, die alte Geschichte zusammenzufassen
Ich habe versucht, die Standortinformationen des Odakyu-Busses zu erhalten
Ich habe versucht, mit TensorFlow den Durchschnitt mehrerer Spalten zu ermitteln
Ich habe versucht, den in Pandas häufig verwendeten Code zusammenzufassen
Ich habe versucht, die Zeit und die Zeit der C-Sprache zu veranschaulichen
Ich habe versucht, die im Geschäftsleben häufig verwendeten Befehle zusammenzufassen
Ich habe versucht, die Mail-Sendefunktion in Python zu implementieren
[TF] Ich habe versucht, das Lernergebnis mit Tensorboard zu visualisieren
[Maschinelles Lernen] Ich habe versucht, die Theorie von Adaboost zusammenzufassen
[Feier: Marktwert von 2 Billionen US-Dollar] Ich habe versucht, Apples Patent zu visualisieren
[Python] Ich habe versucht, Daten mit der API von Wikipedia zu sammeln
Ich habe versucht, das lokale Minimum der Goldstein-Preis-Funktion zu bekämpfen
Was ich bei der Analyse der Daten des Ingenieurmarktes gesehen habe
Ich habe versucht, das Blackjack of Trump-Spiel mit Python zu implementieren
Ich habe die Daten von Raspberry Pi an GCP gesendet (kostenlos)
Ich habe versucht, den Stromverbrauch meines Hauses mit Nature Remo E lite zu visualisieren
[Verarbeitung natürlicher Sprache] Ich habe diese Woche versucht, die aktuellen Themen in der Slack-Community zu visualisieren
Ich habe versucht, die Daten des Fußballturniers der FIFA Fussball-Weltmeisterschaft Russland mit Fußball zu analysieren
Ich schrieb einen Test in "Ich habe versucht, die Wahrscheinlichkeit eines Bingospiels mit Python zu simulieren".
Ich habe versucht, HULFT IoT (Edge Streaming) in das Gateway Rooster von Sun Electronics zu integrieren
Da die Aktie aufgrund des Einflusses des neuen Corona-Virus eingebrochen ist, habe ich versucht, die Performance meines Investment Trusts mit Python zu visualisieren.
Ich habe versucht, den Datenverkehr mit WebSocket in Echtzeit zu beschreiben
[Linux] Ich habe versucht, die Ressourcenbestätigungsbefehle zusammenzufassen