Programming/WEB
[12] project_02> 노엘피디아 : Get, Post 연습, API 만들기, 로컬 개발 환경
알 수 없는 사용자
2023. 7. 19. 11:17
1. app.py 만들기
2. venv 가상환경 설정하기
더보기


python -m venv venv


- (venv)로 잘 뜨는지 확인하기!
3. templates > index.html 만들기
4. 패키지 설치하기
더보기
pip install flask pymongo dnspython bs4 requests
- 띄어쓰기로 구분
- 원하는 라이브러리 확인하려면 pip freeze로 확인할 수 있음!
pip freeze
5. 조각 기능 구현해보기
1) 조각기능
더보기
- flask 라이브러리를 쓰지 않는 기능이라면 app.py 밖에서 구현해서 붙이는 방법
- flask에서 브라우저를 열고 진행하지 않아서 속도가 더 빠르다
2) 웹스크래핑을 사용해 URL에서 페이지 정보 가져오기
더보기
1) meta 태그 활용
- meta 태그 : 웹의 속성을 설명해주는 태그
▫️썸네일 사진 og:image
▫️썸네일 제목 og:title
▫️썸네일 설명 og:description
2) meta 태그 정보를 가져오는 조각 기능 만들기
- meta_prac.py 파일 만들기
- 크롤링 기본 코드
import requests
from bs4 import BeautifulSoup
URL = 'https://movie.daum.net/moviedb/main?movieId=161806'
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')
ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
ogimage = soup.select_one('meta[property="og:image"]')['content']
print(ogimage,ogtitle,ogdesc)
5. 기본 코드
1) app.py
더보기
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route("/movie", methods=["POST"])
def movie_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg':'POST 연결 완료!'})
@app.route("/movie", methods=["GET"])
def movie_get():
return jsonify({'msg':'GET 연결 완료!'})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
2) index.html
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<title>노엘피디아</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Dongle&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Nanum+Myeongjo&display=swap");
.title_all {
font-family: "Dongle", sans-serif;
background-color: goldenrod;
color: white;
height: 400px;
background-image: linear-gradient(0deg,
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.1)),
url(https://p4.wallpaperbetter.com/wallpaper/988/646/461/la-la-land-ryan-gosling-emma-stone-hd-wallpaper-preview.jpg);
background-position: center;
background-size: cover;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mytitle {
font-size: xxx-large;
font-weight: 600;
}
.mybtn_comment {
margin-top: 15px;
width: 130px;
height: 40px;
color: white;
font-weight: 300;
font-size: x-large;
background-color: transparent;
border-color: white;
border-radius: 20px;
border: 2px solid;
}
.mybtn_comment:hover {
border: 2px solid yellow;
color: yellow;
}
.mycards {
width: 95%;
margin: 20px auto 0px auto;
font-family: "Nanum Myeongjo", serif;
}
.card-title {
font-weight: bolder;
}
.mycomment {
color: gray;
}
.newpost {
font-family: "Nanum Myeongjo", serif;
width: 500px;
margin: 20px auto 20px auto;
padding: 20px 20px 20px 20px;
box-shadow: 0px 0px 3px 0px gray;
}
.mybtn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 20px;
}
.mybtn>button {
margin-right: 10px;
}
</style>
<script>
$(document).ready(function () {
loadtemp();
loadcards();
})
function loadtemp() {
$("#temp").empty()
fetch("http://spartacodingclub.shop/sparta_api/weather/seoul").then(res => res.json()).then(data => {
let a = data['temp']
let temp_html = `<span id="temp">${a}</span>`
$("#temp").append(temp_html)
})
}
function loadcards() {
$("#newcards").empty()
fetch("http://spartacodingclub.shop/web/api/movie").then(res => res.json()).then(data => {
let movie = data['movies']
movie.forEach((a) => {
img = a['image']
title = a['title']
desc = a['desc']
star = a['star']
star_img = '⭐'.repeat(star)
comment = a['comment']
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${img}" class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p>${star_img}</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$("#newcards").append(temp_html)
})
})
}
</script>
</head>
<body>
<div class="title_all">
<h1 class="mytitle">노엘피디아</h1>
<div>
<h3> 현재 서울의 날씨 : <span id="temp">20</span>도 </h3>
</div>
<button class="mybtn_comment">영화 기록하기</button>
</div>
<div class="newpost">
<div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com" />
<label for="floatingInput">영화 URL</label>
</div>
<div class="new_star">
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">별점</label>
<select class="form-select" id="inputGroupSelect01">
<option selected>-- 선택하기 --</option>
<option value="1">⭐</option>
<option value="2">⭐⭐</option>
<option value="3">⭐⭐⭐</option>
<option value="4">⭐⭐⭐⭐</option>
<option value="5">⭐⭐⭐⭐⭐</option>
</select>
</div>
</div>
<div class="form-floating">
<textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2"
style="height: 100px"></textarea>
<label for="floatingTextarea2">Comments</label>
</div>
<div class="mybtn">
<button type="button" class="btn btn-dark">기록하기</button>
<button type="button" class="btn btn-outline-dark">닫기</button>
</div>
</div>
<div class="mycards">
<div id="newcards" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
3) mongoDB 창 띄워두기
6. POST 요청
1) 데이터 명세
더보기
1) 요청 정보 : URL= /movie, 요청 방식 = POST
2) 클라이언트(fetch) ➡️ 서버(flask) : URL, star, comments
3) 서버(flask) ➡️ 클라이언트(fetch) : 확인 메시지 전송 (포스팅 완료!)
2) 클라이언트와 서버 연결 확인하기
더보기
1) app.py 코드
@app.route("/movie", methods=["POST"])
def movie_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg':'POST 연결 완료!'})
2) index.html 코드
function posting() {
let formData = new FormData();
formData.append('sample_give', '샘플데이터')
fetch('/movie', {method: "POST", body: formData}).then((res)=> res.json()).then((data)=> {
console.log(data)
alert(data['msg'])
})
}
<button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
3) 서버 만들기
더보기
1) pymongo import하기!
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@noelle.ricsrmv.mongodb.net/')
db = client.dbsparta
2) 받은 데이터를 db에 저장하고 완료됐음을 return 해줌
@app.route("/movie", methods=["POST"])
def movie_post():
image_receive = request.form['image_give']
title_receive = request.form['title_give']
desc_receive = request.form['desc_give']
star_receive = request.form['star_give']
comment_receive = request.form['comment_give']
doc = {
'image' : image_receive,
'title' : title_receive,
'desc' : desc_receive,
'star' : star_receive,
'comment' : comment_receive
}
db.movie2.insert_one(doc)
return jsonify({'msg':'포스팅 완료'})
4) 클라이언트 만들기
더보기

