Sparta/스파르타코딩 웹개발 종합반

웹개발 종합반 프로젝트 - 05 진행상황&로그인&뉴스크롤링

또롱또 2022. 3. 13. 00:46
728x90

로그인페이지, 회원가입페이지, 메인페이지, 총 3가지 페이지가 준비되었다

로그인 페이지 코드 (클라이언트)

<!doctype html>
<html lang="en">
<head>

    <!-- Webpage Title -->
    <title>Log In | SWEETER</title>
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <meta property="og:title" content="MSS - My Small Space"/>
    <meta property="og:description" content="mini project for Web Plus"/>
    <meta property="og:image" content="{{ url_for('static', filename='ogimg.png') }}"/>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bulma CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
    <!-- Font Awesome CSS -->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <!-- Google Font -->
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Gamja+Flower&family=Stylish&display=swap" rel="stylesheet">

    <!-- JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>


    <style>
        .title {
            font-weight: 800;
            font-size: 5rem;
            font-family: 'Stylish', sans-serif;
        }

        .subtitle {
            font-family: 'Gamja Flower', cursive;
            font-size: 2rem;

        }

        .help {
            color: gray;
        }
    </style>
    <script>
        // .toggleClass 로 열닫 기능
        function toggle_sign_up() {
            $("#sign-up-box").toggleClass("is-hidden")
            $("#div-sign-in-or-up").toggleClass("is-hidden")
            $("#btn-check-dup").toggleClass("is-hidden")
            $("#help-id").toggleClass("is-hidden")
            $("#help-password").toggleClass("is-hidden")
            $("#help-password2").toggleClass("is-hidden")
        }

        // 정규 표현식
        function is_nickname(asValue) {
            var regExp = /^(?=.*[a-zA-Z])[-a-zA-Z0-9_.]{6,15}$/;
            // (?=.*[a-zA-Z]) - a-z 혹은 A-Z가 꼭 있어야하고
            // [-a-zA-Z0-9_.] - 나머지는 a-z, A-Z, 0-9, _, . 까지 있어도 되고
            // {2,10} 최소 2자리에서 최대 10자리 까지 이다.
            return regExp.test(asValue);
        }
        function is_password(asValue) {
            var regExp = /^(?=.*\d)(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
            return regExp.test(asValue);
        }

        function check_dup() {
            // input 값 받기
            let username = $("#input-username").val()
            console.log(username)
            // input 값이 아무것도 없을경우
            if (username == "") {
                $("#help-id").text("Enter Your ID").removeClass("is-safe").addClass("is-danger")
                $("#input-username").focus()
                return;
            }
            // input값을 위에서 만든 정규표현식 id에 넣었는데 원하는 아이디 형식이 아닐경우
            if (!is_nickname(username)) {
                $("#help-id").text("Please Check Your ID").removeClass("is-safe").addClass("is-danger")
                $("#input-username").focus()
                return;
            }
            // 그 외에 아이디 생성이 가능할때
            $("#help-id").addClass("is-loading")
            $.ajax({
                type: "POST",
                url: "/sign_up/check_dup",
                data: {
                    username_give: username
                },
                success: function (response) { // db에 있는 아이디 일 경우
                    // 아이디가 이미 db에 있는지 체크
                    if (response["exists"]) {
                        $("#help-id").text("Already Used ID").removeClass("is-safe").addClass("is-danger")
                        $("#input-username").focus()
                    } else { // 없는경우, .addClass("is-success")를 추가해서, 회원가입전에 중복확인이 성공적인지 체크가 가능
                        $("#help-id").text("Able to Use This ID").removeClass("is-danger").addClass("is-success")
                    }
                    $("#help-id").removeClass("is-loading")

                }
            });
        }

        function sign_up() {
            let username = $("#input-username").val()
            let password = $("#input-password").val()
            let password2 = $("#input-password2").val()
            console.log(username, password, password2)


            if ($("#help-id").hasClass("is-danger")) {
                alert("Please Check Your ID Again")
                return;
            } else if (!$("#help-id").hasClass("is-success")) {
                alert("Please Check If Your ID is Duplicated.")
                return;
            }

            if (password == "") {
                $("#help-password").text("Enter Your Password").removeClass("is-safe").addClass("is-danger")
                $("#input-password").focus()
                return;
            } else if (!is_password(password)) {
                $("#help-password").text("Please Check Your Password Type").removeClass("is-safe").addClass("is-danger")
                $("#input-password").focus()
                return
            } else {
                $("#help-password").text("Able to Use This Password").removeClass("is-danger").addClass("is-success")
            }
            if (password2 == "") {
                $("#help-password2").text("Enter Your Password").removeClass("is-safe").addClass("is-danger")
                $("#input-password2").focus()
                return;
            } else if (password2 != password) {
                $("#help-password2").text("Invaild Password").removeClass("is-safe").addClass("is-danger")
                $("#input-password2").focus()
                return;
            } else {
                $("#help-password2").text("Valid Password").removeClass("is-danger").addClass("is-success")
            }
            $.ajax({
                type: "POST",
                url: "/sign_up/save",
                data: {
                    username_give: username,
                    password_give: password
                },
                success: function (response) {
                    alert("Thank You for Signing Up")
                    window.location.replace("/login")
                }
            });

        }

        function sign_in() {
            let username = $("#input-username").val()
            let password = $("#input-password").val()

            if (username == "") {
                $("#help-id-login").text("Enter Your ID.")
                $("#input-username").focus()
                return;
            } else {
                $("#help-id-login").text("")
            }

            if (password == "") {
                $("#help-password-login").text("Enter Your Password")
                $("#input-password").focus()
                return;
            } else {
                $("#help-password-login").text("")
            }
            // API를 보내기
            $.ajax({
                type: "POST",
                url: "/sign_in",
                data: {
                    // ID와 비밀번호
                    username_give: username,
                    password_give: password
                },
                // 보내기에 성공하면,
                success: function (response) {
                    if (response['result'] == 'success') {
                        // 제이쿼리로 쿠키 생성
                        $.cookie('mytoken', response['token'], {path: '/'});
                        window.location.replace("/")
                        //href는 페이지를 이동하는 것이기 때문에 뒤로가기 버튼을 누른경우 이전 페이지로 이동이 가능하지만,
                        // replace는 현재 페이지를 새로운 페이지로 덮어 씌우기 때문에 이전 페이지로 이동이 불가능하다.
                    } else {
                        alert(response['msg'])
                    }
                }
            });
        }


    </script>

</head>
<body style="background-color: #F2F2F2 !important; ">
    <section class="hero is-white" >
        <div class="hero-body has-text-centered" style="padding-bottom:1rem;margin:auto;">
            <h1 class="title is-sparta">M S S</h1>
            <h3 class="subtitle is-sparta">My Small Space</h3>
        </div>
    </section>
    <section class="section">
        <div class="container">
            <div class="box" style="max-width: 480px;margin:auto">
                <article class="media">
                    <div class="media-content">
                        <div class="content">
                            <div class="field has-addons">
                                <div class="control has-icons-left" style="width:100%">
                                    <input id="input-username" class="input" type="text" placeholder="ID or Nick Name">
                                    <span class="icon is-small is-left"><i class="fa fa-user"></i></span>
                                </div>
                                <div id="btn-check-dup" class="control is-hidden">
                                    <button class="button is-sparta" onclick="check_dup()">Duplication Check</button>
                                </div>

                            </div>
                            <p id="help-id" class="help is-hidden">At Least 6 Letters of English or Numbers or Special
                                Letters( . _ ),
                                <br>Maximum 15 Letters</p>
                            <p id="help-id-login" class="help is-danger"></p>

                            <div class="field">
                                <div class="control has-icons-left">
                                    <input id="input-password" class="input" type="password" placeholder="Password">
                                    <span class="icon is-small is-left"><i class="fa fa-lock"></i></span>
                                </div>
                                <p id="help-password" class="help is-hidden">At Least 8 Letters of English or Numbers or
                                    Special Letters( !@#$%^&* ), <br>Maximum 20 Letters
                                </p>
                                <p id="help-password-login" class="help is-danger"></p>

                            </div>


                        </div>
                        <div id="div-sign-in-or-up" class="has-text-centered">
                            <nav class="level is-mobile">
                                <button class="level-item button is-sparta" onclick="sign_in()">
                                    Sign In
                                </button>

                            </nav>
                            <hr>
                            <h4 class="mb-3">Want to be a Member of MSS?</h4>
                            <nav class="level is-mobile">

                                <button class="level-item button is-sparta is-outlined"
                                        onclick="toggle_sign_up()">
                                    Sign Up
                                </button>
                            </nav>
                        </div>

                        <div id="sign-up-box" class="is-hidden">
                            <div class="mb-5">
                                <div class="field">
                                    <div class="control has-icons-left" style="width:100%">
                                        <input id="input-password2" class="input" type="password"
                                               placeholder="Verify Password">
                                        <span class="icon is-small is-left"><i class="fa fa-lock"></i></span>
                                    </div>
                                    <p id="help-password2" class="help is-hidden">Verify Password</p>

                                </div>
                            </div>
                            <nav class="level is-mobile">
                                <button class="level-item button is-sparta" onclick="sign_up()">
                                    Submit
                                </button>
                                <button class="level-item button is-sparta is-outlined" onclick="toggle_sign_up()">
                                    Cancel
                                </button>
                            </nav>
                        </div>


                    </div>
                </article>
            </div>

        </div>
    </section>

</body>
</html>

로그인페이지 코드(서버)

from pymongo import MongoClient
import jwt
import datetime
import hashlib
from flask import Flask, render_template, jsonify, request, redirect, url_for
from werkzeug.utils import secure_filename
from datetime import datetime, timedelta



app = Flask(__name__) # Flask 객체를 app이라는 변수에 할당
#config안의 속성들의 값을 조절
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['UPLOAD_FOLDER'] = "./static/profile_pics"

SECRET_KEY = 'SPARTA' # 로그인 토큰 만들때 secret key

# Mongo DB에 연결
client = MongoClient('13.124.196.127', 27017, username="test", password="test")
db = client.dbsparta_project_01_mss

# main page
@app.route('/')
def home():
    # 생성된 쿠키 가져오기
    token_receive = request.cookies.get('mytoken')
    try: #쿠키를 이용해서 jwt 자유이용권 만듬
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        # 자유이용권에 등록된 유저 id를 가져오기
        user_info = db.users.find_one({"username": payload["id"]})
        # 유저 정보를 jinja로 가져다 쓸수있게 return 해줌
        return render_template('index.html', user_info=user_info)
    
    # 에러처리
    except jwt.ExpiredSignatureError:
        return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
    except jwt.exceptions.DecodeError:
        return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))

