비트교육센터/파이썬

[비트교육센터] 자바 기반 풀스택 개발자 양성 과정(Bigdata) 8일차

달의요정루나 2023. 6. 12. 20:51

1. Police(서울시 범죄 현황)

- 해당 게시물에서 이어진다.

https://julian5383.tistory.com/153

 

[비트교육센터] 자바 기반 풀스택 개발자 양성 과정(Bigdata) 7일차

1. Seoul_CCTV - 서울의 CCTV데이터로 데이터 분석을 하려고 한다. 1) 코딩문 [1] - source폴더에 새로운 폴더 practice를 선언한다. - 그리고 그안에 data폴더를 선언한다. [2] - data폴더에 파일 2개를 넣는다. 2

julian5383.tistory.com

 

1) 코딩문

[1]

result_cctv=pd.read_csv('data/cctv_pop_seoul.csv',index_col='구별')
#data폴더에서 cctv_pop_seoul.csv 파일을 가져오고, 구별열을 인덱스로 지정한다.

result_cctv.head()
#상위 5개 데이터를 출력한다.

--> 결과

[2]

crime_anal_norm[['인구수','cctv']]=result_cctv[['인구수','총계']]
#crime_anal_norm 데이터프레임에 새로운 칼럼을 추가한다.
#result_cctv에 있는 인구수, 총계 값을 crime_anal_norm의 새로운 컬럼에 대입한다.

crime_anal_norm.head()
#상위 5개의 데이터를 출력한다.

--> 결과

[3]

col=['강간','강도','살인','절도','폭력']
#칼럼명을 리스트로 묶는다.

crime_anal_norm['범죄']=np.sum(crime_anal_norm[col], axis=1)
#범죄라는 새로운 칼럼을 만든다. 그리고 각 칼럼마다 해당하는 값들을 더해준다.
#칼럼 한줄 전체에 해당값을 넣는다.

crime_anal_norm.head()
#상위 5개의 값을 출력한다.

--> 결과

[4]

col=['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
#칼럼명을 리스트로 묶어준다.

crime_anal_norm['검거']=np.sum(crime_anal_norm[col], axis=1)
#검거라는 새로운 칼럼을 만든다. 그리고 각 칼럼마다 해당하는 값들을 더해준다.
#칼럼 한줄 전체에 해당값을 넣는다.

crime_anal_norm.head()
#상위 5개 데이터를 출력한다.

--> 결과

2) 코딩문

[1]

import matplotlib.pyplot as plt
import seaborn as sns
import platform
from matplotlib import font_manager, rc # Runtime Configuration

path = "c:/Windows/Fonts/malgun.ttf"
if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~~')
sns.pairplot(crime_anal_norm, vars=['강도','살인','폭력'],kind='reg',height=3)
#crime_anal_norm의 값에 따라 그래프를 생성한다.
#vars: 강도, 살인, 폭력 칼럼으로만 그린다.
#kind: 그래프의 종류를 지정한다. reg는 회귀선 그래프 중심으로 그리는 것이다.
#height: 그래프의 높이를 지정한다.

plt.show()
#그래프를 출력한다.

--> 결과

[2]

sns.pairplot(crime_anal_norm, x_vars=['인구수','cctv'],
            y_vars=['살인','강도'],kind='reg',height=3)
#x_vars, y_vars: x축과 y축에 어떤 데이터를 배치할지 정한다.

plt.yticks(fontname='Arial')
#yticks(): y축의 눈금을 표시한다. fontname을 넣어 글꼴을 설정할수 있다.

plt.show()
#그래프를 출력한다.

--> 결과

[3]

sns.pairplot(crime_anal_norm, x_vars=['인구수','cctv'],
            y_vars=['살인검거율','강도검거율'],kind='reg',height=3)
plt.yticks(fontname='Arial')
plt.show()

--> 결과

[4]

sns.pairplot(crime_anal_norm, x_vars=['인구수','cctv'],
            y_vars=['절도검거율','강도검거율'],kind='reg',height=3)
plt.yticks(fontname='Arial')
plt.show()

--> 결과

3) 코딩문

[1]

crime_anal_norm.head()

--> 결과

[2]

tmp_max=crime_anal_norm['검거'].max()
#검거 칼럼의 최대값부터 반환한다.

crime_anal_norm['검거']=crime_anal_norm['검거']/tmp_max*100
#검거 칼럼에 전체검거비율을 입력한다.

crime_anal_norm_sort=crime_anal_norm.sort_values(by='검거',ascending=False)
#검거 칼럼을 기준으로 내림차순으로 정렬한다.

crime_anal_norm_sort.head()
#새롭게 정렬된 값의 5줄을 출력한다.

--> 결과

[3]

target_col=['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
#입력할 칼럼을 선택한다.

plt.figure(figsize=(10,10))
#그래프의 사이즈를 설정한다. (가로, 세로)

sns.heatmap(crime_anal_norm_sort[target_col], annot=True, fmt='f',linewidth=0.5,cmap='RdPu')
#annot: 그래프에 수치를 표시한다. True로 설정하면 수치가 표시된다.
#fmt: 그래프의 수치 표기법을 결정한다. 'f'는 실수형으로 표시한다.
#linewidth: 각 셀을 나눌 선의 두께이다.
#cmap: 그래프의 색상을 설정한다. 비율이 높아질수록 빨간색(Rd)에서 보라색(Pu)까지 변하도록 설정한다.

plt.title('범죄 검거 비율')
# 그래프의 제목을 출력한다.

plt.show()
#그래프를 출력한다.

--> 결과

[4]

target_col=['강간','강도','살인','절도','폭력']
plt.figure(figsize=(10,10))
sns.heatmap(crime_anal_norm_sort[target_col], annot=True, fmt='f',linewidth=0.5,cmap='RdPu')
plt.title('범죄 검거 비율')
plt.show()

--> 결과

[5]

target_col=['강간','강도','살인','절도','폭력']
#이용한 칼럼을 정해준다.

crime_anal_norm['범죄']=crime_anal_norm['범죄']/5
#범죄 칼럼에 있는 값들을 5로 나누어준다.

crime_anal_norm_sort=crime_anal_norm.sort_values(by='범죄',ascending=False)
#범죄 칼럼을 기준으로 열을 내림차순으로 정렬한다.

plt.figure(figsize=(10,10))
#그래프의 크기를 설정한다.

sns.heatmap(crime_anal_norm_sort[target_col], annot=True, fmt='f',linewidth=0.5,cmap='RdPu')
#annot: 그래프에 수치를 표시한다. True로 설정하면 수치가 표시된다.
#fmt: 그래프의 수치 표기법을 결정한다. 'f'는 실수형으로 표시한다.
#linewidth: 각 셀을 나눌 선의 두께이다.
#cmap: 그래프의 색상을 설정한다. 비율이 높아질수록 빨간색(Rd)에서 보라색(Pu)까지 변하도록 설정한다.

plt.title('범죄 검거 비율')
#그래프 제목을 설정한다.

plt.show()
#그래프를 출력한다.

--> 결과

4) 코딩문

