1. 필요한 라이브러리, 모듈 임포트
# 필요한 라이브러리, 모듈 import
import urllib.request import Request, urlopen
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
Python
복사
특정 페이지에서 일반적으로 urlopen(url) 을 하게 되면 위의 403 error 를 발생시키는 경우가 있다.
이 해결 방법으로 아래와 같은 Request(), useragent 작업을 해줘야 한다.
2. 문제 없이 페이지 크롤링해오기 - Request(), 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.ie})
html = urlopen(req)
# 상태를 확인해보면 200 으로 정상적으로 요청하고 받은 것으로 확인할 수 있다.-> html.status
soup = BeautifulSoup(html, 'html.parser')
print(soup.prettify()) # 제대로 불러와진 html 확인
Python
복사
3. 원하는 부분 가져오기
from urllib.parse import urljoin # 하위 페이지에 url_base가 제대로 붙여져 있으면 그대로 가져오고, 없다면 붙여서 url_add 에 넣어주기 위해 필요한 모듈
import re # 정규표현식 모듈 가져오기
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(class_='sammyListing').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'])) # 링크
Python
복사
‘\n’ 이나 ‘\r\n’ 을 기준으로 문자들을 모두 분리해준다.
url_base 가 item.find(’a’)[’href’] 에 포함되어 있으면 그대로 가져오고, 없다면 url_base 를 붙여주는 기능
4. 원하는 부분 추출한 데이터 → 데이터프레임 형태로 만들어주기
# 만들어진 리스트를 데이터프레임으로 만들어주기
import pandas as pd
data = {
'Rank': rank,
'Main Menu' : main_menu,
'Cafe Name' : cafe_name,
'URL' : url_add
}
df = pd.DataFrame(data)
# 만든 데이터프레임 csv 파일로 저장하기
df.to_csv('../data/03. best_sandwiches_list_chicago.csv', sep=',', encoding='utf-8')
Python
복사
5. 하위 페이지 데이터 가져오기 - price, address
# 위의 저장한 df 다시 가져오기
df = pd.read_csv('../data/03. best_sandwiches_list_chicago.csv', index_col=0)
Python
복사
# 하위 페이지 소스 가져오기
from tqdm import tqdm # 진행 정도를 확인하기 위한 모듈
price = [] # 가격 정보를 넣어줄 리스트
address = [] # 주소 정보를 넣어줄 리스트
for idx, row in tqdm(df.iterrows()):
# 요청하고 받기
req = Request(row['URL'], headers={'user-agent':ua.ie})
html = urlopen(req)
soup_tmp = BeautifulSoup(html, 'html.parser')
# 크롤링해오기
gettings = soup_tmp.find('p','addy').text
price_address = re.split('.,', gettings)[0]
tmp = re.search("\$\d+\.(\d+)?", price_address).group() # 정규표현식으로 원하는 데이터만 추출하기
price.append(tmp)
address.append(price_address[len(tmp) + 2:])
print(idx)
Python
복사
반복문을 돌리는 경우, 진행 상황이나 시간이 얼마나 걸리는지 등의 정보를 확인할 수 있는 모듈이다.
“” 안에 넣어준 정규표현식을 충족하는 문자만 찾아준다. group() 을 넣어줘야 원하는 값을 출력할 수 있다.
ex) \$\d+\.(\d+)? 가 의미하는 것
•
\$ : 달러가 포함되어야 하고,
•
\d+ : 숫자가 포함되어야 하는데, 그 크기는 상관이 없고
•
\. : ‘ . ’ 을 포함해야 한다.
•
( )? : ( ) 안에 있는 내용은 포함되어도 되고, 포함되지 않아도 된다.
6. 데이터프레임으로 합쳐주고 다시 저장해주기
# 위의 가격 정보와 주소 정보를 리스트에 담았으니
# 기존 데이터프레임에 변수로 더해주자.
df['Price'] = price
df['Address'] = address
# 필요없는 url 필드는 빼고 저장
df = df.iloc[:,[0,1,2,4,5]]
# Rank 필드를 index 로 설정
df.set_index('Rank', inplace=True)
# csv 파일로 다시 저장
df.to_csv('../data/03. best_sandiwiches_list_chicago2.csv', sep=',', encoding='utf-8')
Python
복사
7. 지도에 샌드위치 맛집 50군데 위치 표시하기
# 필요한 라이브러리, 모듈 임포트
import folium
import numpy as np
import googlemaps
Python
복사
# 시카고 맛집 데이터 다시 불러오기
df = pd.read_csv('../data/03. best_sandiwiches_list_chicago2.csv', index_col=0)
Python
복사
주소 필드 활용해서 위도, 경도 데이터를 가져오기
gmaps_key = 'AIzaSyD8GhzmjZ2g4p4ZOWv8zMBEG7IoruaGIoc'
gmaps = googlemaps.Client(gmaps_key)
Python
복사
lat = [] # 위도 정보 담을 리스트
lng = [] # 경도 정보 담을 리스트
for idx, row in tqdm(df.iterrows()):
# Address 주소가 여러개일 경우, 'Multiple location' 으로 되어 있다. -> 빼주고 진행
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)
Python
복사
# 기존 데이터프레임에 위도, 경도 필드로 더해주자
df['lat'] = lat
df['lng'] = lng
Python
복사
# 지도에 마커 표시하기
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 Name'],
tooltip=row['Main Menu'],
icon = folium.Icon(
icon='coffee',
prefix='fa'
)
).add_to(mapping)
Python
복사