본문 바로가기
들은 강의

[웹개발 입문 강의] [프로젝트 실습] 스파르타피디아 페이지

by hotdog7778 2023. 6. 12.

스파르타 코딩클럽 웹개발 종합반 인강 - 공부 기록

 

수강 시작 ~ 

2023. 06. 06 ~ 

 

https://github.com/hotdog7778/sparta


영화 리뷰를 기록하는 간단한 페이지를 만드는 프로젝트

 

프로젝트 폴더 구성

  • app.py 파일 생성
  • venv 구성
  • templates 디렉토리 생성
  • index.html 파일 생성

 

라이브러리 설치

  • pip install flask pymongo dnspython requests bs4

 

완성작 부터 보기

영화 기록하기를 누르고 영화URL, 별점, 코멘트를 기록하면 해당 내용이 하단에 카드로 생김.

 

 

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

meta 태그를 활용

meta 태그를 가져오는 조각기능 만들기

meta_parc.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')

# 여기에 코딩을 해서 meta tag를 먼저 가져와보겠습니다.
# 어느 사이트이던 공통된요소인 메타태그를 크롤링 해오는 일을 하는것입니다.
ogtitle = soup.select_one('meta[property="og:title"]')
print(ogtitle) # <meta content="스즈메의 문단속" property="og:title"/>

ogtitle = soup.select_one('meta[property="og:title"]')['content']
print(ogtitle) # 스즈메의 문단속

print("####################")

ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogimage = soup.select_one('meta[property="og:image"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
print(ogtitle,ogimage,ogdesc)

 

 

 

뼈대 만들기

GET, POST 연결이 정상적으로 되는 app.py, index.html 파일

 

 

POST 기능

먼저, URL과 코멘트를 채워서 작성하기를 누르면 크롤링 해온 데이터를 DB 에 쌓는것 까지

백엔드, 프론트엔드

 

GET 기능

로딩이 완료되면 카드들을 만들어서 화면에 표시 되도록 해보자.

 

별점도 추가

프론트엔드에 별점 추가 박스 만들기.

GET, POST 기능을 추가하기.

 

app.py

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.zlnt7r2.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta 

import requests
from bs4 import BeautifulSoup

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/movie", methods=["POST"])
def movie_post():
    # URL과 코멘트를 받아야 한다.
    # sample_receive = request.form['sample_give']
    # print(sample_receive)
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']
    star_receive = request.form['star_give']

    # 받아온 URL을 기반으로 크롤링을 해야한다.
    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']
    ogimage = soup.select_one('meta[property="og:image"]')['content']
    ogdesc = soup.select_one('meta[property="og:description"]')['content']
    
    # 크롤링 해온 og~~~ 들을 DB에 넣어줘야 한다.
    doc = {
        'title':ogtitle,
        'desc':ogdesc,
        'image':ogimage,
        #'url':url_receive,
        'star':star_receive,
        'comment':comment_receive
    }

    db.movies.insert_one(doc)
    
    # return jsonify({'msg':'POST 연결 완료!'})
    return jsonify({'msg':'저장 완료!'})

@app.route("/movie", methods=["GET"])
def movie_get():

    # db에서 이미지,코멘트,설명을 가져와서 프론트로 전달해주자.
    all_movies = list(db.movies.find({},{'_id':False}))
    return jsonify({'result':all_movies})
    # return jsonify({'msg':'GET 연결 완료!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5001, debug=True)

 

 

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>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mytitle {
            width: 100%;
            height: 250px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mytitle>button {
            width: 200px;
            height: 50px;

            background-color: transparent;
            color: white;

            border-radius: 50px;
            border: 1px solid white;

            margin-top: 10px;
        }

        .mytitle>button:hover {
            border: 2px solid white;
        }

        .mycomment {
            color: gray;
        }

        .mycards {
            margin: 20px auto 0px auto;
            width: 95%;
            max-width: 1200px;
        }

        .mypost {
            width: 95%;
            max-width: 500px;
            margin: 20px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px gray;

            display: none;
        }

        .mybtns {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-top: 20px;
        }

        .mybtns>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'])

                let rows = data['result']
                $('#cards-box').empty() // 시작 전 카드박스를 지워주기

                // console.log(rows)
                rows.forEach((a) => {
                    let comment = a['comment']
                    let title = a['title']
                    let desc = a['desc']
                    let image = a['image']
                    let star = a['star']
                    // start의 숫자만큼 별 아이콘을 만들어주는 방법
                    let star_repeat = '⭐'.repeat(star)
                    // console.log(comment,title,desc,image)

                    // 카드박스를 만들어야 한다.
                    let temp_html = `<div class="col">
                                        <div class="card h-100">
                                            <img src="${image}"
                                                class="card-img-top">
                                            <div class="card-body">
                                                <h5 class="card-title">${title}</h5>
                                                <p class="card-text">${desc}</p>
                                                <p>${star_repeat}</p>
                                                <p class="mycomment">${comment}</p>
                                            </div>
                                        </div>
                                    </div>`
                    $('#cards-box').append(temp_html)

                })
            })
        }

        function posting() {
            // URL 과 코멘트 작성한 것을 찾아와야 합니다.
            let url = $('#url').val() // url 이란 인풋 태그에서 가져옴
            let comment = $('#comment').val() // comment 이란 인풋 태그에서 가져옴
            let star = $('#star').val()

            // FormData 에 url과 코멘트를 태워 보내자
            let formData = new FormData();
            // formData.append("sample_give", "샘플데이터");
            formData.append("url_give", url);
            formData.append("comment_give", comment);
            formData.append("star_give", star);

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                // console.log(data)
                alert(data['msg'])
                window.location.reload() // 새로고침
            })
        }

        function open_box() {
            $('#post-box').show()
        }
        function close_box() {
            $('#post-box').hide()
        }
    </script>
</head>

<body>
    <div class="mytitle">
        <h1>내 생애 최고의 영화들</h1>
        <button onclick="open_box()">영화 기록하기</button>
    </div>
    <div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="input-group mb-3">
            <label class="input-group-text" for="inputGroupSelect01">별점</label>
            <select class="form-select" id="star">
                <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 class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>
        <div class="mybtns">
            <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="mycards">
        <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="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>