[EDA] 03. 시카고 맛집 페이지 분석

쩡이·2023년 8월 9일

EDA

목록 보기
7/12

Python List 데이터형

  • list형은 대괄호로 생성
colors = ["red", "blue", "green"]
  • list형을 반복문(for)에 적용
for color in colors:
    print(color)
  • in명령으로 조건문(if) 적용
if "black" in colors:
    print("True")
  • append : list에서 제일 뒤에 추가, 하나의 아이템만 추가하므로 리스트형이 오면 리스트 형태로 추가됨
  • pop : list에서 제일 뒤부터 삭제
  • extend : list에서 제일 뒤에 자료 추가, 리스트형이 오더라도 원소 하나씩 추가됨
  • remove("item명") : 원하는 item 삭제
  • 슬라이싱 : [n:m] n번째부터 m-1까지
  • insert(넣을 위치 인덱스, 넣을 문자)
  • isinstance: 자료형 True/False
#리스트 안에 또 리스트가 있을 때 모든 아이템 출력
for each_item in favorite_movies:
    if isinstance(each_item, list):
        for nested_item in each_item:
            print("nested_item", nested_item)
    else:
        print("each_item", each_item)

시카고 맛집 페이지 데이터 분석

https://www.chicagomag.com/chicago-magazine/november-2012/best-sandwiches-chicago/

위 사이트에 접속하여 50개의 페이지에서 아래 정보를 가져오고, DataFrame으로 저장하고,
지도 시각화 해보기

메인페이지와 하위페이지(메뉴 상세페이지)로 나눠서 정보를 얻는다

  • 가게이름
  • 대표메뉴
  • 대표메뉴의 가격
  • 가게주소

메인페이지

from urllib.request import Request, urlopen
from bs4 import BeautifulSoup

url_base = "https://www.chicagomag.com/"
url_sub = "chicago-magazine/november-2012/best-sandwiches-chicago/"
url = url_base + url_sub
req = Request(url, headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0"})
html = urlopen(req)
soup = BeautifulSoup(html, "html.parser")
print(soup.prettify())

#user-agent value 값을 간단하게 Chrome으로 표현할 수도 있음 
# req = Request(url, headers={"User-Agent":"Chrome"}) 
#일단 headers값이 없이 url만 넣고 접속해보고, 오류가 나면 headers를 넣어준다

#UserAgent 모듈을 이용하는 방법
#설치 : pip install fake-useragent 
from urllib.request import Request, urlopen
from fake_useragent import UserAgent
from bs4 import BeautifulSoup

url_base = "https://www.chicagomag.com/"
url_sub = "chicago-magazine/november-2012/best-sandwiches-chicago/"
url = url_base + url_sub
ua = UserAgent()
req = Request(url, headers={"User-Agent":ua.ie}) 
html = urlopen(req)
soup = BeautifulSoup(html, "html.parser")
print(soup.prettify())

우리가 찾고자 하는 정보 : <div class="sammy"> 형태

soup.find_all("div", class_="sammy") ,len(soup.find_all("div", class_="sammy"))
#soup.select(".sammy"), len(soup.select(".sammy"))
#len 함수를 써서 모든 가게 정보가 나왔는지 확인

#50개 가게 중, 첫번째 가게로 테스트함
tmp_one = soup.find_all("div", "sammy")[0]
type(tmp_one) 
#tmp_one의 결과가 bs4.element.Tag로 BeautifulSoup 객체라서 find, find_all 같은 매서드 사용가능

#순위
tmp_one.find(class_="sammyRank").get_text()
#tmp_one.select_one(".sammyRank").text

#메뉴, 카페 이름
tmp_one.find("div", {"class":"sammyListing"}).get_text()
#tmp_one.select_one(".sammyListing").text

(결과)
'BLT\nOld Oak Tap\nRead more '
메뉴, 카페 이름이 한 문장에 같이 나와있으므로 이를 분리해야함

import re

tmp_string = tmp_one.find("div", {"class":"sammyListing"}).get_text()
re.split(("\n|\r\n"), tmp_string)
print(re.split(("\n|\r\n"), tmp_string)[0]) #메뉴이름
print(re.split(("\n|\r\n"), tmp_string)[1]) #카페이름

#링크(상대주소)
tmp_one.find("a")["href"]
#tmp_one.select_one("a").get("href")
from urllib.parse import urljoin
#urljoin : 상대주소, 절대주소 둘 다 받을 수 있고 상대주소이면 절대주소로 바꿔줌

url_base = "https://www.chicagomag.com/"

rank = []
main_menu = []
cafe_name = []
url_add = []

list_soup = soup.find_all("div", "sammy") #soup.select(".sammy")
for item in list_soup:
    rank.append(item.find(class_="sammyRank").text) 
    tmp_string = item.find("div", {"class":"sammyListing"}).get_text()
    main_menu.append(re.split(("\n|\r\n"), tmp_string)[0])
    cafe_name.append(re.split(("\n|\r\n"), tmp_string)[1])
    url_add.append(urljoin(url_base, item.find("a")["href"])) 
    
    
# 위 값을 DataFrame으로 만든다
import pandas as pd

data = {
    "Rank" : rank,
    "Menu" : main_menu,
    "Cafe" : cafe_name,
    "URL" : url_add
}
# data
df = pd.DataFrame(data)


#컬럼 순서 변경
df = pd.DataFrame(data, columns=["Rank", "Cafe", "Menu", "URL"])

# 데이터 저장
df.to_csv(
    "../data/03. best_sandwiches_list_chicago.csv", sep=",", encoding="utf-8"
)


하위페이지

하위페이지(메뉴 상세페이지)에서 가격, 주소를 가져와서 df와 합친다.

#필요조건
#import만 쓰는 것을 제일 상단에 써주고 from이 필요한 것은 밑에 쓰는게 문법상 예쁜것
import pandas as pd
from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from bs4 import BeautifulSoup

df = pd.read_csv("../data/03. best_sandwiches_list_chicago.csv", index_col=0)

메뉴 상세페이지에서 가격, 가게주소 가져오기
이 때, 상세페이지 주소가 필요한데 이를 위해서 url을 만들어둠
또, 가격,주소 정보는 <p class="addy">에 있음

#첫번째 가게로 테스트
df.URL[0]
req = Request(df["URL"][0], headers={"user-agent":ua.ie})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, "html.parser")
soup_tmp.find("p", "addy")

