colors = ["red", "blue", "green"]
for color in colors:
print(color)
if "black" in colors:
print("True")
#리스트 안에 또 리스트가 있을 때 모든 아이템 출력
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>
위 결과에서 가격, 주소만 받아오기 위해 정규표현식 사용
.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:]
: 일정 시간이 걸리는 코드에서 진행경과를 눈에 보이게 나타내주는 모듈
#전체 가게 가격, 주소 가져오기
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
