문제 1.
주유소 데이터를 저장할 테이블을 다음의 구조로 생성하세요. (PDF 파일 참고)
import mysql.connector
conn = mysql.connector.connect(
host = "host",
port = 3306,
user = "oneday",
password = "1234",
database = "oneday"
)
cursor = conn.cursor(buffered=True)
# gas_brand
sql_b = "CREATE TABLE GAS_BRAND(" + \
"id int not null auto_increment primary key, " + \
"name varchar(16) not null)"
cursor.execute(sql_b)
# gas_station
sql_s = "CREATE TABLE GAS_STATION(" + \
"id int auto_increment primary key, " +\
"brand int not null, " +\
"name varchar(64) not null, " +\
"city char(2) not null, " +\
"gu varchar(10) not null, " +\
"address varchar(128) not null, " +\
"gasoline int not null, " +\
"diesel int not null, " +\
"self boolean not null, " +\
"car_wash boolean not null, " +\
"charging_station boolean not null, " +\
"car_maintenance boolean not null, " +\
"convenience_store boolean not null, " +\
"24_hours boolean not null, " +\
"lat decimal(16,14) not null, " +\
"lng decimal(17,14) not null, " +\
"foreign key (brand) references GAS_BRAND(id));"
cursor.execute(sql_s)
문제 2.
Python 코드로 GAS_BRAND 데이터를 다음과 같이 입력하고 확인하세요. (PDF 파일 참고)
📍 오류 발생:
IntegrityError: 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (oneday.GAS_STATION, CONSTRAINT GAS_STATION_ibfk_1 FOREIGN KEY (brand) REFERENCES GAS_BRAND (id))
📍 해결:
데이터 입력 확인: 삽입하려는 데이터가GAS_BRAND
테이블에 존재하는지 확인하세요. 존재하지 않는 브랜드의 id를GAS_STATION
테이블에 넣으면 이러한 에러가 발생할 수 있습니다.# 어디에서 오류 발생하고 있는지 확인 print(f"{brand}")
--> 과제에서 제시된
GAS_BRAND
의 '현대오일뱅크' 브랜드가 최근 이름을 'HD현대오일뱅크' 로 변경하여, 사이트에서 가져온 정보와 맞지 않아 발생하는 오류였다.# GAS_BRAND 변경해주기 sqltmp = "UPDATE GAS_BRAND SET name = 'HD현대오일뱅크' WHERE id = 2;" cursor.execute(sqltmp) conn.commit()
queries1 = [
(1, 'SK에너지'),
(2, 'HD현대오일뱅크'),
(3, 'GS칼텍스'),
(4, 'S-OIL'),
(5, '알뜰주유소'),
(6, '자가상표')
]
query = "INSERT INTO GAS_BRAND VALUES (%s, %s)"
cursor.executemany(query, queries1)
conn.commit()
제출 1.
sql_result = "DESC GAS_STATION"
cursor.execute(sql_result)
result = cursor.fetchall()
for i in result:
print(i)
제출 2.
sql_result = "SELECT * FROM GAS_BRAND"
cursor.execute(sql_result)
result = cursor.fetchall()
for i in result:
print(i)
문제 3.
다음의 함수와 그 함수를 테스트하는 코드를 작성하세요. (PDF 파일 참고)
# 화폐단위 문자형 -> 숫자형
def stringToInt(s):
if s != '':
s = s.replace(',', '')
return int(s)
else:
return None
stringToInt('1,000')
# 주유소 브랜드를 입력하면 GAS_BRAND 데이터를 참고하여 ID 반환
def getID(brand):
sql_result = "SELECT * FROM GAS_BRAND"
cursor.execute(sql_result)
result = cursor.fetchall()
for i in result:
if i[1] == brand:
return i[0]
# 브랜드명이 '알뜰(ex)'인 경우 있음
elif brand == '알뜰(ex)':
return 5
getID('SK에너지')
# 주소를 입력받아 구 이름 반환
def getGu(add):
addList = add.split()
return addList[1]
getGu('서울시 강남구 헌릉로 730')
import googlemaps
gmaps_key = "AIzaSyBn4xqGnCRJRbB-y4uCvBjqNu97pCuXcnc"
gmaps = googlemaps.Client(key = gmaps_key)
# 주소를 입력받아 위도, 경도 반환
def getLL(add):
tmp = gmaps.geocode(add, language='ko')
lat = tmp[0].get("geometry")["location"]["lat"]
lng = tmp[0].get("geometry")["location"]["lng"]
return lat, lng
getLL('서울시 강남구 헌릉로 730')
문제 4.
Python 코드에서 주유소 페이지에서 데이터를 가져올때, GAS_STATION 테이블에 바로 입력하도록 수정하세요. (앞서 생 성한 함수활용)
제출 4.
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
from tqdm import tqdm_notebook
# 오피넷 -> 구 정보 가져오기
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome()
driver.get(url)
gu_list_raw = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu_list = gu_list_raw.find_elements(By.TAG_NAME, 'option')
gu_names = [option.get_attribute('value') for option in gu_list]
gu_names = gu_names[1:]
sql = "INSERT INTO GAS_STATION (brand, name, city, gu, address, gasoline, diesel, self, " +\
"car_wash, charging_station, car_maintenance, convenience_store, 24_hours, lat, lng) " +\
"VALUES (%s, %s, '서울', %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
def check(data, tag):
return 'off' not in data.select_one(tag)['src']
sqltmp = "ALTER TABLE GAS_STATION MODIFY diesel int NULL;"
cursor.execute(sqltmp)
conn.commit()
for gu in tqdm_notebook(gu_names):
element = driver.find_element(By.ID, 'SIGUNGU_NM0')
element.send_keys(gu)
time.sleep(0.5)
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# 검색할 주유소 개수
cnt = int(driver.find_element(By.ID, 'totCnt').text)
for i in range(1, cnt+1):
# 각 주유소 클릭
station = driver.find_element(By.CSS_SELECTOR, f'#body1 > tr:nth-child({i}) > td.rlist > a')
station.click()
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
data = soup.select('#os_dtail_info')[0]
# brand
brand = getID(data.select_one('#poll_div_nm').text)
# name
name = data.select_one('.header').text.strip()
# address
address = data.select_one('#rd_addr').text
# gasoline
gasoline = stringToInt(data.select_one('#b027_p').text)
# diesel
diesel = stringToInt(data.select_one('#d047_p').text)
# self
slf = data.select_one('#SPAN_SELF_VLT_YN_ID')
if type(slf.find('img')) == type(None):
is_self = False
else:
is_self = True
# car_wash
car_wash = check(data, '#cwsh_yn')
# charging_station
charging_station = check(data, '#lpg_yn')
# car_maintenance
car_maintenance = check(data, '#maint_yn')
# convenience_store
convenience_store = check(data, '#cvs_yn')
# 24_hours
sel24 = check(data, '#sel24_yn')
tmp = gmaps.geocode(address, language='ko')
# lat
lat = tmp[0].get('geometry')['location']['lat']
# lng
lng = tmp[0].get('geometry')['location']['lng']
cursor.execute(sql, (brand, name, gu, address, gasoline, diesel,
is_self, car_wash, charging_station, car_maintenance, convenience_store, sel24, lat, lng))
conn.commit()
# 데이터 개수 확인
cursor.execute("select count(*) from GAS_STATION")
result = cursor.fetchall()
print(result[0])
# 데이터 상위 10개 출력
cursor.execute("select * from GAS_STATION limit 10")
result = cursor.fetchall()
for i in result:
print(i)
문제 5.
시각화 프로젝트를 위하여 다음의 규칙으로 쿼리하여 CSV 파일로 저장합니다. (Python 코드로 작성)
제출 5.
import pandas as pd
sql = "select s.id, b.name 'brand', s.name, s.city, s.gu, s.address, s.gasoline, s.diesel, s.self, " +\
"s.car_wash, s.charging_station, s.car_maintenance, s.convenience_store, s.24_hours, " +\
"s.lat, s.lng " +\
"from GAS_BRAND b, GAS_STATION s " +\
"where b.id = s.brand ORDER BY s.id"
cursor.execute(sql)
result = cursor.fetchall()
columns = [i[0] for i in cursor.description]
df = pd.DataFrame(result)
df.columns = columns
df.head()
df.to_csv('./sql2_oil_station_data.csv', index=False, encoding='utf-8')
# 확인
df = pd.read_csv('./sql2_oil_station_data.csv', index_col=0, thousands=',', encoding='utf-8')
df.head()
문제 6.
위도, 경도 정보를 이용하여 미왕빌딩에서 1킬로 이내에 위치한 주유소 정보를 검색하세요.
제출 6.
# 미왕빌딩 주소: 서울 강남구 강남대로 364
lat, lng = getLL('서울 강남구 강남대로 364')
lat, lng
sql = "select s.id, b.name 'brand', s.name, s.address, " +\
"(6371 * acos(cos(radians(37.4955525)) * cos(radians(s.lat)) * cos(radians(s.lng) - radians(127.0292924)) + sin(radians(37.4955525)) * sin(radians(s.lat)))) 'distance' " +\
"from GAS_BRAND b, GAS_STATION s " +\
"where s.brand = b.id " +\
"having distance <= 1 " +\
"order by distance"
cursor.execute(sql)
result = cursor.fetchall()
for i in result:
print(i)
문제 7.
위도, 경도 정보를 이용하여 미왕빌딩에서 셀프주유가 가능하고 24시간이면서 편의점이 있는 가장 가까운 주유소 10개를 휘 발유 가격이 가장 저렴한 순으로 정렬하여 조회하세요.
제출 7.
sql = "select * " +\
"from (select s.id, b.name 'brand', s.name, s.address, s.gasoline, s.self, s.24_hours, s.convenience_store, " +\
"(6371 * acos(cos(radians(37.4955525)) * cos(radians(s.lat)) * cos(radians(s.lng) - radians(127.0292924)) + sin(radians(37.4955525)) * sin(radians(s.lat)))) 'distance' " +\
"from GAS_BRAND b, GAS_STATION s " +\
"where s.brand = b.id and s.self=1 and s.24_hours=1 and s.convenience_store=1 " +\
"order by distance limit 10) as tmp " +\
"order by tmp.gasoline;"
cursor.execute(sql)
result = cursor.fetchall()
for i in result:
print(i)
문제 8.
구별로 주유소 브랜드 별 휘발유 평균가격을 조회하여 저렴한 순으로 출력하세요.
제출 8.
sql = "select s.gu, b.name, avg(s.gasoline) " +\
"from GAS_STATION as s, GAS_BRAND as b " +\
"where b.id = s.brand " +\
"group by s.gu, b.name " +\
"order by s.gu, avg(s.gasoline)"
cursor.execute(sql)
result = cursor.fetchall()
for i in result:
print(i)
conn.close()