[1]

import folium
import json
geo_path='data/seoul_geo.json'
geo_str=json.load(open(geo_path,encoding='utf-8'))
map = folium.Map(location=[37.5502, 126.982], zoom_start=12)
#Map함수에 지정할 위도와 경도를 location값으로 입력한다.

folium.Choropleth(
    geo_data=geo_str,
    #경계선 좌표값이 담긴 데이터(json파일에 있다.)
    
    data = crime_anal_norm['살인'],
    #살인 칼럼에 대한 데이터를 넣는다.
    
    fill_color='PuRd',
    #시각화에 쓰일 색상
    #비율이 높아질때 보라색(Pu)에서 빨간색(Rd)으로 간다.
    
    key_on='feature.id'
    #json파일과 gu_data.index의 값을 동일하게 하는 것이다.
    
).add_to(map) #미리 만들어둔 변수 map에 내용을 추가한다.
map

--> 결과

[2]

map = folium.Map(location=[37.5502, 126.982], zoom_start=12)
folium.Choropleth(
    geo_data=geo_str,
    data = crime_anal_norm['범죄'],
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)
map

--> 결과

[3]

tmp_criminal = crime_anal_norm['살인'] / crime_anal_norm['인구수'] * 1000000
#새롭게 만든 비율데이터를 만든다.

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(
    geo_data=geo_str,
    data = tmp_criminal,
    #분석할 데이터는 인구수에 대한 살인비율이다.
    
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)
map

--> 결과

[4]

tmp_criminal = crime_anal_norm['범죄'] / crime_anal_norm['인구수'] * 1000000

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(
    geo_data=geo_str,
    data = tmp_criminal,
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)
map

--> 결과

[5]

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(
    geo_data=geo_str,
    data = crime_anal_norm['검거'],
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)
map

--> 결과

5) 코딩문

[1]

crime_police['lat']=station_lat
crime_police['lng']=station_lng
#경찰서의 위도와 경도값을 칼럼으로 추가한다.

target_col = ['강간검거','강도검거','살인검거','절도검거','폭력검거']
#분석할 칼럼을 가져온다.

tmp = crime_police[target_col] / crime_police[target_col].max()
#각 검거에 따른 비율을 구한다.

crime_police['검거'] = np.sum(tmp, axis=1)
#5개의 검거비율을 전부 더해서 검거 칼럼에 열 단위로 넣는다.

crime_police.head()
#위쪽에서 5개 줄을 출력한다.

--> 결과

[2]

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)
#Map함수에 지정할 위도와 경도를 location값으로 입력한다.

for n in crime_police.index:
    folium.Marker([crime_police['lat'][n], crime_police['lng'][n]]).add_to(map)
	#각 위도와 경계에 따른 위치를 표시해준다.

map

--> 결과

[3]

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

for n in crime_police.index:
    folium.CircleMarker([crime_police['lat'][n], crime_police['lng'][n]],
    			#각 위도와 경계에 따른 위치를 입력한다.
                        
                       radius=crime_police['검거'][n]*10,
                	#평균 검거 비율에 따라 반경의 범위를 입력한다.
                
                       color='#3186cc',
                	#선의 색깔을 입력한다.
                
                       fill_color='#3186cc',
                	#채워질 원의 색깔을 입력한다.
                
                       fill=True
                	#원 색깔을 채우도록 한다.
                
                       ).add_to(map)

map

--> 결과

[4]

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(
    geo_data=geo_str,
    data = crime_anal_norm['검거'],
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)

for n in crime_police.index:
    folium.CircleMarker([crime_police['lat'][n], crime_police['lng'][n]],
                       radius=crime_police['검거'][n]*10,
                       color='#3186cc',
                       fill_color='#3186cc',
                       fill=True).add_to(map)

map

--> 결과

2. Sandwich(시카고 샌드위치 맛집 분석)

https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-Chicago/

 

The 50 Best Sandwiches in Chicago

Our list of Chicago’s 50 best sandwiches, ranked in order of deliciousness

www.chicagomag.com

- 시카고에서 TOP 50의 샌드위치 정보를 추출하려고 한다.

 

1) 코딩문

[1]

from bs4 import BeautifulSoup
#BeautifulSoup: HTML과 XML 문서들의 구문을 분석하기 위한 파이썬 패키지이다.

import requests
#requests: Python 프로그래밍 언어용 HTTP 클라이언트 라이브러리이다.
url='https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-Chicago/'

html=requests.get(url,headers={'User-Agent':'Mozilla/5.0'}).text
#requests.get() 함수의 headers 매개변수를 사용하여 요청에 HTTP 헤더를 포함할 수 있고
#headers 내에 'User-Agent'를 넘겨주면 된다.
#.text로 문자들을 선언해준다.

soup=BeautifulSoup(html,'lxml')
#html 문자열에 대해 lxml의 html 파서를 사용한다.

 

[2]

print(len(soup.select('div.sammy'))) #sammy 클래스를 가진 div태그를 선택한다.

- 해당 사이트 아래쪽에 1-50위의 샌드위치 링크가 있다.

- 해당 링크 하나에 '오른쪽 마우스'를 누르고 '검사'를 누르면 해당 html의 구문을 확인할 수 있다.

 

--> 결과

50

 

[3]

soup.select('div.sammy')[0] # 첫번째 div.sammy태그를 출력한다.

--> 결과

<div class="sammy" style="position: relative;">
<div class="sammyRank">1</div>
<div class="sammyListing"><a href="/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/"><b>BLT</b><br/>
Old Oak Tap<br/>
<em>Read more</em> </a></div>
</div>

[4]

tmp=soup.select('div.sammy')[0]
tmp.select('.sammyRank')[0].text
# sammyRank 클래스를 가진 div 태그의 텍스트를 출력한다.

--> 결과

'1'

[5]

tmp.select('.sammyListing')[0].text
# sammyListing 클래스를 가진 div 태그의 텍스트를 출력한다.

