*/스파르타코딩클럽 웹개발종합반

[스파르타코딩클럽 | 웹개발종합반] 3주차: Python, 크롤링, mongoDB

sssbin 2021. 9. 13. 00:29

 

1. 복습: 나홀로메모장에 OpenAPI 붙여보기

  - 로딩 후 바로 실행

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

function listing() {
	console.log('화면 로딩 후 잘 실행되었습니다');
}

  - html 붙이기

let temp_html=`<div class="card">
			           <img class="card-img-top" src="${image}" alt="Card image cap">
                 <div class="card-body">
	                 <a class="card-title" href="${url}"> ${title} </a>
	                 <p class="card-text"> ${desc} </p>
	                 <p class="card-text comment"> ${comment} </p>
                 </div>
               </div>`
$('#cards-box').append(temp_html)


// ${ }
// 링크는 "${ }"
// $('# ')

  - 완성

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

<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://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <title>스파르타코딩클럽 | 부트스트랩 연습하기</title>

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Stylish&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Stylish', sans-serif;
        }
        .wrap {
            width : 880px;
            margin : auto;
        }
        .comment {
            color : blue;
            font-weight : bold;
        }
        .posting-box {
            width : 500px;
            margin : 0px auto 20px auto;
            padding : 50px;
            border : 2px solid black;
            border-radius : 5px;

            display : none;
        }
    </style>

    <script>
        $(document).ready(function () {
            $('#cards-box').empty();
            listing();
        });

        function listing() {
            $.ajax({
                type: "GET",
                url: "http://spartacodingclub.shop/post",
                data: {},
                success: function (response) {
                    let rows = response['articles']

                    for (let i=0; i<rows.length; i++) {
                        let comment = rows[i]['comment']
                        let desc = rows[i]['desc']
                        let image = rows[i]['image']
                        let title = rows[i]['title']
                        let url = rows[i]['url']

                        let temp_html=`<div class="card">
                                            <img class="card-img-top" src="${image}" alt="Card image cap">
                                            <div class="card-body">
                                                <a class="card-title" href="${url}"> ${title} </a>
                                                <p class="card-text"> ${desc} </p>
                                                <p class="card-text comment"> ${comment} </p>
                                            </div>
                                        </div>`
                        $('#cards-box').append(temp_html)
                    }
                }
            })
        }

        function openclose() {
            let status = $('#post-box').css('display');

            if (status == 'block') {
                $('#post-box').hide();
                $('#btn-posting-box').text('포스팅 박스 열기');
            }
            else {
                $('#post-box').show();
                $('#btn-posting-box').text('포스팅 박스 닫기');
            }
        }
    </script>
</head>

<body>
    <div class="wrap">
        <div class="jumbotron">
        <h1 class="display-4">나홀로 링크 메모장!</h1>
        <p class="lead"> 중요한 링크를 저장해두고, 나중에 볼 수 있는 공간입니다 </p>
        <hr class="my-4">
        <p class="lead">
            <a onclick="openclose()" id="btn-posting-box" class="btn btn-primary btn-lg" href="#" role="button">포스팅 박스 열기</a>
        </p>
    </div>
        <div class="posting-box" id="post-box">
            <div class="form-group">
                <label> 아티클 URL </label>
                <input type="email" class="form-control" id="article-url" aria-describedby="emailHelp">
                <small id="emailHelp" class="form-text text-muted"> </small>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">간단 코멘트</label>
                <textarea class="form-control" id="exampleInputPassword1" rows="3"> </textarea>
            </div>
            <button type="submit" class="btn btn-primary">기사저장</button>
        </div>


        <div class="card-columns" id="cards-box">
            <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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </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 class="card-title" href="http://naver.com/"> 여기 기사 제목이 들어가죠 </a>
                    <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
                    <p class="card-text comment"> 여기에 코멘트가 들어갑니다. </p>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

 

2. Python

  - 리스트

a_list = []
a_list.append(1)                      # a_list[0] = 1
a_list.append([2,3])                  # a_list[1] = [2,3]
                                      # a_list[1][0] = 2

  - 딕셔너리

a_dict = {}
a_dict = {'name':'bob', 'age':21}
a_dict['height'] = 178                # a_dict = {'name':'bob', 'age':21, 'height':178}

  - 함수

def sum(num1, num2) :
	return num1+num2

result = sum(1,2)
print(result)           # 3 출력
# 조건문

def is_adult(age):
	if age > 20:
		print('성인입니다')    # 조건이 참이면 '성인입니다' 출력
	else:
		print('청소년이에요')  # 조건이 거짓이면 '청소년이에요' 출력

is_adult(30)            # '성인입니다' 출력
# 반복문: 리스트의 요소들을 하나씩 꺼내쓰는 형태

fruits = ['사과','배','배','감','수박','귤','딸기','사과','배','수박']

count = 0
for fruit in fruits:
	if fruit == '사과':
		count += 1

print(count)            # 2 출력 (사과의 개수)

  - requests 패키지 써보기

import requests # requests 라이브러리 설치 필요

