Lina's Toolbox

[스파르타 내일배움캠프_AI웹개발 과정] 3일차 복습/ Python, flask 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/python

[스파르타 내일배움캠프_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 %}

 

 

이미지 삽입 

  1. 먼저, static 이라는 폴더를 만든다 .(static 폴더 이름은 필수)
  2. 그 안에 image 폴더 생성
  3. 이미지 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주차 부터는 모르는 내용이 많이 나와서 유익하고 재밌다...!

근데 프로그래밍 언어가 다 그렇듯이, 자바랑 웹 연동 방식이 매우 비슷하다.

그런데 훨씬 단순하고, 가볍고 쉽다!! 

파이썬을 한번 시작하고 부터는 정말 다시는 자바로 돌아가기 싫음..ㅋㅋㅋ