오늘은 지난 주차에 완성했던 영화 사이트를 활용해서 프로젝트를 완성해 보려고 한다.
완성된 페이지는 스파르타피디아 여기서 예시 코드를 확인할 수 있다.
📌project setting
1) flask 폴더 구조 생성
03.pedia
폴더app.py
파일 생성templates
폴더 생성index.html
파일 생성2) 가상환경 생성, 활성화
Terminal > New Terminal
클릭python -m venv venv
입력, 엔터venv
선택3) 패키지 설치
✔ 이번에 필요한 패키지는 3개 :flask
, pymongo
, dnspython
, bs4
, requests
여러 개를 설치할 때는 띄어쓰기로 구분하기 !
pip freeze
입력하고 엔터 !1) meta 태그를 활용하기
og:image
og:title
og:description
2) meta 태그 정보 가져오는 조각기능 만들기
meta_prac.py
파일 만들기 (크롤링 기본 코드 ↓)import requests
from bs4 import BeautifulSoup
url = 'https://movie.naver.com/movie/bi/mi/basic.naver?code=65923'
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')
# 여기에 코딩을 해서 meta tag를 먼저 가져와보겠습니다.
url 주소에서 ctrl 누른 상태에서 click하면 페이지가 열린다. 이 페이지에서 F12 ! 그럼 아래 사진처럼 창이 열리는데
<head>...</head>
부분을 열어보면 meta 태그들이 쭉 나오는 것을 확인할 수 있다. 그럼 이제 meta태그를 크롤링 해보자. select_one을 사용할 것
title meta 태그는 <meta property="og:title" content="라 비 앙 로즈">
이렇게 생겼다.
og_image = soup.select_one('meta[property="og:image"]')['content']
og_title = soup.select_one('meta[property="og:title"]')['content']
og_description = soup.select_one('meta[property="og:description"]')['content']
print(og_image,og_title,og_description)
title을 예로 들어보면, 일단 og_title
이라는 변수 만들어서, soup을 가져오는데, 여기서 select_one
: 하나를 선택해서, meta[property="og:title"]
meta태그에서 property가 og:title인 것 중에서 우리가 필요한 내용이 들어가 있는 content
를 가져오라는 뜻. title만 일단 print 해보면
이렇게 터미널 창에 우리가 원하는 내용이 출력되는 것을 확인할 수 있다. 연습은 끝 ! 이제 뼈대 구축하고 프로젝트 만들 준비 !!
1) app.py 준비하기
📌 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 준비하기
📌 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"
/>
<title>movie rating</title>
<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>
<style>
@import url("https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap");
* {
font-family: "Gowun Dodum", sans-serif;
}
.title {
color: white;
height: 250px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-image: linear-gradient(
0deg,
rgba(0, 0, 0, 0.5),
rgba(0, 0, 0, 0.5)
),
url("https://images.unsplash.com/photo-1543158266-0066955047b1?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80");
background-position: center;
background-size: cover;
}
.title > button {
width: 250px;
height: 50px;
background-color: transparent;
border: 1px solid white;
border-radius: 50px;
color: white;
margin-top: 20px;
}
.title > button:hover {
border: 3px solid white;
}
.quote {
color: grey;
}
.cards {
width: 2200px;
margin: 40px auto 0 auto;
}
.post {
width: 500px;
margin: 20px auto 20px auto;
padding: 20px 20px 5px 20px;
box-shadow: 0px 0px 3px 0px gray;
}
.quote {
color: darkolivegreen;
}
.btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 5px;
}
.btn > button {
margin-right: 10px;
}
</style>
<script>
$(document).ready(function () {
listing();
});
function listing() {
fetch("/movie")
.then((res) => res.json())
.then((data) => {
console.log(data);
alert(data["msg"]);
});
}
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"]);
});
}
function open_box() {
$("#post-box").show();
}
function close_box() {
$("#post-box").hide();
}
</script>
</head>
<body>
<div class="title">
<h1>내가 애정하는 영화들</h1>
<button onclick="open_box()">영화 기록하기</button>
</div>
<div class="post" id="post-box">
<div class="form-floating mb-3">
<input
id="url"
type="email"
class="form-control"
placeholder="name@example.com"
/>
<label for="floatingInput">movie URL</label>
</div>
<div class="form-floating">
<textarea
class="form-control"
placeholder="Leave a comment here"
id="comment"
></textarea>
<label for="floatingTextarea">Comments</label>
</div>
<div class="btn">
<button onclick="posting()" type="button" class="btn btn-dark">
기록하기
</button>
<button
onclick="close_box()"
type="button"
class="btn btn-outline-dark"
>
닫기
</button>
</div>
</div>
<div class="cards">
<div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
<div class="col">
<div class="card h-100">
<img
src="http://img.movist.com/?img=/x00/00/88/75_p1.jpg"
class="card-img-top"
alt="..."
/>
<div class="card-body">
<h5 class="card-title">The Notebook</h5>
<p class="card-text">
17살, ‘노아’는 밝고 순수한 ‘앨리’를 보고 첫눈에 반한다. 빠른
속도로 서로에게 빠져드는 둘. 그러나 이들 앞에 놓인 장벽에 막혀
이별하게 된다.<br />
24살, ‘앨리’는 우연히 신문에서 ‘노아’의 소식을 접하고 잊을 수
없는 첫사랑 앞에서 다시 한 번 선택의 기로에 서게 되는데… <br />
열일곱의 설렘, 스물넷의 아픈 기억, 그리고 마지막까지… 한 사람을
지극히 사랑했으니 내 인생은 성공한 인생입니다.
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="quote">"If you're a bird, I'm a bird."</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img
src="https://movie-phinf.pstatic.net/20201229_146/1609226288425JgdsP_JPEG/movie_image.jpg"
class="card-img-top"
alt="..."
/>
<div class="card-body">
<h5 class="card-title">La La Land</h5>
<p class="card-text">
황홀한 사랑, 순수한 희망, 격렬한 열정… 이 곳에서 모든 감정이
폭발한다! 꿈을 꾸는 사람들을 위한 별들의 도시 ‘라라랜드’. 재즈
피아니스트 ‘세바스찬’(라이언 고슬링)과 배우 지망생 ‘미아’(엠마
스톤), 인생에서 가장 빛나는 순간 만난 두 사람은 미완성인 서로의
무대를 만들어가기 시작한다.
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="quote">
“City of stars. Are you shining just for me? City of stars.
There's so much that I can't see.”
</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img
src="https://movie-phinf.pstatic.net/20171121_75/1511230561589Rvq85_JPEG/movie_image.jpg"
class="card-img-top"
alt="..."
/>
<div class="card-body">
<h5 class="card-title">If Only</h5>
<p class="card-text">
오늘, 비로소 사랑을 알았어 눈앞에서 사랑하는 연인을 잃은 남자는
다음 날 아침, 자신의 옆에서 자고 있는 연인을 보고 소스라치게
놀란다. 기쁨도 잠시, 정해진 운명은 바꿀 수 없단 것을 깨달은 그는
더 늦기 전에 자신의 진정한 사랑을 전하기로 마음먹는데… 네가
아니었다면 난 영영 사랑을 몰랐을 거야 사랑하는 법을 알게 해줘서
고마워, 사랑받는 법도.
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="quote">
"Samantha if not for today, if not for you I would never have
known love at all... So thank you for being the person who
taught me to love... and to be love."
</p>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img
src="https://movie-phinf.pstatic.net/20170421_168/1492761621282U7CYn_JPEG/movie_image.jpg"
class="card-img-top"
alt="..."
/>
<div class="card-body">
<h5 class="card-title">The Devil Wears Prada</h5>
<p class="card-text">
화려한 뉴욕을 꿈꾸는 자, 그 무게를 견뎌라! 최고의 패션 매거진
‘런웨이’에 기적 같이 입사했지만 ‘앤드리아’(앤 해서웨이)에겐 이
화려한 세계가 그저 낯설기만 하다. 원래의 꿈인 저널리스트가 되기
위해 딱 1년만 버티기로 결심하지만 악마 같은 보스, ‘런웨이’
편집장 ‘미란다’(메릴 스트립)와 일하는 것은 정말 지옥 같은데…!!
24시간 울려대는 휴대폰, 자친구 생일도 챙기지 못할 정도의 풀
야근, 심지어 그녀의 쌍둥이 방학 숙제까지! 꿈과는 점점 멀어지고..
잡일 전문 쭈구리 비서가 된 '앤드리아' 오늘도 ‘미란다’의 칼 같은
질타와 불가능해 보이는 미션에 고군분투하는 ‘앤드리아’ 과연, 전쟁
같은 이곳에서 버틸 수 있을까?
</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="quote">
“Oh, don't be ridiculous. Andrea. Everybody wants this.
Everybody wants to be us.”
</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
3) mongoDB Atlas 창 띄워두기
📌 mongoDB 주소
1) 데이터 명세
/movie
, 요청 방식 = POST
url
, comment
포스팅 완료!
)2) 클라이언트와 서버 연결 확인하기
서버 코드 - app.py
@app.route("/movie", methods=["POST"])
def movie_post():
sample_receive = request.form['sample_give']
print(sample_receive)
return jsonify({'msg':'POST 연결 완료!'})
클라이언트 코드 - index.html
function posting() {
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) 서버 만들기
url, comment 정보 받아서 저장하면 된다. meta_prac.py
, dbtest.py
파일 참고해서 만들기 !
@app.route("/movie", methods=["POST"])
def movie_post():
url_receive = request.form['url_give']
comment_receive = request.form['comment_give']
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')
ogimage = soup.select_one('meta[property="og:image"]')['content']
ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
doc = {
'image':ogimage,
'title':ogtitle,
'desc':ogdesc,
'comment':comment_receive
}
db.movies.insert_one(doc)
return jsonify({'msg':'포스팅 완료!'})
이렇게 하면 백엔드 구축은 완료했다 ! ㅎㅎ
4) 클라이언트 만들기
클라이언트는 index.html
에서 만들기 !
formData에 url 이랑 comment를 태워서 보내야 하니까 일단 얘네를 가져와야 한다.
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"]);
});
}
url을 어디서 가져오냐면
<div class="form-floating mb-3">
<input
id="url"
type="email"
class="form-control"
placeholder="name@example.com"
/>
<label for="floatingInput">movie URL</label>
</div>
<div class="form-floating">
<textarea
class="form-control"
placeholder="Leave a comment here"
id="comment"
></textarea>
<label for="floatingTextarea">Comments</label>
</div>
index.html 파일에서 보면 url을 입력하는 부분에 input tag, Comments 입력하는 부분에 textarea 태그를 보면 id를 생성해놨다. 이걸 가져오면 된다. posting() 함수 안에
let url = $('#url').val();
let comment = $('#comment').val();
url이랑 comment 변수 만들어서 얘네의 값을 가져온다는 .val() 코드까지 작성 완료했으니 이걸 formData에 넣어서 append 해주면 될듯. sample_give 데이터를 가져올 필요 없으니 아까 py 파일에서 생성했던 url_give
에 url을 넣어주고 comment도 동일한 방식으로 진행.
let formData = new FormData();
//formData.append("sample_give", "샘플데이터");
formData.append("url_give", url);
formData.append("comment_give", comment);
그리고 밑에 fetch 코드 보면 이제 console 창에 찍어줄 필요 없으니 코드 날리고, alert 코드를 보면 msg를 띄우는데
fetch("/movie", { method: "POST", body: formData })
.then((res) => res.json())
.then((data) => {
//console.log(data);
alert(data["msg"]);
});
뭘 띄우나 보면 ( app.py
파일에서 확인 )
#return jsonify({'msg':'POST 연결 완료!'})
return jsonify({'msg':'POST 연결 완료!'})
POST 연결 완료라는 메세지를 return 하는데 우리는 굳이 이게 뜰 필요 없으니까 저장 완료 ! 라고 뜨게 바꿔줬다. 여기까지 저장하고 실행. localhost:5000
들어가서 url 창에다가 url 붙여넣고
mongoDB에 가보니까
내가 작성한 대로 title, desc, img, comment가 다 잘 저장되어 있는 것을 확인할 수 있다. 이제 이걸 기록하기 누르면 저장되고 페이지가 새로고침 됐으면 좋겠으니 코드를 한 줄 더 써준다.
fetch("/movie", { method: "POST", body: formData })
.then((res) => res.json())
.then((data) => {
alert(data["msg"]);
window.location.reload(); //새로고침 !
});
1) 데이터 명세
/movie
, 요청 방식 = GET
2) 클라이언트와 서버 확인하기
서버 코드 - app.py
@app.route("/movie", methods=["GET"])
def movie_get():
return jsonify({'msg':'GET 연결 완료!'})
클라이언트 코드 - index.html
$(document).ready(function(){
listing();
});
function listing() {
fetch('/movie').then((res) => res.json()).then((data) => {
console.log(data)
alert(data['msg'])
})
}
3) 서버 만들기
db에 저장되어 있는 다 가져와야 하니까 아래 코드를 사용한다.
#all_users = list(db.users.find({},{'_id':False})) 기본 코드
all_movies = list(db.movies.find({},{'_id':False}))
아래 GET methods 코드에 위 코드를 넣어주고
@app.route("/movie", methods=["GET"])
def movie_get():
all_movies = list(db.movies.find({},{'_id':False}))
#return jsonify({'msg':'GET 연결 완료!'}) 원래 코드를 아래 코드로 바꿔서 작성
return jsonify({'result':all_movies})
all_movies라는 data들을 db에서 가져올 변수를 만들었으니, 이걸 return 해줘야 한다. 'msg':'GET 연결 완료!'이걸 보내주는 게 아니라 result로 all_movies를 보내준다고 코드 바꿔서 작성해줬다. 그리고 이제 다시 index.html 파일로 가서 아래 코드를 보자.
4) 클라이언트 만들기
function listing() {
fetch("/movie")
.then((res) => res.json())
.then((data) => {
console.log(data);
alert(data["msg"]);
});
}
이 코드를 고쳐줘야한다. 일단 console이랑 alert 코드 지우기 !
function listing() {
fetch("/movie")
.then((res) => res.json())
.then((data) => {
let rows = data['result'];
console.log(rows);
});
}
여기서 data는 앞에 app.py
에거 작성했던 result : all_movies이다. rows라는 변수에 data에서 result 값을 가지고 오라는 코드 작성하고 console 창에 찍어보는 것까지 해본 결과
이렇게 콘솔창에 저장한 data가 list 형태로 찍히는 것을 확인할 수 있다. 이걸 이제 반복문을 돌려서 html에 append 해보자. desc, comment, title, img를 다 가져와야하니 반복문 내에 변수를 4개 만들자.
function listing() {
fetch("/movie")
.then((res) => res.json())
.then((data) => {
let rows = data["result"];
rows.forEach((film) => {
let comment = film["comment"];
let img = film["img"];
let desc = film["desc"];
let title = film["title"];
});
});
}
일단 이 반복문이 돌면서 붙어야될 부분이 어딘지 확인해보면
cards에서 이미 저장되어 있던 카드들을 쭉 접어서 확인해보면 id = "cards-box"
가 있다. 여기에 저 카드 하나를 복사해서 temp_html 변수에 넣고 넣을 내용을 수정해주면 될 거 같다. 해보자구 ~
이렇게 temp_html에 카드 하나 붙여넣어주고, 코드를 수정하자.
function listing() {
fetch("/movie")
.then((res) => res.json())
.then((data) => {
let rows = data["result"];
rows.forEach((film) => {
let comment = film["comment"];
let img = film["img"];
let desc = film["desc"];
let title = film["title"];
let temp_html = `<div class="col">
<div class="card h-100">
<img src="${img}" class="card-img-top"/>
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${desc}</p>
<p>⭐⭐⭐⭐⭐</p>
<p class="quote">${comment}</p>
</div>
</div>
</div>`;
$("#cards-box").append(temp_html);
});
});
}
$("#cards-box").append(temp_html); 이렇게 제이쿼리 사용해서 append하면 끝 ! 확인해보면
이렇게 화면에 똑바로 나오는 걸 확인 ! 다음에 별점 수정하는 것만 하면 될듯 !!