(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
->
완성!!