최종 목표
총 51개의 페이지에서 각 가게의 정보를 가져온다.
- 가게 이름
- 대표 메뉴
- 대표 메뉴의 가격
- 가게 주소
서버는 정상적인데 서버가 문제 있다고 판단하고 차단한 것
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
response = urlopen(url)
response
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": "Chrome"}) # 어떤 웹 브라우저로 접근 할 것인가?
response = urlopen(req)
response.status
처음에는 그냥 접근해보고 문제가 생기면 headers 값을 주고 추가해보면 된다.
User-Agent 안에 있는 값을 랜덤하게 만들어주는 기능
여러 번 실행할 때마다 다른 환경으로 자동으로 만들어준다.
설치 - pip install fake-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()
ua.ie
# !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}) # 어떤 웹 브라우저로 접근 할 것인가?
response = urlopen(req)
response.status
잘 실행되는 모습을 볼 수 있다.
# !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태그에 "sammy" class를 가져오면 원하는 데이터를 가지고 올 수 있음을 알 수 있다.
# find_all
soup.find_all("div", "sammy"), len(soup.find_all("div", "sammy"))
# select
soup.select(".sammy"), len(soup.select(".sammy"))
전체 데이터를 가져오기 전 데이터 하나를 가져와서 확인한다.
tmp_one = soup.find_all("div", "sammy")[0]
tmp_one
type을 이용하여 BeautifulSoup 객체로 생성됐음을 확인 할 수 있다.
즉, BeautifulSoup에서 사용하는 find_all, select 등 메서드를 사용할 수 있음을 확인한다.
from urllib.parse import urljoin
import re
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 = tmp_one.find(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"])) # urljoin : 있으면 join 안하고, 없으면 알아서 붙여줌
웹 페이지에서 가져온 데이터로 DataFrame 생성
import pandas as pd
data = {
"Rank": rank,
"Menu": main_menu,
"Cafe": cafe_name,
"URL": url_add,
}
df = pd.DataFrame(data)
df
# 컬럼 순서 변경
df = pd.DataFrame(data, columns=["Rank", "Cafe", "Menu", "URL"])
df.tail(2)
# 데이터 저장
df.to_csv(
"../data/03. best_sandwiches_list_chicago.csv", sep=",", encoding="utf-8"
)
예제 3에서 가져온 데이터에서 URL을 찾아 들어가 해당 페이지 가게의 메뉴 가격, 가게 주소를 가져오자.
# requirements
import pandas as pd
from urllib.request import urlopen
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
df = pd.read_csv("../data/03. best_sandwiches_list_chicago.csv", index_col=0)
df.tail(2)
df["URL"][0]
ua = UserAgent()
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") # 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]
price_tmp
tmp = re.search("\$\d+\.(\d+)?", price_tmp).group()
tmp
tmp = re.search("\$\d+\.(\d+)?", price_tmp).group()
price_tmp[len(tmp) + 2:] # 가격 길이 끝난 부분부터 맨 끝까지
tqdm 설치 conda install -c conda-forge tqdm
from tqdm import tqdm
import re
price = []
address = []
for idx, row in tqdm(df.iterrows()):
req = Request(row["URL"], headers={"user-agent":ua.ie})
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)
강의에서는 위와 같이 코드했을 때 잘 작동했는데 내가 했을 땐 실행할 때마다 계속 403 상태 에러가 나타나서 그냥 UserAgent 사용하지 않고 "Crome"이라 작성하고 사용했다.
(idx가 출력되면서 2, 30, 5, 44 등등 랜덤 타이밍에 해당 줄에서 에러가 나길래 ua.ie가 문제라고 생각함)
req = Request(row["URL"], headers={"user-agent":"Crome"}) # ua.ie
df["Price"] = price
df["Address"] = address
df = df.loc[:, ["Rank", "Cafe", "Menu", "Price", "Address"]] # 행은 전체 선택, URL 컬럼 제외(최종적으로 필요없음)
df.set_index("Rank", inplace=True)
df.head()
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)
# requirements
import folium
import pandas as pd
import numpy as np
import googlemaps
from tqdm import tqdm
df = pd.read_csv("../data/03. best_sandwiches_list_chicago2.csv", index_col=0)
df.tail(10)
gmaps_key = "API키 입력"
gmaps = googlemaps.Client(key=gmaps_key)
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)
df["lat"] = lat
df["lng"] = lng
df.tail()
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"],
).add_to(mapping)
mapping
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(
icon="coffee",
prefix="fa"
)
).add_to(mapping)
mapping
웹 데이터 분석 예제를 몇 가지 해보고 나니 재밌다는 생각이 들었다.
웹에서 원하는 데이터를 가져오는 것도, 데이터 프레임에 저장하는 것도, 지도에 시각화하는 것도 다 재밌다.
처음 하는 작업이라 어렵기도 하고 아직 이해가 안 되는 부분도 있고 혼자서 하려면 물론 아직 제대로 못하겠지만 흥미롭고 뿌듯하다.👍웹 프로그래밍 프로젝트 때 같은 팀원이 지도에서 맛집 데이터를 가져왔었는데 이렇게 한 게 아닐까 싶으면서 이해가 되고 있다!! 재밌다.😊
"이 글은 제로베이스 데이터 취업 스쿨 강의 자료 일부를 발췌한 내용이 포함되어 있습니다."