시카고 맛집 데이터 분석

eunbi kim·2024년 3월 18일
0
post-custom-banner

The 50 Best Sandwiches in Chicago

(https://www.chicagomag.com/chicago-magazine/november-2012/best-sandwiches-chicago/) <- 이 웹페이지에는 총 50개의 샌드위치 가게 정보가 있다.

우리의 목표는 50개 가게 정보를 다 가져오는 것이다.
(가게 이름, 대표메뉴, 대표메뉴의 가격, 가게 주소)

너무... 맛있어 보여...

하...

개발자도구로 크롤링하면 좋을 태그들을 알 수 있다.

"sammy"라는 클래스 안에, sammyRank 클래스에 순위가 있고,
sammyListing 안에는 링크 정보와 메뉴 이름, 가게 이름이 들어있다.
이 부분을 크롤링하면 될 것 같다.

BeautifulSoup으로 url주소 넘겨주어 html 태그들을 가져오자~

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
url

# 403 error 방지
req = Request(url, headers={"user-agent":"Chrome"})
html = urlopen(req)

soup = BeautifulSoup(html, "html.parser")
print(soup.prettify())

-> 무수한 html태그들

len(soup.find_all("div", "sammy"))

-> 50

50개가 잘 확인되었다.

soup.find_all("div", "sammy")[0]

->

이걸 가지고 순위, 메뉴 이름, 가게 이름, 링크를 가져올 수 있다.

쓸데없는 글자 지워주기:

import re

tmp_string = tmp_one.find(class_="sammyListing").get_text()
re.split(("\n"), tmp_string)

-> ['BLT', 'Old Oak Tap', 'Read more ']

인덱스 번호로 가져올 수 있다.

print(re.split(("\n"), tmp_string)[0])
print(re.split(("\n"), tmp_string)[1])

-> BLT
Old Oak Tap

이제 이 방식을 가지고 for문으로 50개 전체 데이터에 적용하자!!
리스트로 하나씩 컬럼을 만들고, DataFrame으로 합치는 구문이다.

from urllib.parse import urljoin

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

# 필요한 내용을 담을 빈 리스트

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

list_soup = soup.find_all("div", "sammy")

for item in list_soup:
    rank.append(item.find(class_="sammyRank").get_text())
    tmp_string = item.find(class_="sammyListing").get_text()
    main_menu.append(re.split(("\n"), tmp_string)[0])
    cafe_name.append(re.split(("\n"), tmp_string)[1])
    url_add.append(urljoin(url_base, item.find("a")["href"]))  # urljoin <- 상대주소 불러옴을 방지
import pandas as pd

data = {
    "Rank":rank,
    "Cafe":cafe_name,
    "Menu":main_menu,
    "URL":url_add
}

df = pd.DataFrame(data)
df.tail(2)

데이터프레임으로 정리하였다~~~


이제 각 50개 가게의 url을 가져왔고,
링크 타고 들어가 각각의 가격, 가게 주소를 가져와야 한다.

이렇게 메뉴에 대한 설명과
가격, 주소, 전화번호 정보가 있다.

원하는 태그를 파악하고,

req = Request(df["URL"][0], headers={"user-agent":"Chrome"})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, "html.parser")
soup_tmp.find("p", "addy")

->

추출해왔다.

price_tmp = soup_tmp.find("p", "addy").text
price_tmp

-> '\n$10. 2109 W. Chicago Ave., 773-772-0406, theoldoaktap.com'
요런 형식으로 나옴.

import re
re.split(".,", price_tmp) 

-> ['\n$10. 2109 W. Chicago Ave', ' 773-772-040', ' theoldoaktap.com']

가격 부분 추출 어케 하지 <- 정규식!!

처음엔 달러로 시작하고, 숫자가 오고, .이 오고, 그 뒤엔 숫자가 올수도 안올수도~ 라는 식
-> $\d+.(\d+)?
의 형태이다!

group() <-값만 추출해준다.

re.search("\$\d+\.(\d+)?", price_tmp).group() 

'$10.'

가격 부분(10달러)만 잘 추출되었다.

그 다음 정보인 주소는 두 칸 띄어서 나오는 값이 출력되는 구조이다.

tmp = re.search("\$\d+\.(\d+)?", price_tmp).group() 
price_tmp[len(tmp) + 2:]

-> '2109 W. Chicago Ave'

이제 이 과정을 for문으로 돌면 된다.

from tqdm import tqdm

price = []
address = []

for idx, row in 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").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:])

price, address를 df에 추가해주고, 컬럼 위치와 인덱스를 rank로 조정하였다.

df["Price"] = price
df["Address"] = address

df = df.loc[:, ["Rank", "Cafe", "Menu", "Price", "Address"]]
df.set_index("Rank", inplace=True)
df.head()

ㅎㅎ


시카고 지도 위에 맛집 위치를 표시하는
지도 시각화를 해 보자!!

-> 구글맵에 address를 떤져서 lat, lng 좌표 받고,
folium으로 지도에 marker를 표시!

import folium
import pandas as pd

# gamps변수 는 api key임
gmaps = googlemaps.Client(key=gmaps_key)

위치를 찾을 주소를 만들어주고
gmaps의 geocode에다 이 이름들 넣어서 좌표값 가져오기.
리스트 값이므로 0번째 선택 후, "geomatry"의 "lat", "lng"만 가져와야 한다.

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)

이제 위도/경도 컬럼을 추가해주고.

df["lat"] = lat
df["lng"] = lng

Folium으로 지도 시각화:

도화지에 시카고 지도를 띄우고...

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"],
            icon=folium.Icon(
                icon="coffee",
                prefix="fa"
            )
        ).add_to(mapping)

mapping

->

완성!!

post-custom-banner

0개의 댓글