--> 결과

'BLT\nOld Oak Tap\nRead more '

[6]

tmp.find('a')['href']
#속성값 href의 데이터를 추출한다.

--> 결과

'/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/'

[7]

info = tmp.select('.sammyListing')[0].text
info_list = info.split('\n')
info_list
#해당 속성태그 문자를 \n를 기준으로 나누어서 출력한다.

--> 결과

['BLT', 'Old Oak Tap', 'Read more ']

[8]

rank=[]
main_menu=[]
cafe_name=[]
link_url=[]

list_soup=soup.select('div.sammy')
#list_soup리스트에 div.sammy태그 코드문들이 들어간다.

for item in list_soup:
    rank.append(item.select('.sammyRank')[0].text)
    #rank리스트에 sammyRank속성 태그 문자를 입력한다.
    
    info = item.select('.sammyListing')[0].text.split('\n')
    #info에 sammyListing속성 태그 문자를 '\n'기준으로 나누어서 입력한다.
    
    main_menu.append(info[0])
    #main_menu리스트에 info의 인덱스 0번의 값이 들어간다.
    
    cafe_name.append(info[1])
    #cafe_name리스트에 info의 인덱스 1번의 값이 들어간다.
    
    link_url.append(item.find('a')['href'])
    #link_url리스트에 속성값 href데이터가 들어간다.
print(len(rank),len(main_menu), len(cafe_name), len(link_url))
#각 리스트들의 인덱스 개수를 출력한다.

--> 결과

50 50 50 50

[9]

rank

--> 결과

['1',
 '2',
 '3',
 '4',
 '5',
 '6',
 '7',
 '8',
 '9',
 '10',
 '11',
 '12',
 '13',
 '14',
 '15',
 '16',
 '17',
 '18',
 '19',
 '20',
 '21',
 '22',
 '23',
 '24',
 '25',
 '26',
 '27',
 '28',
 '29',
 '30',
 '31',
 '32',
 '33',
 '34',
 '35',
 '36',
 '37',
 '38',
 '39',
 '40',
 '41',
 '42',
 '43',
 '44',
 '45',
 '46',
 '47',
 '48',
 '49',
 '50']

---

main_menu

--> 결과

['BLT',
 'Fried Bologna',
 'Woodland Mushroom',
 'Roast Beef',
 'PB&L',
 'Belgian Chicken Curry Salad',
 'Lobster Roll',
 'Smoked Salmon Salad',
 'Atomica Cemitas',
 'Grilled Laughing Bird Shrimp and Fried Po’ Boy',
 'Ham and Raclette Panino',
 'Breaded Steak',
 'The Hawkeye',
 'Chicken Dip',
 'Wild Boar Sloppy Joe',
 'Meatball Sub',
 'Corned Beef',
 'Turkey Club',
 'Falafel',
 'Crab Cake',
 'Chicken Schnitzel',
 'Shawarma',
 'Toasted Pimiento Cheese',
 'Vegetarian Panino',
 'Cali Chèvre',
 'Pastrami',
 'The Fredo',
 'Smoked Ham',
 'Jibarito',
 'Shaved Prime Rib',
 'Serrano Ham and Manchego Cheese',
 'Tuna Salad',
 'Paramount Reuben',
 'The Istanbul',
 'B.A.D.',
 'Duck Confit and Mozzarella',
 'Croque Monsieur',
 'Green Garbanzo',
 'The Hen House',
 'Tuscan Chicken',
 'The Marty ',
 'Whitefish',
 'Oat Bread, Pecan Butter, and Fruit Jam',
 'Cauliflower Melt',
 'Cubana',
 'Kufta',
 'Debbie’s Egg Salad',
 'Beef Curry',
 'Le Végétarien',
 'The Gatsby']

---

cafe_name

--> 결과

['Old Oak Tap',
 'Au Cheval',
 'Xoco',
 'Al’s Deli',
 'Publican Quality Meats',
 'Hendrickx Belgian Bread Crafter',
 'Acadia',
 'Birchwood Kitchen',
 'Cemitas Puebla',
 'Nana',
 'Lula Cafe',
 'Ricobene’s',
 'Frog n Snail',
 'Crosby’s Kitchen',
 'Longman & Eagle',
 'Bari',
 'Manny’s',
 'Eggy’s',
 'Old Jerusalem',
 'Mindy’s HotChocolate',
 'Olga’s Delicatessen',
 'Dawali Mediterranean Kitchen',
 'Big Jones',
 'La Pane',
 'Pastoral',
 'Max’s Deli',
 'Lucky’s Sandwich Co.',
 'City Provisions',
 'Papa’s Cache Sabroso',
 'Bavette’s Bar & Boeuf',
 'Hannah’s Bretzel',
 'La Fournette',
 'Paramount Room',
 'Melt Sandwich Shoppe',
 'Floriole Cafe & Bakery',
 'First Slice Pie Café',
 'Troquet',
 'Grahamwich',
 'Saigon Sisters',
 'Rosalia’s Deli',
 'Z&H MarketCafe',
 'Market House on the Square',
 'Elaine’s Coffee Call',
 'Marion Street Cheese Market',
 'Cafecito',
 'Chickpea',
 'The Goddess and Grocer',
 'Zenwich',
 'Toni Patisserie',
 'Phoebe’s Bakery']

---

link_url

--> 결과