(결과)
<p class="addy">
<em>$10. 2109 W. Chicago Ave., 773-772-0406, <a href="http://www.theoldoaktap.com/">theoldoaktap.com</a></em></p>

위 결과에서 가격, 주소만 받아오기 위해 정규표현식 사용

정규 표현식(Regular Expression) 기초

.x : x가 마지막으로 끝난다
x+ : x가 1번 이상 반복
x? : x가 있거나 없거나
x* : x가 0번 이상 반복
x|y : or 연산 x 또는 y를 찾는다

price_tmp = soup_tmp.find("p", "addy").text
price_tmp
(결과)
'\n$10. 2109 W. Chicago Ave., 773-772-0406, theoldoaktap.com'


import re #regular expression 모듈 split을 쓰기위해
re.split(".,", price_tmp)
(결과)
['\n$10. 2109 W. Chicago Ave', ' 773-772-040', ' theoldoaktap.com']

price_tmp = re.split(".,", price_tmp)[0]
price_tmp
(결과)
'\n$10. 2109 W. Chicago Ave'

#가격
re.search("\$\d+\.(\d+)?", price_tmp).group()
#$다음에 숫자가 연달아 나오고 .이 꼭 나오고, 그 뒤로 숫자가 있을 수도 없을 수도 있다. group()으로 받아온다

#주소 : 가격이 끝나는 위치를 생각해서 주소를 받아온다
# 가격에서 2번 뒤로가면 주소가 나옴
tmp = re.search("\$\d+\.(\d+)?", price_tmp).group()
price_tmp[len(tmp)+ 2:]

tqdm 모듈

: 일정 시간이 걸리는 코드에서 진행경과를 눈에 보이게 나타내주는 모듈

#전체 가게 가격, 주소 가져오기
from tqdm import tqdm

price = []
address = []

for idx, row in tqdm(df.iterrows()):
    req = Request(row["URL"], headers={"User-Agent":"Chrome"})
    html = urlopen(req).read()
    soup_tmp = BeautifulSoup(html, "html.parser")

    gettings = soup_tmp.find("p", "addy").get_text()
    price_tmp = re.split(".,", gettings)[0]
    tmp = re.search("\$\d+\.(\d+)?", price_tmp).group()
    price.append(tmp)
    address.append(price_tmp[len(tmp)+ 2:])
    print(idx)

tqdm을 이용하면 아래와 같이 나옴


# 원본 데이터에 가격, 주소 합치기
#url정보는 가격, 주소를 가져오기 위해 가져온 것이므로 없앴음
df["Price"] = price
df["Address"] = address
df = df.loc[:,["Rank", "Cafe", "Menu", "Price", "Address"]]
df.set_index("Rank", inplace=True)
df.head()


#저장
df.to_csv("../data/03. best_sandwiches_list_chicago2.csv",sep=",", encoding="UTF-8")

시카고 맛집 데이터 지도 시각화

# 필요조건 가져오기
import folium
import pandas as pd
import numpy as np
import googlemaps
from tqdm import tqdm

#DataFrame 가져오기
df = pd.read_csv("../data/03. best_sandwiches_list_chicago2.csv", index_col=0)

#gmaps 키값
gmaps_key = ""
gmaps = googlemaps.Client(key=gmaps_key)

#주소를 gmaps에 넣어서 위도, 경도 값을 받아온다
lat = []
lng = []

for idx, row in tqdm(df.iterrows()):
    if not row["Address"] == "Multiple location":
        target_name = row["Address"] + "," + "Chicago"
        gmaps_output = gmaps.geocode(target_name)
        location_output = gmaps_output[0].get("geometry")
        lat.append(location_output["location"]["lat"])
        lng.append(location_output["location"]["lng"])
    else:
        lat.append(np.nan)
        lng.append(np.nan)


지도 시각화

mapping = folium.Map(location=[41.8781136, -87.6297982], zoom_start=11)

for idx, row in df.iterrows():
    if not row["Address"] == "Multiple location":
        folium.Marker(
            location=[row["lat"], row["lng"]],
            popup=row["Cafe"],
            tooltip=row["Menu"],
            icon=folium.Icon(
                color="green",
                icon="coffee",
                prefix="fa"
            )
        ).add_to(mapping)

mapping

0개의 댓글