Python 찍먹 - Flask - 메모 포스팅 api 만들기

조해빈·2023년 1월 24일
0

짧은이론

목록 보기
9/23

app.py

from flask import Flask, render_template, jsonify, request
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient  # pymongo를 임포트 하기(패키지 인스톨 먼저 해야겠죠?)

app = Flask(__name__)

client = MongoClient('localhost', 27017)  # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta  # 'dbsparta'라는 이름의 db를 만들거나 사용합니다.

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

@app.route('/memo', methods=['POST'])
def post_article():
    # 1. 클라이언트로부터 데이터를 받기
    url_receive = request.form['url_give']  # 클라이언트로부터 url을 받는 부분
    comment_receive = request.form['comment_give']  # 클라이언트로부터 comment를 받는 부분

    # 2. meta tag를 스크래핑하기
    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')

    og_image = soup.select_one('meta[property="og:image"]')
    og_title = soup.select_one('meta[property="og:title"]')
    og_description = soup.select_one('meta[property="og:description"]')

    url_title = og_title['content']
    url_description = og_description['content']
    url_image = og_image['content']

    article = {'url': url_receive, 'title': url_title, 'desc': url_description, 'image': url_image,
               'comment': comment_receive}

    # 3. mongoDB에 데이터를 넣기
    db.articles.insert_one(article)

    return jsonify({'result': 'success'})


@app.route('/memo', methods=['GET'])
def read_articles():
    # 1. mongoDB에서 _id 값을 제외한 모든 데이터 조회해오기 (Read)
    result = list(db.articles.find({}, {'_id': 0}))
    # 2. articles라는 키 값으로 article 정보 보내주기
    return jsonify({'result': 'success', 'articles': result})

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

index.html

<!Doctype html>
<html lang="ko">
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
    integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
  <!-- JS -->
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
  <script src="../static/index.js"></script>
  <link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">
  <title>SW사관학교 정글 | 나홀로 메모장</title>
  <!-- style -->
  <style type="text/css">
    * {
      font-family: "Stylish", sans-serif;
    }

    .wrap {
      width: 900px;
      margin: auto;
    }

    .comment {
      color: blue;
      font-weight: bold;
    }

    #post-box {
      width: 500px;
      margin: 20px auto;
      padding: 50px;
      border: black solid;
      border-radius: 5px;
    }
  </style>
</head>

<body>
  <div class="wrap">
    <div class="jumbotron">
      <h1 class="display-4">나홀로 링크 메모장!</h1>
      <p class="lead">중요한 링크를 저장해두고, 나중에 볼 수 있는 공간입니다</p>
      <hr class="my-4">
      <p class="lead">
        <button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기
        </button>
      </p>
    </div>
    <div id="post-box" class="form-post" style="display:none">
      <div>
        <div class="form-group">
          <label for="post-url">아티클 URL</label>
          <input id="post-url" class="form-control" placeholder="">
        </div>
        <div class="form-group">
          <label for="post-comment">간단 코멘트</label>
          <textarea id="post-comment" class="form-control" rows="2"></textarea>
        </div>
        <button type="button" class="btn btn-primary" onclick="postArticle()">기사저장</button>
      </div>
    </div>
    <div id="cards-box" class="card-columns">
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
      <div class="card">
        <img class="card-img-top"
          src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
          alt="Card image cap">
        <div class="card-body">
          <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
          <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
          <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

index.html 의 js


    $(document).ready(function () {
      showArticles();
    });

    function openClose() {
      // id 값 post-box의 display 값이 block 이면(= 눈에 보이면)
      if ($("#post-box").css("display") == "block") {
        // post-box를 가리고
        $("#post-box").hide();
        // 다시 버튼을 클릭하면, 박스 열기를 할 수 있게 텍스트 바꿔두기
        $("#btn-post-box").text("포스팅 박스 열기");
      } else {
        // 아니면(눈에 보이지 않으면) post-box를 펴라
        $("#post-box").show();
        // 다시 버튼을 클릭하면, 박스 닫기를 할 수 있게 텍스트 바꿔두기
        $("#btn-post-box").text("포스팅 박스 닫기");
      }
    }
    function postArticle() {
      let url = $("#post-url").val();
      let comment = $("#post-comment").val();

      // 2. memo에 POST 방식으로 메모 생성 요청하기
      $.ajax({
        type: "POST", // POST 방식으로 요청하겠다.
        url: "/memo", // /memo라는 url에 요청하겠다.
        data: {
          url_give: url,
          comment_give: comment
        }, // 데이터를 주는 방법
        success: function (response) { // 성공하면
          if (response["result"] == "success") {
            alert("포스팅 성공!");
            // 3. 성공 시 리스트 새로고침하기
            showArticles();
          } else {
            alert("서버 오류!")
          }
        }
      })
    }
    function showArticles() {
      $("#cards-box").html("");
      $.ajax({
        type: "GET",
        url: "/memo",
        data: {},
        success: function (response) {
          let articles = response["articles"];
          console.log(articles);
          for (let i = 0; i < articles.length; i++) {
            makeCard(articles[i]["image"], articles[i]["url"], articles[i]["title"], articles[i]["desc"],
              articles[i]["comment"]);
          }
        }
      });
    }
    function makeCard(image, url, title, desc, comment) {
      let tempHtml = `<div class="card">
                        <img class="card-img-top" src="${image}" alt="Card image cap">
                        <div class="card-body">
                        <a href="${url}" target="_blank" class="card-title">${title}</a>
                        <p class="card-text">${desc}</p>
                        <p class="card-text comment">${comment}</p>
                        </div>
                    </div>`;
      $("#cards-box").append(tempHtml);
    }

분석

1. app.py 상단

  • 패키지 및 메소드의 기본적 임포팅 및 home 라우팅

2. 카드생성

포스팅API - 카드 생성 (Create) : 클라이언트에서 받은 url, comment를 이용해서 페이지 정보를 찾고 저장하기

(ajax, HTTP request method POST 방식으로 클라이언트로부터 데이터를 받기)

아래 정리된 내용을 참고한다.

2-A. 요청 정보 (Create)

	data: {
 	  url_give: $("#post-url").val(),
 	  comment_give: $("#post-comment").val()
	}, // 데이터를 주는 방법

2-B,C. 서버가 제공할 기능, 응답 데이터

client = MongoClient('localhost', 27017)
db = client.dbsparta

데이터가 변수 article 에 저장, 그것을 db.articles에 insert_one(). 즉, db의 articles 폴더에 저장.

2. 저장된 카드 보여주기

리스팅API - 저장된 카드 보여주기 (Read)

(ajax, HTTP request method POST 방식으로 클라이언트로부터 데이터를 받기)

  • index.js
    response받기. 만들어놨던 카드생성함수인 makeCard() 함수에 인자로 가져온 response["articles"]의 i번째 요소의 image, url, title, desc, comment 정보 뿌리기.
  • app.py
    변수 result 에 id 값을 제외한 모든 데이터 조회해와 저장.
    articles라는 키 값으로 article 정보를 jsonify()하기.

    http://localhost:5000/memo에 접속했을 때, 결과가 JSON 형식으로 보이면 성공입니다. 브라우저에서 주소창에 엔터를 치는 것이 GET 요청과 같다는 사실, 기억하시죠??

test

네이버 메인 아무거나 들어가서 포스트 주소를 가져왔다.
https://post.naver.com/viewer/postView.naver?volumeNo=33033414&memberNo=48374180
결과는 다음과 같이 잘 작동한다.


스게... 신기하다.

profile
JS, CSS, HTML, React etc

0개의 댓글