['/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Au-Cheval-Fried-Bologna/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Xoco-Woodland-Mushroom/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Als-Deli-Roast-Beef/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Publican-Quality-Meats-PB-L/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Hendrickx-Belgian-Bread-Crafter-Belgian-Chicken-Curry-Salad/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Acadia-Lobster-Roll/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Birchwood-Kitchen-Smoked-Salmon-Salad/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Cemitas-Puebla-Atomica-Cemitas/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Nana-Grilled-Laughing-Bird-Shrimp-and-Fried-Oyster-Po-Boy/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Lula-Cafe-Ham-and-Raclette-Panino/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Ricobenes-Breaded-Steak/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Frog-n-Snail-The-Hawkeye/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Crosbys-Kitchen-Chicken-Dip/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Longman-and-Eagle-Wild-Boar-Sloppy-Joe/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Bari-Meatball-Sub/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Mannys-Corned-Beef/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Eggys-Turkey-Club/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Jerusalem-Falafel/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Mindys-HotChocolate-Crab-Cake/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Olgas-Delicatessen-Chicken-Schnitzel/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Dawali-Mediterranean-Kitchen-Shawarma/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Big-Jones-Toasted-Pimiento-Cheese/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-La-Pane-Vegetarian-Panino/',
 '/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Pastoral-Cali-Chevre/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Maxs-Deli-Pastrami/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Luckys-Sandwich-Co-The-Fredo/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-City-Provisions-Smoked-Ham/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Papas-Cache-Sabroso-Jibarito/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Bavettes-Bar-and-Boeuf-Shaved-Prime-Rib/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Hannahs-Bretzel-Serrano-Ham-and-Manchego-Cheese/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-La-Fournette-Tuna-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Paramount-Room-Paramount-Reuben/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Melt-Sandwich-Shoppe-The-Istanbul/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Floriole-Cafe-and-Bakery-BAD/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-First-Slice-Pie-Cafe-Duck-Confit-and-Mozzarella/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Troquet-Croque-Monsieur/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Grahamwich-Green-Garbanzo/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Saigon-Sisters-The-Hen-House/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Rosalias-Deli-Tuscan-Chicken/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Z-and-H-MarketCafe-The-Marty/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Market-House-on-the-Square-Whitefish/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Elaines-Coffee-Call-Oat-Bread-Pecan-Butter-and-Fruit-Jam/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Marion-Street-Cheese-Market-Cauliflower-Melt/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Cafecito-Cubano/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Chickpea-Kufta/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-The-Goddess-and-Grocer-Debbies-Egg-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Zenwich-Beef-Curry/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Toni-Patisserie-Le-Vegetarien/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Phoebes-Bakery-The-Gatsby/']

2) 코딩문

[1]

base_url='https://www.chicagomag.com'
#link_url리스트를 나열하면 완전한 주소가 아닌 것들이 있다.
#그래서 base_url주소가 없는 얘들에게 베이스주소를 붙여주어야 한다.
from urllib.parse import urljoin
#urllib.parse: url문자열을 구성요소로 분리하고, 구성요소를 다시 url문자열로 결합해
#상대 url을 주어진 기본 url에 따라 절대 url로 변환하는 표준 인터페이스를 정의합니다.

from tqdm.notebook import tqdm
#tqdm_notebook: tqdm하고 같은 라이브러리이다.
link_url=[]
list_soup=soup.select('div.sammy')

for item in list_soup:
    link_url.append(urljoin(base_url, item.find('a')['href']))
    #urljoin을 이용해 베이스주소가 없는 주소들만 골라서 base_url을 붙여준다.
link_url

--> 결과

['https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Au-Cheval-Fried-Bologna/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Xoco-Woodland-Mushroom/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Als-Deli-Roast-Beef/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Publican-Quality-Meats-PB-L/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Hendrickx-Belgian-Bread-Crafter-Belgian-Chicken-Curry-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Acadia-Lobster-Roll/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Birchwood-Kitchen-Smoked-Salmon-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Cemitas-Puebla-Atomica-Cemitas/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Nana-Grilled-Laughing-Bird-Shrimp-and-Fried-Oyster-Po-Boy/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Lula-Cafe-Ham-and-Raclette-Panino/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Ricobenes-Breaded-Steak/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Frog-n-Snail-The-Hawkeye/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Crosbys-Kitchen-Chicken-Dip/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Longman-and-Eagle-Wild-Boar-Sloppy-Joe/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Bari-Meatball-Sub/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Mannys-Corned-Beef/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Eggys-Turkey-Club/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Jerusalem-Falafel/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Mindys-HotChocolate-Crab-Cake/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Olgas-Delicatessen-Chicken-Schnitzel/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Dawali-Mediterranean-Kitchen-Shawarma/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Big-Jones-Toasted-Pimiento-Cheese/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-La-Pane-Vegetarian-Panino/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Pastoral-Cali-Chevre/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Maxs-Deli-Pastrami/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Luckys-Sandwich-Co-The-Fredo/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-City-Provisions-Smoked-Ham/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Papas-Cache-Sabroso-Jibarito/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Bavettes-Bar-and-Boeuf-Shaved-Prime-Rib/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Hannahs-Bretzel-Serrano-Ham-and-Manchego-Cheese/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-La-Fournette-Tuna-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Paramount-Room-Paramount-Reuben/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Melt-Sandwich-Shoppe-The-Istanbul/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Floriole-Cafe-and-Bakery-BAD/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-First-Slice-Pie-Cafe-Duck-Confit-and-Mozzarella/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Troquet-Croque-Monsieur/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Grahamwich-Green-Garbanzo/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Saigon-Sisters-The-Hen-House/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Rosalias-Deli-Tuscan-Chicken/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Z-and-H-MarketCafe-The-Marty/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Market-House-on-the-Square-Whitefish/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Elaines-Coffee-Call-Oat-Bread-Pecan-Butter-and-Fruit-Jam/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Marion-Street-Cheese-Market-Cauliflower-Melt/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Cafecito-Cubano/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Chickpea-Kufta/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-The-Goddess-and-Grocer-Debbies-Egg-Salad/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Zenwich-Beef-Curry/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Toni-Patisserie-Le-Vegetarien/',
 'https://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Phoebes-Bakery-The-Gatsby/']

[2]

import pandas as pd

data={'Rank':rank, 'Cafe':cafe_name, 'Menu':main_menu, 'URL':link_url}
df = pd.DataFrame(data)
#지금까지 추출한 데이터를 딕셔너리로 만들어 Dataframe에 이용한다. 

df.head()
#상위 5개의 데이터를 추출한다.

--> 결과

[3]

html=requests.get(df['URL'][0],headers={'User-Agent':'Mozilla/5.0'}).text
tmp=BeautifulSoup(html,'lxml')
#첫번째 url주소를 beautifulsoup으로 읽어서 들어간다.

 

tmp.select('p.addy')[0].text
#들어가서 addy클래스를 가진 p태그를 추출한다.

--> 결과

'\n$10. 2109 W. Chicago Ave., 773-772-0406, theoldoaktap.com'

[4]

tmp.select('p.addy')[0].text.split(',')[0]
#해당 주소를 ,단위로 나누어 인덱스 0의 값을 선택한다.

--> 결과

'\n$10. 2109 W. Chicago Ave.'

[5]

tmp.select('p.addy')[0].text.split(',')[0].split()[0]
# ,을 기준으로 나누고 이번에는 공백을 기준으로 나누어 인덱스 0번의 값을 출력한다.

--> 결과

'$10.'

[6]

