from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
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()
ua.ie
# 'User-Agent':'Chrome'
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
req = Request(url, headers={'User-Agent':ua.ie})
html = urlopen(req)
# html.status
soup = BeautifulSoup(html, 'html.parser')
print(soup.prettify())
사이트가 있는 서버에 접근하여 모바일인지 PC인지 맥환경인지에 따라서 맞춤 데이터를 제공 하기위해 헤더 값에 사용자의 데이터 값을 추가하여 크롤링을 하였습니다..
from urllib.parse import urljoin
url_base = 'https://www.chicagomag.com/'
# 필요한 내용을 담을 빈 리스트
# 리스트로 하나씩 칼럼을 만들고 DaraFrame으로 합칠 예정
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())
url_add.append(urljoin(url_base, item.find('a')['href']))
tmp_string = item.find('div', class_='sammyListing').text
cafe_name.append(re.split(('\n|\r\n'), tmp_string)[1])
main_menu.append(re.split(('\n|\r\n'), tmp_string)[0])
시카고 맛집 데이터를 각각의 리스트에 추가해줍니다.
data = {
'Rank' : rank,
'Menu' : main_menu,
'Cafe' : cafe_name,
'URL' : url_add
}
df = pd.DataFrame(data)
df.head()
이것을 데이터 프레임으로 만들어줍니다.
import re
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').get_text()
price_tmp = re.split('.,', gettings)[0]
# $는 특수 문자이기 때문에 이스케이프 문자를 앞에 사용해줌
# 달러 뒤의 숫자와 소숫점이후 숫자까지와 주소를 분리해줌
# 이후 group 함수로 분리한 달러와 가격을 합쳐줌
tmp = re.search('\$\d+\.(\d+)?', price_tmp).group()
price.append(tmp)
# 가격 이후 부터 뒷내용을 제외 해줌 + 2 인 이유 공백과 '.'의 존재 때문
address.append(price_tmp[len(tmp) + 2:])
print(idx)
위에서 찾은 URL 컬럼에 접근하여 해당 페이지에 존재하는 메뉴의 가격과 가게 주소를 불러와 줍니다. 여기서 re라이브러리를 사용하는데 자세한 내용은 하단링크를 참고바람
여기서는 \n$10. 2109 W. Chicago Ave' 와 같은 출력값에서 $가 특수 문자인 것과 10달러로 딱 떨어지는 경우가 아닌 뒤에 소수점이 붙을것을 염두하여 위와 같이 처리하였다.
re 라이브러리
이후
df['Price'] = price
df['Address'] = address
df = df.loc[:, ['Rank', 'Cafe', 'Menu', 'Price', 'Address']]
df.set_index('Rank', inplace=True)
df.head()
아까 만든 데이터프레임에 가격과 주소 항목을 추가해주고 URL 컬럼을 제거해 줍니다.
import folium
import numpy as np
import googlemaps
lat = []
lng = []
for idx, row in tqdm(df.iterrows()):
if not row['Address'] == 'Multple location':
target_name = row['Address'] + ', ' + 'Chicago'
# print(target_name)
gmaps_output = gmaps.geocode(target_name)
# print(gmaps_output)
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)
시각화를 위해 가게이름의 경도 위도 값을 구글맵 라이브러리를 사용하여 불러와 줍니다.
그런데 해당 가게가 여러 체인점일 경우에는 NAN을 대신 채워주고 아닌경우에는 구글 맵에있는 값을 추가해줍니다.
mapping = folium.Map(location=[41.8781136, -87.6297982], zoom_start=11)
for idx, row in df.iterrows():
folium.Marker(
location=[row['lat'], row['lng']],
popup=row['Cafe'],
tooltip=row['Menu'],
icon=folium.Icon(
icon='coffee',
prefix='fa'
)
).add_to(mapping)
mapping
이제 folium 라이브러리를 사용하여 시카고 지도에 가게를 표시해줍니다.