# 로그인 페이지
@app.route('/login')
def login():
    msg = request.args.get("msg")
    return render_template('login.html', msg=msg)

# 로그인
@app.route('/sign_in', methods=['POST'])
def sign_in():
 
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']

    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    result = db.users.find_one({'username': username_receive, 'password': pw_hash})

    if result is not None:
        payload = {
         'id': username_receive,
         'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

        return jsonify({'result': 'success', 'token': token})
    # 찾지 못하면
    else:
        return jsonify({'result': 'fail', 'msg': 'Wrong ID/PASSWORD'})

# 회원가입
@app.route('/sign_up/save', methods=['POST'])
def sign_up():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    doc = {
        "username": username_receive,
        "password": password_hash,
        "profile_name": username_receive,
        "profile_pic": "",
        "profile_pic_real": "profile_pics/profile_placeholder.png",
        "profile_info": ""
    }
    db.users.insert_one(doc)
    return jsonify({'result': 'success'})

# 중복확인
@app.route('/sign_up/check_dup', methods=['POST'])
def check_dup():
    # id 중복확인
    username_receive = request.form['username_give'] #request form으로 username을 받고
    # bool 값을 사용해서, db에 username이 있는지 체크
    exists = bool(db.users.find_one({"username": username_receive}))
    # bool 값을 넘겨줘서 있다 없다를 체크 가능
    return jsonify({'result': 'success', 'exists': exists})

다음으로는 네이버 뉴스를 크롤링 해오고 있다. 아직 오류가 많아 수정을 더 해야하지만, 

기본적인 title과 시간은 가져올 수 있다. 일단은 크롤링해서 DB에 저장한다.

python 코드

from selenium import webdriver
from bs4 import BeautifulSoup
import time
from selenium.common.exceptions import NoSuchElementException
from pymongo import MongoClient
import requests

# 몽고 db에 저장
client = MongoClient('13.124.196.127', 27017, username="test", password="test")
db = client.dbsparta_project_01_mss

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('https://news.naver.com/main/ranking/popularDay.naver',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')


news = soup.select('#wrap > div.rankingnews._popularWelBase._persist > div.rankingnews_box_wrap._popularRanking > div > div')

for data in news:
    title = data.select_one('ul > li > div > a').text;
    time = data.select_one('ul > li > div > span').text;
    print(title, time);
    # 찾은 내용들을 db에 저장하기
    doc = {
        "title": title,
        "time": time,
    }
    db.news.insert_one(doc)

클라이언트 쪽에서는 페이지가 로딩될때마다 뉴스를 가져오게 해 두었다.

     function get_news() {
            $.ajax({
                type: "GET",
                url: `/news`,
                data: {},
                success: function (response) {
                    let news = response["news_list"];
                    {#console.log(news);#}
                    for (let i = 0; i < news.length; ++i) {
                        let title = news[i]['title'];
                        let time = news[i]['time']
                        console.log(title)
                        let html_temp = `   <ul class="news-list">
                                                <li>${title}</li>
                                                <span>${time}</span>
                                            </ul>`

                        // add html
                        $('#news').append(html_temp)
                    }
                }
            });
        }

 

아래는 현재 진행상황이다.

728x90