tmp.select('p.addy')[0].text.split(',')[0].split()[0][:-1]
#뒤에 있는 점을 안보이게 하기 위해 [:-1]을 선언한다.
#[:-1]은 맨 오른쪽 값을 제외한 모두를 출력한다는 의미이다.

--> 결과

'$10'

[7]

tmp.select('p.addy')[0].text.split(',')[0].split()[1:]
# , 기준으로 인덱스 1번 값부터 끝까지 나열한다.

--> 결과

['2109', 'W.', 'Chicago', 'Ave.']

[8]

' '.join(tmp.select('p.addy')[0].text.split(',')[0].split()[1:])
# 인덱스 1번부터 나우어진 값들을 띄어쓰기를 하며 하나로 묶는다.

--> 결과

'2109 W. Chicago Ave.'

3) 코딩문

[1]

import time
price=[]
address=[]

for n in tqdm(df.index):
    html=requests.get(df['URL'][n],headers={'User-Agent':'Mozilla/5.0'}).text
    time.sleep(1) #로딩할 시간을 준다.
    tmp=BeautifulSoup(html,'lxml')
    val_list=tmp.select('p.addy')[0].text.split(',')[0].split()
    price.append(val_list[0][:-1])
    address.append(' '.join(val_list[1:]))

#상태바를 적용해서 샌드위치 페이지 50개에 접근한다.
#그리고 '2)코딩문'에서 했던 방식대로 주소값과 가격 리스트를 입력한다.

--> 결과

100%
 
50/50 [01:34<00:00, 1.95s/it]

[2]

print(len(price),len(address))
# 가격과 주소 리스트의 길이를 출력한다.

--> 결과

50 50

[3]

price

--> 결과

['$10',
 '$9',
 '$9.50',
 '$9.40',
 '$10',
 '$7.25',
 '$16',
 '$10',
 '$9',
 '$17',
 '$11',
 '$5.49',
 '$14',
 '$10',
 '$13',
 '$4.50',
 '$11.95',
 '$11.50',
 '$6.25',
 '$15',
 '$5',
 '$6',
 '$8',
 '$5.99',
 '$7.52',
 '$11.95',
 '$7.50',
 '$12.95',
 '$7',
 '$21',
 '$9.79',
 '$9.75',
 '$13',
 '$7.95',
 '$9',
 '$9',
 '$8',
 '$8',
 '$7',
 '$6',
 '$7.25',
 '$11',
 '$6',
 '$9',
 '$5.49',
 '$8',
 '$6.50',
 '$7.50',
 '$8.75',
 '$6.85']

[4]

address#multipe locations 주의

--> 결과

['2109 W. Chicago Ave.',
 '800 W. Randolph St.',
 '445 N. Clark St.',
 '914 Noyes St.',
 '825 W. Fulton Mkt.',
 '100 E. Walton St.',
 '1639 S. Wabash Ave.',
 '2211 W. North Ave.',
 '3619 W. North Ave.',
 '3267 S. Halsted St.',
 '2537 N. Kedzie Blvd.',
 'Multiple locations',
 '3124 N. Broadway',
 '3455 N. Southport Ave.',
 '2657 N. Kedzie Ave.',
 '1120 W. Grand Ave.',
 '1141 S. Jefferson St.',
 '333 E. Benton Pl.',
 '1411 N. Wells St.',
 '1747 N. Damen Ave.',
 '3209 W. Irving Park Rd.',
 'Multiple locations',
 '5347 N. Clark St.',
 '2954 W. Irving Park Rd.',
 'Multiple locations',
 '191 Skokie Valley Rd.',
 'Multiple locations',
 '1818 W. Wilson Ave.',
 '2517 W. Division St.',
 '218 W. Kinzie St.',
 'Multiple locations',
 '1547 N. Wells St.',
 '415 N. Milwaukee Ave.',
 '1840 N. Damen Ave.',
 '1220 W. Webster Ave.',
 '5357 N. Ashland Ave.',
 '1834 W. Montrose Ave.',
 '615 N. State St.',
 'Multiple locations',
 '241 N. York Rd.',
 '1323 E. 57th St.',
 '655 Forest Ave.',
 'Hotel Lincoln',
 '100 S. Marion St.',
 '26 E. Congress Pkwy.',
 '2018 W. Chicago Ave.',
 '25 E. Delaware Pl.',
 '416 N. York St.',
 '65 E. Washington St.',
 '3351 N. Broadway']

[5]

df['Price']=price
df['Address']=address
#df 데이터프레임에 Price, Address 칼럼을 추가합니다.
#그리고 price, address리스트를 넣어 값을 집어넣어 줍니다.

df.head()
#상위 5개 데이터를 출력합니다.

--> 결과

[6]

del df['URL']
# df데이터프레임에서 URL컬럼을 삭제합니다.

df.head()

--> 결과

[7]

df.set_index('Rank',inplace=True)
#set_index: 기존의 열을 인덱스로 설정하는 메소드이다.
#인덱스로 설정한 열은 Rank가 됩니다.
#inplace=True: 원본 객체를 변경할지 여부를 결정합니다.

df.head()

--> 결과

4) 코딩문

[1]

import folium
import googlemaps
import numpy as np
gmaps_key='-------------'
gmaps = googlemaps.Client(key=gmaps_key)
#googlemaps 키를 이용해 googlemaps 라이브러리를 실행한다.
lat=[]
lng=[]

for n in tqdm(df.index):
    if df['Address'][n] !='Multiple locations':
        target_name = df['Address'][n] + ' , '+'Chicago'
        gmaps_output = gmaps.geocode(target_name)
        time.sleep(0.5)
        location = gmaps_output[0].get('geometry')
        lat.append(location['location']['lat'])
        lng.append(location['location']['lng'])
    else:
        lat.append(np.nan)
        lng.append(np.nan)

#50개 샌드위치 집의 위도와 경도를 받아와서 Multiple locations가 아닌 곳의 주소를 조회한다.
#Multiple locations은 곳은 nan으로 입력한다.

--> 결과

100%
 
50/50 [00:27<00:00, 1.61it/s]

[2]

print(len(lat),len(lng))
# 위도와 경도 리스트이 길이를 출력합니다.

--> 결과

50 50

[3]

lat

--> 결과

