2023.01.18 개발일지 - 스파르타피디아 proj

클로이🖤·2023년 1월 18일
0

Web-developer

목록 보기
20/22
post-thumbnail

스파르타피디아

오늘은 지난 주차에 완성했던 영화 사이트를 활용해서 프로젝트를 완성해 보려고 한다.
완성된 페이지는 스파르타피디아 여기서 예시 코드를 확인할 수 있다.

📌project setting

1) flask 폴더 구조 생성

  • 03.pedia 폴더
    app.py 파일 생성
    templates 폴더 생성
        └index.html 파일 생성

2) 가상환경 생성, 활성화

  • 화면 상단 Terminal > New Terminal 클릭
  • python -m venv venv 입력, 엔터
  • 화면 오른쪽 하단 파이썬 버전 선택 - venv 선택
  • 터미널 닫고 새 터미널 생성, (venv)라고 뜨면 활성화 완료

3) 패키지 설치
✔ 이번에 필요한 패키지는 3개 :flask, pymongo, dnspython, bs4, requests
여러 개를 설치할 때는 띄어쓰기로 구분하기 !

  • 패키지 설치 코드 : pip install flask pymongo dnspython requests bs4
    ✔ 원하는 라이브러리 확인하려면 ?
  • 터미널에서 pip freeze 입력하고 엔터 !

웹스크래핑을 사용해 URL에서 페이지 정보 가져오기

1) meta 태그를 활용하기

  • meta태그는 눈에 보이는 것 이외의 웹의 속성을 설명해주는 tag
  • 예시 : 아래에서 보이는 세 가지 정보들도 meta 태그의 역할 중 하나 !
  1. 썸네일 사진 - og:image
  2. 썸네일 제목 - og:title
  3. 썸네일 설명 - og:description

2) meta 태그 정보 가져오는 조각기능 만들기

  • 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 주소

POST 연습 (포스팅 하기)

  1. API 만들고 사용하기 - 포스팅 API (Create → POST)

1) 데이터 명세

  • 요청 정보 : URL= /movie, 요청 방식 = POST
  • 클라(fetch) → 서버(flask) : url, comment
  • 서버(flask) → 클라(fetch) : 메세지를 보냄(포스팅 완료!)

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(); //새로고침 !
          });

GET 연습 (보여주기)

  1. API 만들고 사용하기 - 포스트 보여주기 API(Read → GET)

1) 데이터 명세

  • 요청 정보 : URL= /movie, 요청 방식 = GET
  • 클라(fetch) → 서버(flask) : 없음
  • 서버(flask) → 클라(fetch) : 전체 주문을 보내주기

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하면 끝 ! 확인해보면

이렇게 화면에 똑바로 나오는 걸 확인 ! 다음에 별점 수정하는 것만 하면 될듯 !!

profile
front-end developer

0개의 댓글