function posting() {
let url = $("#url").val()
let star = $("#star").val()
let comment = $("#comment").val()
let formData = new FormData();
formData.append('url_give', url)
formData.append('star_give', star)
formData.append('comment_give', comment)
fetch('/movie', {method: "POST", body: formData}).then(res=> res.json()).then(data => {
alert(data['msg'])
window.location.reload()
})
}

- id를 잘 붙이는 게 중요하다.
- id를 지정할 태그명을 다시 잘 확인해서 실수하지 않도록 하자.
- 입력받은 데이터의 id 값으로 가져오는 것 잊지 말자~!
- $("#id").val()
- window.location.reload() : 새로고침
5) 완성 확인하기
더보기



1) 데이터 입력

2) alert 확인

3) DB 확인

7. GET 요청
1) 데이터 명세
더보기
1) 요청 정보 : URL= /movie, 요청 방식 = GET
2) 클라이언트(fetch) ➡️ 서버(flask) : 없음
3) 서버(flask) ➡️ 클라이언트(fetch) : 전체 데이터 전송
2) 클라이언트와 서버 연결 확인하기
더보기
1) app.py
@app.route("/mars", methods=["GET"])
def mars_get():
return jsonify({'msg':'GET 연결 완료!'})
2) index.html
$(document).ready(function () {
show_order();
});
function show_order() {
fetch('/mars').then((res) => res.json()).then((data) => {
console.log(data)
alert(data['msg'])
})
3) 서버 만들기
더보기
- 'result'에 전체 데이터를 담아서 내려주기!
- db.db명.find({}, {_id}:False) : 전체 다 찾아서 받기
@app.route("/movie", methods=["GET"])
def movie_get():
movies = list(db.movie2.find({},{'_id':False}))
return jsonify({'result': movies})
4) 클라이언트 만들기
더보기
<script>
$(document).ready(function () {
loadtemp();
loadcards();
})
function loadtemp() {
$("#temp").empty()
fetch("http://spartacodingclub.shop/sparta_api/weather/seoul").then(res => res.json()).then(data => {
let a = data['temp']
let temp_html = `<span id="temp">${a}</span>`
$("#temp").append(temp_html)
})
}
function loadcards() {
$("#newcards").empty()
fetch('/movie').then(res => res.json()).then(data => {
let movies = data['result']
movies.forEach((a) => {
img = a['image']
title = a['title']
desc = a['desc']
star = a['star']
star_img = '⭐'.repeat(star)
comment = a['comment']
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${img}" class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p>${star_img}</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$("#newcards").append(temp_html)
})
})
}
</script>
- 받아온 정보가 리스트이므로 리스트를 돌면서 하나씩 출력하도록 한다.
5) 완성 확인하기
더보기

새로고침해서 DB에 있는 정보들이 잘 들어오는지 확인하기