[41.8955577,
 41.8846392,
 41.8905226,
 42.0583217,
 41.8866036,
 41.9002501,
 41.8590541,
 41.9102031,
 41.909758,
 41.8345302,
 41.9276207,
 nan,
 41.9384419,
 41.9451143,
 41.9300559,
 41.8912971,
 41.8678529,
 41.8852691,
 41.9080547,
 41.91369539999999,
 41.95371249999999,
 nan,
 41.9794496,
 41.9541592,
 nan,
 42.1567073,
 nan,
 41.9652783,
 41.9027261,
 41.8893683,
 nan,
 41.9105258,
 41.8896361,
 41.91504990000001,
 41.9218523,
 41.9797099,
 41.9617122,
 41.8929151,
 nan,
 41.9608129,
 41.7913223,
 41.7068231,
 41.9152875,
 41.8863622,
 41.8758205,
 41.8961134,
 41.89897850000001,
 41.9105832,
 41.8831061,
 41.9431632]

---

lng

--> 결과

[-87.6799673,
 -87.6475897,
 -87.6307834,
 -87.6837479,
 -87.64853649999999,
 -87.6250781,
 -87.6252007,
 -87.68287529999999,
 -87.7176767,
 -87.6456492,
 -87.706792,
 nan,
 -87.64464369999999,
 -87.6636971,
 -87.70703379999999,
 -87.6555453,
 -87.6419289,
 -87.6184837,
 -87.6343125,
 -87.6771273,
 -87.7084504,
 nan,
 -87.66795719999999,
 -87.70271079999999,
 nan,
 -87.8036345,
 nan,
 -87.6755421,
 -87.6902278,
 -87.6349487,
 nan,
 -87.6343775,
 -87.64484039999999,
 -87.6778047,
 -87.6592125,
 -87.66934409999999,
 -87.67581620000001,
 -87.62782589999999,
 nan,
 -87.9394449,
 -87.5938615,
 -87.6161865,
 -87.6343888,
 -87.8022297,
 -87.6264566,
 -87.677857,
 -87.6273926,
 -87.94048839999999,
 -87.6254381,
 -87.6445071]

[4]

df['lat']=lat
df['lng']=lng
#df 데이터프레임에 lat, lng칼럼을 추가한다.

df.head()

--> 결과

[5]

map=folium.Map(location=[df['lat'].mean(), df['lng'].mean()],zoom_start=11)
#50개 맛집의 위도, 경도의 평균값을 입력한다.

folium.Marker([df['lat'].mean(), df['lng'].mean()], popup='center').add_to(map)
#각 평균값의 위도와 경도를 입력한다.
#popup: 표기할 팝업 문구를 지정한다.

map

--> 결과

[6]

map=folium.Map(location=[df['lat'].mean(), df['lng'].mean()],zoom_start=11)

for n in df.index:
    if np.isnan(df['lat'][n])== False:
        folium.Marker([df['lat'][n], df['lng'][n]], popup=df['Cafe'][n]).add_to(map)
#50개 맛집에 대해 위도와 경도를 표기한다.
#위도가 없는 좌표는 제외한다.

map

--> 결과

3. Oil_station(서울시 주유소 가격비교)

- 서울의 주유소 가격을 데이터 분석을 하려고 한다.

https://www.opinet.co.kr/searRgSelect.do

 

싼 주유소 찾기 오피넷

 

www.opinet.co.kr

 

1) 코딩문

[1]

from bs4 import BeautifulSoup
import requests
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

#Selenium에서 웹드라이버를 import한다.

 

 

https://julian5383.tistory.com/152

 

[비트교육센터] 자바 기반 풀스택 개발자 양성 과정(Bigdata) 6일차

1. Closure - 간단히 말해 함수 안에 내부 함수(inner function)를 구현하고 그 내부 함수를 반환하는 함수를 말한다. - 이때 외부 함수는 자신이 가진 변숫값 등을 내부 함수에 전달하여 실행되도록 한

julian5383.tistory.com

- practice 폴더 경로에  driver 폴더파일을 옮겨준다.

- 위의 링크 사이트를 보면 크롬드라이버를 설치할 수 있는 방법을 알 수 있다.

driver = webdriver.Chrome(service=Service('driver/chromedriver.exe'))
driver.get('https://www.opinet.co.kr/searRgSelect.do')

[2]

xpath="""//*[@id="SIGUNGU_NM0"]"""
gu_list_raw=driver.find_element(By.XPATH,xpath)
# F12키를 누르고 빨간색 1번 아이콘을 눌러 2번의 html코드 위치를 확인한다.
# 확인이 되면 해당 코드위에 오른쪽 마우스 찍기를 하고 xpath를 복사해 간다.
gu_list = gu_list_raw.find_elements(By.TAG_NAME,'option')
#find_element_by_xpath: xpath위치를 찾는다.
gu_names=[option.get_attribute('value') for option in gu_list]
#각 지역의 리스트를 확보한다.
print(gu_names)

--> 결과

['', '강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구']

[3]

gu_names=gu_names[1:]
gu_names
#인덱스 0번에 있는 값을 제외하고 전체 출력한다.

--> 결과

['강남구',
 '강동구',
 '강북구',
 '강서구',
 '관악구',
 '광진구',
 '구로구',
 '금천구',
 '노원구',
 '도봉구',
 '동대문구',
 '동작구',
 '마포구',
 '서대문구',
 '서초구',
 '성동구',
 '성북구',
 '송파구',
 '양천구',
 '영등포구',
 '용산구',
 '은평구',
 '종로구',
 '중구',
 '중랑구']

[4]

gu_list_raw.send_keys(gu_names[0])
#지역이름을 send_keys를 통해서 입력하게 한다.

xpath="""//*[@id="glopopd_excel"]"""
driver.find_element(By.XPATH,xpath).click()
# 엑셀 다운로드관련 html코드의 xpath를 가져온다.
# 그리고 find_element를 통해 해당 xpath의 위치를 찾아 click으로 다운로드 버튼을 눌러준다.
import time
from tqdm.notebook import tqdm
for gu in tqdm(gu_names):
    xpath="""//*[@id="SIGUNGU_NM0"]"""
    element=driver.find_element(By.XPATH,xpath)
    element.send_keys(gu)
    time.sleep(3)
    xpath="""//*[@id="glopopd_excel"]"""
    driver.find_element(By.XPATH,xpath).click()
    time.sleep(2)
#먼저 지역구를 조회하고 3초 동안을 쉰다.
#그 다음 해당지역구의 주유소 관련 파일을 다운로드 하고 2초동안 쉰다.

--> 결과

100%
 
25/25 [02:17<00:00, 5.47s/it]

- 이렇게 하면 총 25개의 엑셀파일이 다운로드 된다.