r = requests.get('http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99')
rjson = r.json()

gus = rjson['RealtimeCityAir']['row']

for gu in gus:
    gu_name = gu['MSRSTE_NM']
    gu_mise = gu['IDEX_MVL']

    if (gu_mise > 50) :
        print(gu_name, gu_mise)

 

3. mongoDB

  ✔️ pymongo

from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbspart

  ✔️ 코드 요약

# 저장 - 예시
doc = {'name':'bobby','age':21}
db.users.insert_one(doc)

# 한 개 찾기 - 예시
user = db.users.find_one({'name':'bobby'})

# 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
same_ages = list(db.users.find({'age':21},{'_id':False}))

# 바꾸기 - 예시
db.users.update_one({'name':'bobby'},{'$set':{'age':19}})

# 지우기 - 예시
db.users.delete_one({'name':'bobby'})

 

4. 크롤링

 - requests로 요청하고 BeautifulSoup으로 솎아낸다 !

import requests
from bs4 import BeautifulSoup

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://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200303',headers=headers)

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

  - select_one / select

# 선택자를 사용하는 방법 (copy selector)
soup.select('태그명')
soup.select('.클래스명')
soup.select('#아이디명')

soup.select('상위태그명 > 하위태그명 > 하위태그명')
soup.select('상위태그명.클래스명 > 하위태그명.클래스명')

# 태그와 속성값으로 찾는 방법
soup.select('태그명[속성="값"]')

# 한 개만 가져오고 싶은 경우
soup.select_one('위와 동일')

 

  ▶ 웹 크롤링 연습 (영화 순위, 제목, 평점 출력)

     ✔️ https://movie.naver.com/movie/sdb/rank/rmovie.naver?sel=pnt&date=20200303

     ✔️ 오른쪽 마우스 → copy → copy selector

import requests
from bs4 import BeautifulSoup

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://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200303',headers=headers)

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

trs = soup.select('#old_content > table > tbody > tr')

#old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img
#old_content > table > tbody > tr:nth-child(2) > td.title > div > a
#old_content > table > tbody > tr:nth-child(2) > td.point

for tr in trs:
    a_tag = tr.select_one('td.title > div > a')

    if a_tag is not None:
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        title = a_tag['title']
        score = tr.select_one('td.point').text

        print(rank, title, score)

 

  ▶ 웹 크롤링 결과 저장하기

import requests
from bs4 import BeautifulSoup

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

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://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200303',headers=headers)

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

trs = soup.select('#old_content > table > tbody > tr')

#old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img
#old_content > table > tbody > tr:nth-child(2) > td.title > div > a
#old_content > table > tbody > tr:nth-child(2) > td.point

for tr in trs:
    a_tag = tr.select_one('td.title > div > a')

    if a_tag is not None:
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        title = a_tag['title']
        score = tr.select_one('td.point').text

        doc = {
            'rank' : rank,
            'title' : title,
            'score' : score
        }

        db.movies.insert_one(doc)

 

  ▶ 퀴즈: 웹 크롤링 결과 이용하기

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

# (1) 영화제목 '매트릭스'의 평점을 가져오기
target_movie = db.movies.find_one({'title':'매트릭스'}, {'_id':False})
target_score = target_movie['score']
print(target_score)

# (2) '매트릭스'의 평점과 같은 평점의 영화 제목들을 가져오기
movies = list(db.movies.find({'score':target_score}, {'_id':False}))
for movie in movies:
    print(movie['title'])

# (3) 매트릭스 영화의 평점을 0으로 만들기
db.movies.update_one({'title':'매트릭스'},{'$set':{'score':0}})

 

4. 숙제: 지니뮤직 크롤링하기

import requests
from bs4 import BeautifulSoup

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

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://www.genie.co.kr/chart/top200?ditc=D&ymd=20200403&hh=23&rtm=N&pg=1',headers=headers)

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

#body-content > div.newest-list > div > table > tbody > tr:nth-child(1)
#body-content > div.newest-list > div > table > tbody > tr:nth-child(1) > td.number
#body-content > div.newest-list > div > table > tbody > tr:nth-child(1) > td.info > a.title.ellipsis
#body-content > div.newest-list > div > table > tbody > tr:nth-child(1) > td.info > a.artist.ellipsis

trs = soup.select('#body-content > div.newest-list > div > table > tbody > tr')

for tr in trs:
    info = tr.select_one('td.info')

    if info is not None:
        number = tr.select_one('td.number').text
        title = info.select_one('a.title.ellipsis').text
        artist = info.select_one('a.artist.ellipsis').text

        print(number[0:2].strip(), title.strip(), artist)

   ✔️ strip() : 문자열에서 특정 문자 제거

        - strip([chars]) : 인자로 전달된 문자를 String의 왼쪽과 오른쪽에서 제거

        - lstrip([chars]) : 인자로 전달된 문자를 String의 왼쪽에서 제거
        - rstrip([chars]) : 인자로 전달된 문자를 String의 오른쪽에서 제거

        * 인자 X → 공백 제거