* 전체 완성 코드
1) app.py
더보기
from flask import Flask, render_template, request, jsonify
from bs4 import BeautifulSoup
import requests
from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@noelle.ricsrmv.mongodb.net/')
db = client.dbsparta
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
@app.route("/movie", methods=["POST"])
def movie_post():
url_receive = request.form['url_give']
star_receive = request.form['star_give']
comment_receive = request.form['comment_give']
print(url_receive, star_receive, comment_receive)
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_receive,headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
ogimage = soup.select_one('meta[property="og:image"]')['content']
doc = {
'image' : ogimage,
'title' : ogtitle,
'desc' : ogdesc,
'star' : star_receive,
'comment' : comment_receive
}
db.movie2.insert_one(doc)
return jsonify({'msg':'포스팅 완료'})
@app.route("/movie", methods=["GET"])
def movie_get():
movies = list(db.movie2.find({},{'_id':False}))
return jsonify({'result': movies})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
2) index.html
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<title>노엘피디아</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Dongle&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Nanum+Myeongjo&display=swap");
.title_all {
font-family: "Dongle", sans-serif;
background-color: goldenrod;
color: white;
height: 400px;
background-image: linear-gradient(0deg,
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.1)),
url(https://p4.wallpaperbetter.com/wallpaper/988/646/461/la-la-land-ryan-gosling-emma-stone-hd-wallpaper-preview.jpg);
background-position: center;
background-size: cover;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mytitle {
font-size: xxx-large;
font-weight: 600;
}
.mybtn_comment {
margin-top: 15px;
width: 130px;
height: 40px;
color: white;
font-weight: 300;
font-size: x-large;
background-color: transparent;
border-color: white;
border-radius: 20px;
border: 2px solid;
}
.mybtn_comment:hover {
border: 2px solid yellow;
color: yellow;
}
.mycards {
width: 95%;
margin: 20px auto 0px auto;
font-family: "Nanum Myeongjo", serif;
}
.card-title {
font-weight: bolder;
}
.mycomment {
color: gray;
}
.newpost {
font-family: "Nanum Myeongjo", serif;
width: 500px;
margin: 20px auto 20px auto;
padding: 20px 20px 20px 20px;
box-shadow: 0px 0px 3px 0px gray;
}
.mybtn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 20px;
}
.mybtn>button {
margin-right: 10px;
}
</style>
<script>
$(document).ready(function () {
loadtemp();
loadcards();
})
function loadtemp() {
$("#temp").empty()
fetch("http://spartacodingclub.shop/sparta_api/weather/seoul").then(res => res.json()).then(data => {
let a = data['temp']
let temp_html = `<span id="temp">${a}</span>`
$("#temp").append(temp_html)
})
}
function loadcards() {
$("#newcards").empty()
fetch('/movie').then(res => res.json()).then(data => {
let movies = data['result']
movies.forEach((a) => {
img = a['image']
title = a['title']
desc = a['desc']
star = a['star']
star_img = '⭐'.repeat(star)
comment = a['comment']
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${img}" class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p>${star_img}</p>
<p class="mycomment">${comment}</p>
</div>
</div>
</div>`
$("#newcards").append(temp_html)
})
})
}
function posting() {
let url = $("#url").val()
let star = $("#star").val()
let comment = $("#comment").val()
let formData = new FormData();
formData.append('url_give', url)
formData.append('star_give', star)
formData.append('comment_give', comment)
fetch('/movie', {method: "POST", body: formData}).then(res=> res.json()).then(data => {
alert(data['msg'])
window.location.reload()
})
}
</script>
</head>
<body>
<div class="title_all">
<h1 class="mytitle">노엘피디아</h1>
<div>
<h3> 현재 서울의 날씨 : <span id="temp">20</span>도 </h3>
</div>
<button class="mybtn_comment">영화 기록하기</button>
</div>
<div class="newpost">
<div class="form-floating mb-3">
<input id="url" type="email" class="form-control" id="floatingInput" placeholder="name@example.com" />
<label for="floatingInput">영화 URL</label>
</div>
<div class="new_star">
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">별점</label>
<select id="star" class="form-select">
<option selected>-- 선택하기 --</option>
<option value="1">⭐</option>
<option value="2">⭐⭐</option>
<option value="3">⭐⭐⭐</option>
<option value="4">⭐⭐⭐⭐</option>
<option value="5">⭐⭐⭐⭐⭐</option>
</select>
</div>
</div>
<div class="form-floating">
<textarea id="comment" class="form-control" placeholder="Leave a comment here" id="floatingTextarea2"
style="height: 100px"></textarea>
<label for="floatingTextarea2">Comments</label>
</div>
<div class="mybtn">
<button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
<button type="button" class="btn btn-outline-dark">닫기</button>
</div>
</div>
<div class="mycards">
<div id="newcards" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F0e371de6f342a66143c49af3dd2b204342bbb5aa"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">라라랜드</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정…<br />
이 곳에서 모든 감정이 폭발한다!
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="mycomment">City of Stars</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>