- 그리고 해당파일은 data 폴더안에 oil폴더를 만들고 그안으로 이동한다.

 

[5]

print(len(gu_names))
#조회된 지역구리스트의 길이이다.

--> 결과

25

---

driver.close()
#크롬드라이버를 종료한다.

2) 코딩문

[1]

import pandas as pd
from glob import glob
#glob: 파일들의 리스트를 뽑을 때 사용한다.
station_files=glob('data/oil/지역_위치별*.xls')
station_files
#파일리스트를 뽑는다.

--> 결과

['data/oil\\지역_위치별(주유소) (1).xls',
 'data/oil\\지역_위치별(주유소) (10).xls',
 'data/oil\\지역_위치별(주유소) (11).xls',
 'data/oil\\지역_위치별(주유소) (12).xls',
 'data/oil\\지역_위치별(주유소) (13).xls',
 'data/oil\\지역_위치별(주유소) (14).xls',
 'data/oil\\지역_위치별(주유소) (15).xls',
 'data/oil\\지역_위치별(주유소) (16).xls',
 'data/oil\\지역_위치별(주유소) (17).xls',
 'data/oil\\지역_위치별(주유소) (18).xls',
 'data/oil\\지역_위치별(주유소) (19).xls',
 'data/oil\\지역_위치별(주유소) (2).xls',
 'data/oil\\지역_위치별(주유소) (20).xls',
 'data/oil\\지역_위치별(주유소) (21).xls',
 'data/oil\\지역_위치별(주유소) (22).xls',
 'data/oil\\지역_위치별(주유소) (23).xls',
 'data/oil\\지역_위치별(주유소) (24).xls',
 'data/oil\\지역_위치별(주유소) (3).xls',
 'data/oil\\지역_위치별(주유소) (4).xls',
 'data/oil\\지역_위치별(주유소) (5).xls',
 'data/oil\\지역_위치별(주유소) (6).xls',
 'data/oil\\지역_위치별(주유소) (7).xls',
 'data/oil\\지역_위치별(주유소) (8).xls',
 'data/oil\\지역_위치별(주유소) (9).xls',
 'data/oil\\지역_위치별(주유소).xls']

[2]

- 파이선에서 엑셀파일을 다루는 xlrd 라이브러리를 설치한다.

- conda install xlrd

tmp_raw=[]
#xlrd 설치
for file_name in station_files:
    tmp=pd.read_excel(file_name,header=2)
    #엑셀 파일들을 읽어온다.
    
    tmp_raw.append(tmp)
    #엑셀파일들을 읽어 tmp_raw 리스트에 넣는다.
print(len(tmp_raw))
#tmp_raw 리스트의 길이를 출력한다.

--> 결과

25

---

station_raw=pd.concat(tmp_raw)
#concat 명령으로 엑셀파일들을 결합한다.
station_raw.info()

--> 결과

<class 'pandas.core.frame.DataFrame'>
Int64Index: 442 entries, 0 to 33
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   지역      442 non-null    object
 1   상호      442 non-null    object
 2   주소      442 non-null    object
 3   상표      442 non-null    object
 4   전화번호    442 non-null    object
 5   셀프여부    442 non-null    object
 6   고급휘발유   442 non-null    object
 7   휘발유     442 non-null    object
 8   경유      442 non-null    object
 9   실내등유    442 non-null    object
dtypes: object(10)
memory usage: 38.0+ KB

[3]

station_raw.head()
#상위 5개의 자료를 출력한다.

--> 결과

[4]

stations=pd.DataFrame(
    {'Oil_store': station_raw['상호'],
    '주소': station_raw['주소'],
    '가격': station_raw['휘발유'],
    '셀프': station_raw['셀프여부'],
    '상표': station_raw['상표']}
)
stations.head()
# station_raw리스트를 이용해 데이터프레임에 딕셔너리 형태로 표를 구성한다.

--> 결과

[5]

stations['구']=[address.split()[1] for address in stations['주소']]
stations.head()
#칼럼 '구'를 추가한다.

--> 결과

[6]

stations['구'].unique()
#unique()를 이용해 고유값들을 출력한다.

--> 결과

array(['강동구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구',
       '양천구', '영등포구', '강북구', '용산구', '은평구', '종로구', '중구', '중랑구', '강서구',
       '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '강남구'], dtype=object)

[7]

 

stations[stations['가격']=='-']
# 가격 칼럼에서 -값이 있는 행을 조회한다.

--> 결과

---

stations = stations[stations['가격']!='-']
stations.head()
# 가격 칼럼에서 -값이 있는 해을 제외하고 출력한다.

--> 결과

[8]

stations.info()

--> 결과

<class 'pandas.core.frame.DataFrame'>
Int64Index: 441 entries, 0 to 33
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype 
---  ------     --------------  ----- 
 0   Oil_store  441 non-null    object
 1   주소         441 non-null    object
 2   가격         441 non-null    object
 3   셀프         441 non-null    object
 4   상표         441 non-null    object
 5   구          441 non-null    object
dtypes: object(6)
memory usage: 24.1+ KB

---

stations['가격']=[float(value) for value in stations['가격']]
stations.info()
# 가격 칼럼에 있는 값들을 실수형으로 변환한다.

--> 결과

<class 'pandas.core.frame.DataFrame'>
Int64Index: 441 entries, 0 to 33
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   Oil_store  441 non-null    object 
 1   주소         441 non-null    object 
 2   가격         441 non-null    float64
 3   셀프         441 non-null    object 
 4   상표         441 non-null    object 
 5   구          441 non-null    object 
dtypes: float64(1), object(5)
memory usage: 24.1+ KB

---

stations.head(20)
#상위 20행을 출력한다.

--> 결과

[9]

stations.reset_index(inplace=True)
#인덱스를 열로 변환한다. inplace=True는 원본을 변경할지 여부를 물어본다.

stations.head(20)

--> 결과

[10]

del stations['index']
#인덱스 칼럼을 삭제한다.

stations.head()

--> 결과

3) 코딩문

[1]

import matplotlib.pyplot as plt
import seaborn as sns

import platform
from matplotlib import font_manager, rc # Runtime Configuration

path = "c:/Windows/Fonts/malgun.ttf"
if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~~')
stations.boxplot(column='가격', by='셀프', figsize=(10,8))
#boxplot은 수치 데이터를 표현하는 방식입니다.
#column: 박스플롯을 생성해야하는 열 이름
#by: 서로 다른 그룹의 by열을 그룹화하는 서로다른 상자 그림을 플로팅합니다.
#figsize: 그래프의 크기를 설정합니다.

plt.show()

--> 결과

[2]

plt.figure(figsize=(10,8))
sns.boxplot(x="상표", y="가격", hue="셀프", data=stations, 
            palette="Set3")
#x,y: x,y축의 이름을 정한다.
#hue: 범주형 칼럼명을 넣으면 해당범주에 대한 세분화된 그래프를 그릴수 있다.
#palette: 컬러맵을 지정할 수 있다.

plt.show()

--> 결과

[3]

plt.figure(figsize=(10,8))
sns.boxplot(x="상표", y="가격", hue="셀프", data=stations, 
            palette="Set3")
sns.swarmplot(x="상표", y="가격", data=stations, color=".6")
#swarmplot: x인자 또는 y인자에 수치형 원소를 갖는 배열을 넣어준다.
#x,y: x축과 y축의 이름을 정해준다.
#data: 데이터를 넣어준다.
#color: 점의 색깔을 정해준다.

plt.show()

--> 결과

4) 코딩문

