[Zero-Base DS]스터디노트_웹데이터 분석(03)

HAHAHAEUN·2024년 4월 9일
post-thumbnail

주요 학습내용

1. 웹 스크래핑/웹 크롤링

2. 데이터 시각화

  • 최종 목표

    • 총 51개 페이지에서 각 가게의 정보를 가져온다
    • 가게이름/대표메뉴/대표메뉴 가격/가게 주소

1. 요청할 페이지 가져오기

from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
import pandas as pd

url_base =  "https://www.chicagomag.com/"
url_sub = "chicago-magazine/november-2012/best-sandwiches-chicago/"
url = url_base + url_sub


response = urlopen(url)
response.status

이렇게 불러오면 아래와 같은 오류 코드 발생

  • 웹 서버가 사용자의 요청을 인식하지만 추가적인 액세스를 허용할 수 없는 경우에 표시 됨
  • 일반적으로 서버 측에서 충분한 권한이나 인증 자격 증명이 없는 경우에 발생
  • 오류 해결 방법: headers = {} 입력

방법 1) 개발자도구에서 가져오기

# 정식으로는
# req = Request(url, headers={"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"})
req = Request(url, headers={"user-agent": "Chrome"})
response = urlopen(req)
response.status

방법 2) fake_useragent 설치

  • 인터넷 익스플로어 : ua.ie
  • 크롬 : ua.chrome
from fake_useragent import UserAgent

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.chrome})
response = urlopen(req)
response.status

2. for문 사용하여 데이터 추출

from urllib.parse import urljoin

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

# 필요한 내용을 담을 빈 리스트 생성
# 리스트로 하나씩 컬럼을 만들고, DataFrame으로 합칠 예정
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").get_text())
    tmp_string = item.find(class_= "sammyListing").get_text()
    main_menu.append(re.split(("\n|\r\n"), tmp_string)[0])  # 인덱스 0:메뉴
    cafe_name.append(re.split(("\n|\r\n"), tmp_string)[1])  # 인덱스 1:카페이름
    url_add.append(urljoin(url_base, item.find("a")["href"]))
# 모두 잘 가져와졌는지 check
len(rank), len(main_menu), len(cafe_name), len(url_add)
rank[:5], main_menu[:5], cafe_name[:5], url_add[:5]

3. DataFrame으로 만들어주기

import pandas as pd

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

}

df = pd.DataFrame(data)

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

4. 가격 & 주소 데이터 불러오기

  • 가격은 각url별로 따로 불러와야하기 때문에 다시 request 지정
# requirement
import pandas as pd
from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
# 문법상 IMPORT문 가장 최상단에 쓰고, from문 쓰는것이 좋음

# 인덱스 하나 지정하여, 추출하고자하는 데이터 위치 파악
df["URL"][0]

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

# regular expression(정규화)
price_tmp = soup_tmp.find("p", "addy").text
price_tmp

import re
re.split(".,", price_tmp)
# 재분리

price_tmp = re.split(".,", price_tmp)[0]


# 가격
tmp = re.search("\$\d+\.(\d+)?",price_tmp).group()
# 달러 + 숫자 + 그뒤로는 숫자가 올수도 있고 안올수도 있다

# 주소
price_tmp[len(tmp)+2:]

5. 반복문(iterrows)

# 전체
from tqdm import tqdm

price = []
address = []

for idx, row in tqdm(df.iterrows()):
    # 넘버링 해줄 필요 없어짐
    req = Request(row["URL"], headers={"user-agent": ua.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)
  • 전체 데이터 다 불러왔는지 check
len(price), len(address)

price[:5], address[:5]

6. DataFrame에 추가

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

7. 파일 저장 및 확인

df.to_csv("../data/03. best_sandwiches_list_chicago2.csv",sep=",", encoding = "UTF-8")
pd.read_csv("../data/03. best_sandwiches_list_chicago2.csv", index_col=0)

8. 시각화

1) requirements

# requirements

import folium
import pandas as pd
import numpy as np
import googlemaps
from tqdm import tqdm

2) 구글맵 데이터 가져오기

gmaps_key = "개인별 키값 입력"
gmaps = googlemaps.Client(key=gmaps_key)

3) 반복문 사용하여 위도, 경도 추출(iterrows)

lat = []
lng = []

for idx, row in tqdm(df.iterrows()):
    if not row["Address"] == "Multiple location":
        target_name = row["Address"] + ", " + "Chicago"
        # print(target_name)
        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)

4) 기존 DataFrame에 lat, lng추가

df["lat"] = lat
df["lng"] = lng
df.tail()

5) 지도 불러오기

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

6) Marker 찍어주기

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

mapping

[자료 출처] 제로베이스 데이터 스쿨

profile
할 거면 제대로 하자

0개의 댓글