일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 내일배움캠프
- lol
- 장고
- 프로그래머스
- 파이썬
- github
- sort
- drf
- greedy
- 그리디알고리즘
- git
- programmers
- 백준
- 탐욕알고리즘
- Riot
- Django
- 코딩테스트
- 스파르타내일배움캠프TIL
- 코딩테스트준비
- 그리디
- python
- 라이엇
- 스파르타내일배움캠프
- 알고리즘
- 롤
- SQL
- 자바
- API
- 리그오브레전드
- java
- Today
- Total
Lina's Toolbox
[스파르타 내일배움캠프_AI웹개발 과정] 3일차 복습/ Python, flask 본문
[스파르타 내일배움캠프_AI웹개발 과정] 3일차 복습/ Python, flask
Woolina 2024. 6. 26. 23:31
VScode mac 단축키
alt + Shitft + 방향키 : 원하는 코드 복사 가능
(아래 키 누르면서 하면 아래로 한줄씩 복사됨)
가상 환경(virtual environment)
회사에서는 패키지 A, B, C를 설치해서 쓰고, 개인 프로젝트에서는 패키지 B, C, D, E를 설치해서 쓰고 있었는데
회사팀장님이 B를 이전 버전인 B' 로 쓰자고 한다면,
같은 컴퓨터에 깔린 개인 프로젝트에서는 B' 로 쓰면 코드를 다 바꿔야함
다 담아둘 필요 없이 공구함을 2개 만들어서,
공구함1에 A, B', C를 담아두고,
공구함2에 B, C, D, E를 담아두고 쓰면 관리하기 편할 것!!
즉, 가상환경은 프로젝트별 공구함
가상환경: 같은 시스템에서 실행되는 다른 파이썬 응용 프로그램들의 동작에 영향을 주지 않기 위해, 파이썬 배포 패키지들을 설치하거나 업그레이드하는 것을 가능하게 하는 격리된 실행
가상환경 만들기
VScode 보기 -> 명령팔레트 -> env 입력 -> Python: 환경 만들기(Create Enviornment)
-> venv 현재 작업 영역에 '.venv' 가상 환경을 만듭니다. -> Python 3.8.6 버전 선택 -> 기다리면 가상환경 만들어짐
터미널에서 (.venv) 가 뜨고,
왼쪽 파일 탐색기에 >.venv 폴더가 생겼으면 가상환경 세팅 완료된 것
(만약 안보인다면 무언가 잘못된 것임)
패키지와 라이브러리
패키지: 모듈(다른 사람들이 이미 개발해논 기능들 묶음)을 모아 놓은 단위
라이브러리: 패키지의 묶음
파이썬에서 외부 라이브러리를 사용하기 위해서는 패키지 설치
비빔밥을 만들기 위해서 고추장을 만들면서 시작하지는 않듯이, 누군가가 이미 잘 만들어둔 도구를 잘 활용하는 것은 중요하다.
패키지 설치
PIP(Package Installer for Python) 사용
- 여러 패키지를 설치할 수 있게 도와주는 프로그램
- 원하는 패키지만 쏙쏙 골라 설치할 수 있음
터미널에서
pip install requests
입력 후 엔터
설치된 패키지는
pip list
로 확인 가능
라이브러리는 이미 설치가 되어있는데 또 설치해도 별다른 문제가 없으므로,
걱정하지 말고 안되면 다시 설치해주자!
request 패키지
요청에 대해 처리할 때 import request 필요
다음과 같은 API를
import requests # requests 라이브러리 설치 필요
r = requests.get('http://spartacodingclub.shop/sparta_api/seoulair')
rjson = r.json()
print(rjson)
이런식으로 받아올 수 있다.
예제) 각 구이름, 미세먼지 값 받아오기
import requests # requests 라이브러리 설치 필요
r = requests.get('http://spartacodingclub.shop/sparta_api/seoulair')
rjson = r.json()
rows = rjson['RealtimeCityAir']['row']
for row in rows:
gu_name = row['MSRSTE_NM']
gu_mise = row['IDEX_MVL']
print(gu_name, gu_mise)
웹 스크래핑, 웹 크롤링
웹 스크래핑과 웹크롤링은 사실은 다른 개념 (웹 스크래핑 != 웹크롤링)
- 웹크롤링: 웹사이트의 페이지들을 자동으로 탐색하고 인덱싱하는 것. 도서관에서 책의 목록을 만드는 것과 비슷. 도서관의 모든 책을 살펴보며 각각의 책이 어디에 있는지, 무엇에 관한 것인지 기록하는 과정
- 웹스크래핑: 특정 웹페이지에서 원하는 데이터를 추출하는 것. 특정 책의 특정 장에서 필요한 정보만 발췌하는 것과 비슷. 예를 들어, 요리책에서 특정 레시피만 뽑아내는 작업.
네이버 날씨 페이지 웹 스크래핑 하기
1. 가상 환경 활성화 확인
터미널 새 입력줄에 .venv 있는 지 확인할 것
2. 웹 스크래핑 패키지 설치
pip install requests bs4
requests와 bs4 라는 패키지를 .venv라는 가상환경에 설치하겠다는 뜻.
3. python 파일 코드 작성
웹 스크래핑 기본 세팅
import requests
from bs4 import BeautifulSoup
URL = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=날씨"
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(URL, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
스크래핑한 내용이 soup 이라는 변수에 담긴다.
select() / select_one() 으로 원하는 html 정보 가져오는 법
# 선택자를 사용하는 방법 (copy selector)
# select(): 특정 태그/클래스/아이디 에 있는 정보만 긁어온다.
soup.select('태그명')
soup.select('.클래스명')
soup.select('#아이디명')
# 부모관계 태그
soup.select('상위태그명 > 하위태그명 > 하위태그명')
soup.select('상위태그명.클래스명 > 하위태그명.클래스명')
# 태그와 속성값으로 찾는 방법
soup.select('태그명[속성="값"]')
# 한 개만 가져오고 싶은 경우
soup.select_one('위와 동일')
필요한 정보가 담겨있는 태그는 관리자 도구로 확인.
사실 상 갖고 싶은 데이터는 html 태그가 아니고, 안에 들어있는 글자이므로,
html태그 데이터에 .text를 사용하여 가져온다.
temp = soup.select_one('.temperature_text')
print(temp.text)
.클래스이름 > html태그 로 클래스이름에 해당하는 html 태그 안의 html태그 지정 가능
(html 태그, 아이디, 클래스 이름 어떤 것을 써도됨)
.contents 로 내부의 데이터를 전부 알아서 리스트로 쪼개줌
temp = soup.select_one('.temperature_text > strong').contents[1]
cloud = soup.select_one('.weather').text
humid = soup.select_one('.summary_list > div:nth-child(2) > dd').text
wind = soup.select_one('.summary_list > div:nth-child(3) > dd').text
print(temp, cloud, humid, wind)
멜론 차트 웹 스크래핑(웹 크롤링)
trs = soup.select('.lst50')
for tr in trs:
rank = tr.select_one('.rank').text
title = tr.select_one('.rank01 > span > a').text
artist = tr.select_one('.rank02 > a').text
image = tr.select_one('img')['src']
print(rank, artist, "-", title)
실행화면
50위까지 밖에 나오지 않음
-> 51위~100위는 클래스명이 'list50'이 아니라 'list100'으로 바뀌기 때문
-> 클래스나 아이디명이 아닌 다른 정보로 긁어오자.
# 멜론뮤직 스크래핑
from bs4 import BeautifulSoup
import requests
# bs4 시작코드
'''
스크래핑 하고 싶은 주소를 url에 넣어주세요
'''
url = "https://www.melon.com/chart/"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
trs = soup.select('table > tbody > tr')
for tr in trs:
rank = tr.select_one('.rank').text
title = tr.select_one('.rank01 > span > a').text
artist = tr.select_one('.rank02 > a').text
image = tr.select_one('img')['src']
print(rank, artist, "-", title)
Flask
로컬 개발환경: "클라이언트 = 서버" 가 한 컴퓨터일 경우
서버 구동은 원래는 복잡한 과정임. 그것을 편하게 해주는 플라스크
Flask 프레임워크: 서버 구동을 편하게 시켜주는 코드 모음
(웹서버 구동하는 데 필요한 복잡한 코드들을 쉽게 가져다 쓸 수 있음.
사용자의 요청에 맞춰 HTML 파일을 응답해준다.)
Flask는 프로젝트의 폴더 구조가 정해져 있다. (만들 때 폴더 명의 스펠링도 오타 주의!)
flask
|— venv
|—app.py(서버) # 파이썬 파일명은 변경해도 괜찮지만, 라이브러리 이름과 같은 이름을 사용하지 않도록 주의
|— templates (폴더) # templates 폴더 명은 반드시 고정!! 플라스크의 규칙임
|— index.html (클라이언트 파일) # templates 폴더 안에 있음
# html파일은 이름이 달라도 되지만 보통은 index.html사용
1. 가상환경 실행: 명령 팔레트 -> 가상환경 만들기 -> .venv 확인
2. (Flask 폴더 안에서)
pip install flask
* UnicodeDecodeError: 'cp949’ 에러가 뜨며 설치가 안 되는 경우
이 오류는 경로 중 한국어가 포함되어있어 생기는 문제
설치 확인하고 싶다면
pip list
로 Flask가 리스트에 있다면 설치 확인
3. templates 폴더 생성
4. app.py 파일 생성
5. templates 폴더 아래에 index.html 파일 생성
프레임워크
밀키트 같은 것! 누군가 미리 만들어 둔 것. 우리는 편하게 가져다 쓰기만 하면 됨.
6. Flask 코드 작성
flask 시작 코드
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
if __name__ == '__main__':
app.run(debug=True)
터미널에서 다음 작성
python app.py
혹은 mac에서는 python3 app.py
빨간 글씨가 떠서 에러난 것처럼 보였지만, 터미널에서 위처럼 보이면 잘 작동한 것임.
http://localhost:5000 링크 부분에 마우스를 대고 Command + 클릭!
네트워크 5000번 주소(포트)가 이미 사용 중 이라는 에러가 뜬다면,
app.py 파일로 들어와서 마지막 줄에 port=5000을 5001 로 바꾸고 다시 실행해보자.
이 화면이 뜬다면 Flask 잘 작동된 것.
* 코드를 수정했다면 ctrl + C 로 끄고 다시 실행해야함.
URL 따라 다른 화면 보여주기
@app.route('/') 부분을 수정해서 URL따라 화면 분리 가능
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'This is Home!'
@app.route('/mypage')
def mypage():
return 'This is My Page!'
if __name__ == '__main__':
app.run(debug=True)
url 별로 함수명이 같거나, route('/')내의 주소가 같으면 안됨
app.py에 flask 내장함수 render_template를 이용해서 HTML 파일을 불러올 수 있다.
#app.py
from flask import Flask, render_template
app = Flask(__name__)
## URL 별로 함수명이 같거나,
## route('/') 등의 주소가 같으면 안됩니다.
@app.route('/')
def home():
return render_template('index.html')
@app.route('/mypage')
def mypage():
return 'This is My page!'
if __name__ == "__main__":
app.run(debug=True)
변수 넘겨주기
#app.py
@app.route('/')
def home():
name = "김우린"
return render_template('index.html', data=name)
Python 서버에서 넘겨준 데이터를 HTML에서 사용 시 {{ 변수명 }} 형식으로 사용
<!--templates/index.html-->
<body>
<h1>안녕, {{ data }}</h1>
</body>
데이터 여러 개 넘겨주기
딕셔너리를 사용하여 1개로 묶어서 전송
#app.py
context = {
"HTML에서 사용할 이름": 변수명,
"HTML에서 사용할 이름": 변수명,
}
#app.py
@app.route('/')
def home():
name = "최지웅"
lotto = [16, 18, 20, 23, 32, 43]
context = {
"name": name,
"lotto": lotto,
}
return render_template('index.html', data=context)
<!-- index.html -->
<body>
<h1>안녕, {{ data.name }}</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
</body>
반복문으로 출력 할 경우
ffor 입력하고 탭 -> flask for 선택하면 자동완성됨
{% for number in data.lotto %}
{{ number }}
{% endfor %}
fif , felif 입력후 탭으로도 for if elif문 자동 완성 가능
반복문과 HTML 태그를 함께 사용할 경우
<body>
<h1>안녕, {{ data.name }}</h1>
<h2>로또 번호: {{ data.lotto }}</h2>
<ol>
{% for number in data.lotto %}
<li>{{ number }}</li>
{% endfor %}
</ol>
</body>
1~45까지 무작위로 숫자 6개를 중복해서 뽑는 코드 (로또 번호 선정)
import random
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
lotto_numbers = generate_lotto_numbers()
print("추출된 로또 번호:", lotto_numbers)
파이썬 리스트 2개에서 같은 요소의 갯수를 확인하는 코드
def count_common_elements(list1, list2):
common_elements = set(list1) & set(list2)
return len(common_elements)
# 예시 리스트
list1 = [1, 2, 3, 4, 9]
list2 = [4, 5, 6, 7, 8]
common_count = count_common_elements(list1, list2)
print("두 리스트에서 공통된 요소의 개수:", common_count)
최종_ 로또 당첨 확인 사이트
#app.py
import random
@app.route("/")
def home():
name = "최지웅"
lotto = [16, 18, 20, 23, 32, 43]
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
random_lotto = generate_lotto_numbers()
def count_common_elements(list1, list2):
common_elements = set(list1) & set(list2)
return len(common_elements)
common_count = count_common_elements(lotto, random_lotto)
context = {
"name": name,
"lotto": lotto,
"random_lotto": random_lotto,
"common_count": common_count,
}
return render_template("index.html", data=context)
<!-- index.html -->
<h1>안녕, {{ data.name }}</h1>
<h1>로또 번호: {{ data.lotto }}</h1>
<h1>랜덤 로또 번호: {{ data.random_lotto }}</h1>
{% if data.common_count == 6 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 1등입니다.</h2>
{% elif data.common_count == 5 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 2등입니다.</h2>
{% elif data.common_count == 4 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 3등입니다.</h2>
{% elif data.common_count == 3 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 4등입니다.</h2>
{% else %}
<h2>{{ data.common_count }}개 맞았습니다! 탈락입니다...😥😥</h2>
{% endif %}
이미지 삽입
- 먼저, static 이라는 폴더를 만든다 .(static 폴더 이름은 필수)
- 그 안에 image 폴더 생성
- 이미지 image 폴더 안에 다운로드 받음
이미지 태그 사용
url_for : 보통 경로(위치)를 표현
<img src="{{ url_for('static', filename='이미지 경로') }}" alt="">
CSS로 꾸민 최종 코드
app.py
from flask import Flask, render_template
app = Flask(__name__)
import random
@app.route("/")
def home():
name = "김우린"
lotto = [16, 18, 20, 23, 32, 43]
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
random_lotto = generate_lotto_numbers()
def count_common_elements(list1, list2):
common_elements = set(list1) & set(list2)
return len(common_elements)
common_count = count_common_elements(lotto, random_lotto)
context = {
"name": name,
"lotto": lotto,
"random_lotto": random_lotto,
"common_count": common_count,
}
return render_template("index.html", data=context)
@app.route("/mypage")
def mypage():
return "This is My Page!"
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.ball {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #FFD700;
color: #FFFFFF;
text-align: center;
line-height: 30px;
margin-right: 5px;
}
.randomball {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: red;
color: #FFFFFF;
text-align: center;
line-height: 30px;
margin-right: 5px;
}
.coinman {
height: 100px;
}
</style>
</head>
<body>
<h1>안녕, {{ data.name }}</h1>
<img class="coinman" src="{{ url_for('static', filename='image/coinman.png') }}" alt="">
<h2>로또 번호: {{ data.lotto }}</h2>
<h2>랜덤 로또 번호: {{ data.random_lotto }}</h2>
<h1>로또 번호</h1>
{% for num in data.lotto %}
<div class="ball">{{ num|e }}</div>
{% endfor %}
<h1>랜덤 로또 번호</h1>
{% for num in data.random_lotto %}
<div class="randomball">{{ num|e }}</div>
{% endfor %}
{% if data.common_count == 6 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 1등입니다.</h2>
{% elif data.common_count == 5 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 2등입니다.</h2>
{% elif data.common_count == 4 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 3등입니다.</h2>
{% elif data.common_count == 3 %}
<h2>{{ data.common_count }}개 맞았습니다! 로또 4등입니다.</h2>
{% else %}
<h2>{{ data.common_count }}개 맞았습니다! 탈락입니다...😥😥</h2>
{% endif %}
</body>
</html>
영화 검색 사이트
request.args.get('name 속성명') - > Form에서 입력한 데이터를 받아올 수 있음
<!-- movie.html-->
<form action="{{ url_for('movie') }}">
<input type="text" name="query">
<button type="submit">검색</button>
</form>
action = "데이터를 보낼 url"
GET 방식으로 호출하는 API
GET 방식: 주소에 키=값 형식으로 데이터를 넣어서 요청
ex. 기존 주소 끝에 &movieNm=기생충 을 붙여준다.
http://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieList.json?key=f5eef3421c602c6cb7ea224104795888&movieNm=기생충
JSONVIEW 확장 익스텐션 깔아야 보기 편함
완성코드
app.py
from flask import Flask, render_template, request
import random
import requests
app = Flask(__name__)
@app.route("/")
def home():
name = "김우린"
lotto = [16, 18, 20, 23, 32, 43]
def generate_lotto_numbers():
numbers = random.sample(range(1, 46), 6)
return sorted(numbers)
random_lotto = generate_lotto_numbers()
def count_common_elements(list1, list2):
common_elements = set(list1) & set(list2)
return len(common_elements)
common_count = count_common_elements(lotto, random_lotto)
context = {
"name": name,
"lotto": lotto,
"random_lotto": random_lotto,
"common_count": common_count,
}
return render_template("index.html", data=context)
@app.route("/mypage")
def mypage():
return "This is My Page!"
@app.route("/movie")
def movie():
query = request.args.get('query') # 검색어
URL = f"http://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieList.json?key=f5eef3421c602c6cb7ea224104795888&movieNm={query}" # URL
res = requests.get(URL)
rjson = res.json()
movie_list = rjson["movieListResult"]["movieList"]
return render_template("movie.html", data=movie_list)
if __name__ == "__main__":
app.run(debug=True)
movie.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>영화 검색</h1>
<form action="{{ url_for('movie') }}">
<input type="text" name="query">
<button type="submit">검색</button>
</form>
{% for movie in data %}
<!-- <p>{{ movie }}</p> -->
<p>영화 제목: {{ movie.movieNm }}</p>
<p>타입: {{ movie.typeNm }}</p>
{% if movie.directors %}
<p>감독: {{ movie.directors[0].get('peopleNm') }}</p>
{% endif %}
<hr>
{% endfor %}
</body>
</html>
박스오피스 검색
app.py
from flask import Flask, render_template, request
import requests
app = Flask(__name__)
@app.route("/answer")
def answer():
if request.args.get('query'):
query = request.args.get('query')
else:
query = '20230601'
URL = f"http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchWeeklyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt={query}"
res = requests.get(URL)
rjson = res.json()
movie_list = rjson.get("boxOfficeResult").get("weeklyBoxOfficeList")
return render_template("answer.html", data=movie_list)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<h1>박스오피스 검색</h1>
<p>20230501 형식으로 검색하세요.</p>
<form action="{{ url_for('answer') }}">
<input type="text" name="query">
<button type="submit">검색</button>
</form>
<table class="table">
<thead>
<tr>
<th scope="col">랭킹</th>
<th scope="col">영화명</th>
<th scope="col">영화개봉일</th>
<th scope="col">누적관객수</th>
</tr>
</thead>
<tbody>
{% for movie in data %}
<tr>
<th scope="row">{{ movie.rank }}</th>
<td>{{ movie.movieNm}}</td>
<td>{{ movie.openDt }}</td>
<td>{{ movie.audiAcc}}명</td>
</tr>
{% endfor %}
</tbody>
</table>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
</body>
</html>
멜론 차트 정보 스크래핑 해서 카드형식으로 뿌려주기
app.py
from flask import Flask, render_template
from bs4 import BeautifulSoup
import requests
app = Flask(__name__)
@app.route('/')
def index():
url = "https://www.melon.com/chart/"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
melon_data = []
trs = soup.select('table > tbody > tr')
for tr in trs:
rank = tr.select_one('.rank').text
title = tr.select_one('.rank01 > span > a').text
artist = tr.select_one('.rank02 > a').text
image = tr.select_one('img')['src']
melon_data.append({'rank': rank, 'artist': artist, 'title': title, 'image': image})
return render_template('index.html', data=melon_data)
if __name__ == '__main__':
app.run()
index.html
<!Doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<style>
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css");
.background-banner {
background-image: linear-gradient(45deg,
rgb(51 43 43 / 75%),
rgb(20 19 20 / 61%)), url("https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/webjong/images/music_festival.jpg");
max-height: 100%;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed;
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
</head>
<body data-bs-theme="dark">
<div class="background-banner">
<nav class="navbar border-bottom border-bottom-dark d-flex justify-content-space-between" data-bs-theme="dark">
<div class="ms-3">
<img src="https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/webjong/images/sparta-logo.svg" alt="">
</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link text-white" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="#">Music</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="#">Album</a>
</li>
<li class="nav-item">
<a class="nav-link text-white">Movie</a>
</li>
</ul>
</div>
</div>
</nav>
</nav>
<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold text-body-emphasis">멜로디 쉐어</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">
<br>
노래를 들으면 생각나는 누군가가 있으신가요?
<br><br>
당신의 감성이 담긴 인생곡 플레이리스트
<br>
멜로디쉐어에서 소중한 사람과 함께 공유하세요.
</p>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
<!-- Button trigger modal -->
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#exampleModal">
음악 추가
</button>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">최애 음악</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">유저</label>
<input type="text" class="form-control" id="exampleInputEmail1"
aria-describedby="emailHelp">
<div id="emailHelp" class="form-text">사용자(추천인)의 이름을 넣어주세요.</div>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">노래 제목</label>
<input type="text" class="form-control" id="exampleInputPassword1">
<div id="emailHelp" class="form-text">좋아하는 노래 제목을 넣어주세요.</div>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">가수</label>
<input type="text" class="form-control" id="exampleInputPassword1">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">앨범 커버URL</label>
<input type="text" class="form-control" id="exampleInputPassword1">
</div>
<button type="submit" class="btn btn-danger">Submit</button>
</form>
</div>
</div>
</div>
</div>
<!-- Cards -->
<div class="row row-cols-1 row-cols-md-4 g-4 mx-auto w-75 pb-5">
{% for melon in data %}
<div class="col">
<div class="card h-100">
<img src="{{ melon.image }}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">{{ melon.title }}</h5>
<p class="card-text">{{ melon.artist }}</p>
<p class="card-text">추천 by 최지웅</p>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="container">
<footer>
<div class="d-flex flex-column flex-sm-row justify-content-between py-4 my-4 border-top">
<p>© 2023 Company, Inc. All rights reserved.</p>
<ul class="list-unstyled d-flex">
<li class="ms-3"><a class="link-body-emphasis" href="https://www.youtube.com/@SpartaCodingClub"><i class="bi bi-youtube"></i></a></li>
<li class="ms-3"><a class="link-body-emphasis" href="https://www.instagram.com/spartacodingclub/"><i class="bi bi-instagram"></i></a></li>
<li class="ms-3"><a class="link-body-emphasis" href="https://spartacodingclub.kr/blog"><i class="bi bi-postcard"></i></a></li>
</ul>
</div>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
crossorigin="anonymous"></script>
</body>
</html>
f-string
age = 27
print(f"{age}살")
이처럼 문자열"" 안에 f"{변수명}" 으로 변수 사용 가능하다.
문자열 시작 전에 f도 붙여줘야 하는 듯.
파이썬을 안다뤄봤다보니 3주차 부터는 모르는 내용이 많이 나와서 유익하고 재밌다...!
근데 프로그래밍 언어가 다 그렇듯이, 자바랑 웹 연동 방식이 매우 비슷하다.
그런데 훨씬 단순하고, 가볍고 쉽다!!
파이썬을 한번 시작하고 부터는 정말 다시는 자바로 돌아가기 싫음..ㅋㅋㅋ
'스파르타 내일 배움 캠프 AI 웹개발 과정 > python' 카테고리의 다른 글
스파르타 내일 배움 캠프 AI웹개발 | 파이썬 팀 과제 / 클래스 인스턴스(객체) 활용 (0) | 2024.07.04 |
---|---|
스파르타 내일배움캠프 AI웹개발 과정 | 6일차 복습/Pycharm 설치 (2) | 2024.07.01 |
Pythonanywhere로 내 코드 배포하기 (0) | 2024.06.28 |
파이썬 Flask 활용해 DB 연동하기 (SQLite) (0) | 2024.06.28 |
[스파르타 내일배움캠프 AI웹개발 과정] 4일차 복습/db연동 (0) | 2024.06.27 |