[1]

import json
import folium
import googlemaps
stations.sort_values(by='가격', ascending=False).head(10)
#가격 칼럼을 내림차순으로 정렬하고 상위 10개의 데이터를 출력한다.

--> 결과

---

stations.sort_values(by='가격', ascending=True).head(10)
#이번에는 오름차순으로 정렬한다.

--> 결과

5) 코딩문

[1]

import numpy as np

gu_data=pd.pivot_table(stations, index=['구'], values=['가격'], aggfunc=np.mean)
#피봇테이블: 데이터 열 중에서 두 개의 열을 각각 행 인덱스, 열 인덱스로 사용하여 데이터를 조회하여 펼쳐놓은 것을 말한다.
#index: 인덱스로 사용될 열입니다.
#values: 값으로 입력될 열입니다.
#aggfunc: 산식이 들어갑니다. 여기서는 mean, 즉 중앙값이 들어갑니다.

gu_data.head()

--> 결과

[2]

geo_path = 'data/seoul_geo.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(
    geo_data=geo_str,
    data = gu_data,
    columns=[gu_data.index, '가격'],
    fill_color='PuRd',
    key_on='feature.id'
).add_to(map)
map

#서울시 구별정보를 통해서 주유가격이 가장높은곳과 낮은 곳을 출력한다.

--> 결과

[3]

oil_price_top10 = stations.sort_values(by='가격', 
                                       ascending=False).head(10)
oil_price_top10
#가격에 관련해서 내림차순으로 정렬한다.

--> 결과

---

oil_price_bottom10 = stations.sort_values(by='가격', 
                                          ascending=True).head(10)
oil_price_bottom10

#이번에는 반대로 오름차순으로 정렬한다.

--> 결과

6) 코딩문

[1]

gmaps_key='----------'
gmaps = googlemaps.Client(key=gmaps_key)
#구글 맵스 api를 입력한다.
from tqdm.notebook import tqdm
lat=[]
lng=[]

for n in tqdm(oil_price_top10.index):
	# tqdm : progress bar 생성 / for n in oil_price_top10.index : oil_price_top10 의 처음부터 순차적으로 for문 반복
    
    try:
    # try: 실행할 코드
        tmp_add = oil_price_top10['주소'][n].split('(')[0]
        # oil_price_top10 의 '주소' 를 '(' 를 기준으로 분할 후 index 0번째 것만 가져온다
        
        tmp_map = gmaps.geocode(tmp_add)
        # 'tmp_add'의 구글 geocode 를 가져옴.
        
        tmp_loc = tmp_map[0].get('geometry')
        # 구글맵 주소 정보는 dictionary 형태 이기에 keys값(tmp_add)으로 values 값(주소)을 가져온다.
        
        lat.append(tmp_loc['location']['lat'])
        # keys값 : ('geometry')['location']['lat']
        
        lng.append(tmp_loc['location']['lng'])
        # keys값 : ('geometry')['location']['lng']
        
    except:
    	# except:예외가 발생했을 때 처리하는 코드
        
        lat.append(np.nan)
        lng.append(np.nan)
        print("nan!!")
        # "서울 양천구 남부순환로 317" 와 같이 '(' 가 없어서 오류가 나는 것을 방지

# 주유가격 상위 10개 주유소에 대해 위도와 경도를 읽어온다.
# try,catch를 선언해 위도와 경로를 조회하다가 오류가 나면 nan을 출력한다.

--> 결과

100%
 
10/10 [00:01<00:00, 9.15it/s]

---

print(len(lat))
#위도리스트에 몇개의 값이 있는지 조회한다.

--> 결과

10

[2]

oil_price_top10['lat']=lat
oil_price_top10['lng']=lng
oil_price_top10

#oil_price_top10에 위도와 경도 칼럼을 추가한다.

--> 결과

[3]

lat=[]
lng=[]

for n in tqdm(oil_price_bottom10.index):
    try:
        tmp_add = oil_price_bottom10['주소'][n].split('(')[0]
        tmp_map = gmaps.geocode(tmp_add)
        tmp_loc = tmp_map[0].get('geometry')
        lat.append(tmp_loc['location']['lat'])
        lng.append(tmp_loc['location']['lng'])
    except:
        lat.append(np.nan)
        lng.append(np.nan)
        print("nan!!")

#아까와는 반대로 오름차순으로 정렬된 리스트를 가져온다.

--> 결과

100%
 
10/10 [00:00<00:00, 11.97it/s]

[4]

oil_price_top10['lat']=lat
oil_price_top10['lng']=lng
oil_price_top10

#이번에는 오름차순으로 정렬된 위도와 경도 칼럼으로 추가한다.

--> 결과

7) 코딩문

[1]

map = folium.Map(location=[37.5502, 126.982], 
                 zoom_start=11)

#가장 비싼곳(빨간색, 모여있음)
for n in oil_price_top10.index:
    folium.CircleMarker([oil_price_top10['lat'][n], 
                         oil_price_top10['lng'][n]], 
                         radius=15, color='#CD3181', 
                         fill_color='#CD3181',
                         fill=True).add_to(map)
#가장 싼곳(파란색, 흩어져 있음)
for n in oil_price_bottom10.index:
    folium.CircleMarker([oil_price_bottom10['lat'][n], 
                         oil_price_bottom10['lng'][n]], 
                         radius=15, color='#3186cc', 
                         fill_color='#3186cc',
                         fill=True).add_to(map)

map

--> 결과