maino77 2021. 7. 13. 20:16

1. 파이썬 시작하기

파이썬이란?
  컴퓨터는 1001101과 같은 이진법으로 이뤄진 언어를 사용한다. 그러나 사람이 사용하기에는 어려움으로 사람의 언어를 사용한 문법으로 컴퓨터에 명령을 내릴 수 있도록 프로그래밍 언어라는 것을 만들었다. 이것들 중 하나가 바로 파이썬이다.

 

파이썬 간단한 사용법 소개

print('hello sparta')

print를 입력하고 괄호 안에 내용을 적고 실행(run)시키면 결과 창에 hello sparta라고 출력된다.

 

 

2. 파이썬 기초 공부

자바스크립트와는 달리 파이썬에는 let처럼 붙이지 않고 바로 쓴다.

 

1) 변수 & 기본연산

first_name = 'gildong'
last_name = 'hong'
print(first_name+last_name) # gildonghong

a = 2
b = 3
print(a+b) # 5

print(a+last_name) #error 오류가 발생, 문자와 숫자를 결합 할 수 없음

a = str(2)
print(a+last_name) #2hong 이번에는 str(2)로 숫자 2를 문자 2로 바꾸었기 때문에 가능

 

2) 자료형

  • 숫자, 문자형
# 숫자, 문자를 변수에 넣을 수 있다
name = 'honggildong'
num = 1

# 불리언 자료형 -> 참과 거짓을 나타낸다.
is_number = True # True, False

 

  • 리스트 형
a_list = ['사과', '배', '감']
print(a_list[0]) #사과

# 추가
  # 원소 마지막에 추가: append
      a.append('귤')
      print(a_list) # [사과, 배, 감, 귤]
  # 원하는 곳에 추가: insert -> 리스트.index(입력할 index, 값)
  	  a_list.index(1, 귤)
      print(a_list) # [사과, 귤, 배, 감]
  # + 연산자로 더하기
  	  b_list = [포도, 수박, 참외]
      c_list = a_list + b_list
      print(c_list) # [사과, 배, 감, 포도, 수박, 참외]
  # 리스트 ㅊ추가하기: extend -> 리스트.extend(추가할 리스트)
  	  a_list.extend([복숭아, 자몽])
      print(a_list) #[사과, 배, 감, 복숭아, 자몽]

# 삭제
  #키워드를 통한 삭제: del
    del a_list[1]
    print(a_list) #[사과, 감]
  #메소드에 의한 삭제: remove -> 리스트.remove(찾을 아이템)
    a_list.remove(배)
    print(a_list) #[사과, 감]

⁂ 참고자료

https://wikidocs.net/16040

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

  • 객체형
a_dict ={'name': 'bob', 'age':25}
print(a_dict['age']) # 25

#추가
  a_dict['height'] = 183
  print(a_dict['height']) # 183
  
#객체와 리스트의 조합
people = [{'name':'bob','age':20},{'name':'carry','age':38}]
    # people[0]['name']의 값은? 'bob'
    # people[1]['name']의 값은? 'carry'
person = {'name':'john','age':7}
people.append(person)
    # people의 값은? [{'name':'bob','age':20},{'name':'carry','age':38},{'name':'john','age':7}]
    # people[2]['name']의 값은? 'john'

 

 

3) 함수

자바스크립트에서는 function을 이용했다면 파이썬에서는 def를 사용한다.
또한 {}를 사용하지 않는 대신 탭을 이용하기 때문에 주의해야 한다.
return은 함수의 종료를 명시한다.

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

result = sum(2,3)
print(result) # 5

 

 

4) 조건문

age = 15
if age > 20:
	print('성인입니다')
else:
	print('청소년입니다')

#청소년입니다.

 

 

5) 반복문

fruits = ['사과','배','배','감']

for ff in fruits:
    print(ff)
# 사과
# 배
# 배
# 감

#반복문을 이용한 카운팅
fruits = ['사과','배','배','감','수박','귤','딸기','사과','배','수박']
count = 0
for ff in fruits:
    if ff == '수박':
        count += 1

print(count) # 2

# 객체 안의 내용 반복문으로 꺼내기
people = [{'name': 'bob', 'age': 20},
          {'name': 'carry', 'age': 38},
          {'name': 'john', 'age': 7},
          {'name': 'smith', 'age': 17},
          {'name': 'ben', 'age': 27}]
print(people[2]['age']) #7
for person in people:
    print(person)
#{'name': 'bob', 'age': 20}
#{'name': 'carry', 'age': 38}
#{'name': 'john', 'age': 7}
#{'name': 'smith', 'age': 17}
#{'name': 'ben', 'age': 27}

#조건문까지 활용한 반복문
people = [{'name': 'bob', 'age': 20},
          {'name': 'carry', 'age': 38},
          {'name': 'john', 'age': 7},
          {'name': 'smith', 'age': 17},
          {'name': 'ben', 'age': 27}]

print(people[2]['age']) #7

for person in people:
    if person['age'] < 20:
        print(person)
#{'name': 'john', 'age': 7}
#{'name': 'smith', 'age': 17}

 

 

 

3. 파이썬 패키지 설치하기

파이썬에서 패키지는 모듈(일종의 기능들의 묶음)을 모아 놓은 단위이다. 이런 패키지의 묶음을 라이브러리라고 볼 수 있다. 

이해를 돕기 위해 상황을 가정해보자

📌가정: 회사에서는 패키지 a,b,c를 설치해서 쓰고 개인 프로젝트에서는 b,c,d,e를 설치해서 사용하고 있다. 그런데 회사에서 b의 이전 버전인 b1으로 대체하고자 한다. 그러면 나는 회사에서는 b1을 깔고 개인 프로젝트를 할 때는 b를 깔아야한다. 이렇게 계속 작업할 수는 없는데 해결방안이 있을까?

💡해결책: 패키지를 따로 묶어두어서 필요할 때마다 꺼내서 사용하도록하자. 묶음1에는 a,b1,c를 넣어두고 묶음2에는 b,c,d,e를 넣고 사용하면 문제 없다. 우리는 이 묶음을 가상환경이라 부른다. 간단히 라이브러리를 담아두는 폴더라고 생각하면 된다.

이 수업에서 사용할 패키지는 request이다. 이것은 다음 주소를 참고하여 설치했다.

https://znos.tistory.com/42

 

파이썬(python) requests 라이브러리 설치(feat.파이참) | 아무것도 모르고 시작하는 코딩

저번 포스팅에서 말했던 외부 라이브러리를 이용하려면 파이썬에서 따로 설치를 해줘야 한다. 가상환경 셋팅 프로젝트마다 라이브러리를 불러와서 사용할 때 코드를 변형하고 추가하여 사용할

znos.tistory.com

 

request를 설치하고 난 뒤 API를 이용해 자바스크립트에서 AJAX로 했듯이 작업해보았다.

먼저 코드를 이렇게 입력한다.

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

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

r 안에 들어가 있는 URL이 API로 저기서 우리가 필요한 내용을 가져올 수 있다.

API 모습

우리는 저 API에서 구와 미세먼지 수치만 긁어올 것이다. 그렇기 위해서는 반복문을 사용해서 코드를 작성해야 한다.

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

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

i = 0
row = rjson['RealtimeCityAir']['row']
for gu in row:
    gu_name = row[i]['MSRSTE_NM']
    gu_mise = row[i]['IDEX_MVL']
    print(gu_name, gu_mise)
    i += 1

 

 

 

4. 웹스크래핑 기초

분석을 위해 웹에서 원하는 데이터를 추출하는 것을 웹스크래핑이라 한다. 

스크래핑의 기본적인 세팅 코드는 다음과 같다.

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('',headers=headers)

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

title =

 

여기서 우리는 스크래핑을 할 때 두 가지로 나눌 수 있다. 한 가지만 스크래핑 할 때와 여러 개를 스크래핑 할 때.

이 두가지 방법에 대해 알아보겠다.

 

1) 한 가지를 스크래핑 하는 법 [ select_one('') ]

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('',headers=headers)

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

title = soup.select_one('#old_content > table > tbody > tr:nth-child(2) > td.title > div > a')
#<a href="/movie/bi/mi/basic.nhn?code=171539" title="그린 북">그린 북</a>

여기서 data 내에 있는 ''안에 스크래핑 할 url을 적는다. 그리고 난 뒤 스크래핑 할 부분을 콘솔 창에서 마우스 우클릭을 하여 Copy에서 Copy Selector을 눌러 복사하고 soup.select_one('') 안에 붙여주면 그 부분이 스크래핑 된다. select_one('')은 괄호 안에 지정한 것 중 첫번째 것을 가져온다.

여기서 우리는 어떤 것을 가져올지 선택할 수 있다.

1. 글을 가져온다.
  여기서 글만 가져오고 싶다면 print(title.text)로 변경한다. 그러면 '그린 북'만 가져올 수 있다.
2. 속성을 가져온다.
   print(title['href'])를 입력하면 속성을 가져올 수 있다. 그러면 '/movie/bi/mi/basic.nhn?code=171539'가 출력된다.

 

2) 여러 개를 스크래핑 할 때

여러 개를 스크래핑 할 때는 select_one이 아니라 select를 사용한다.

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(3)이나 tr을 모으기 위해 :nth-child(3)를 제거

for tr in trs:
    print(tr)
#모든 tr이 출력된다.

 

만약 우리가 이 뒤의 속성들을 찾고 싶다면 다음과 같이 작성할 수 있다.

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.title > div > a

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

코드 결과

잘 나오는 것을 확인할 수 있다. 여기서 None은 페이지에 있는 점선이다.

우리가 여기서 좀 더 원하는 것만 추출하고 싶다면 어떻게 해야 할까? 위의 결과물에서 텍스트만 추출하고 싶다면 .text를 붙여넣으면 될 것이다. 그러나 이렇게 하면 오류가 나기 때문에 다른 방법을 사용해야 한다.

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.title > div > a

for tr in trs:
    a_tag = tr.select_one('td.title > div > a')
    if a_tag is not None:
        title = a_tag.text
        print(title)

코드 결과물

원하는 대로 잘 나온 것을 확인할 수 있다.

 

⁂참고자료

select_one, select의 차이

https://pycoding.tistory.com/entry/python-%ED%81%AC%EB%A1%A4%EB%A7%81-select-selectone-%EC%B0%A8%EC%9D%B4

 

python 크롤링, select , select_one 차이

실제로 크롤링을 처음 시작할 때 헷갈리는 부분 중 하나가 select와 select_one의 차이다. 차이점은 select는 결과값이 리스트 형태로 저장되고, select_one은 그렇지 않다. select로 수집하였을때는 결론적

pycoding.tistory.com

 

 

5. 웹스크래핑 연습하기

그러면 이제 영화 순위 사이트를 통해 연습해보겠다.

우리의 목표는 순위, 영화 제목, 평점만을 가져오는 것이다.

먼저 웹스크래핑의 기본 코드에 주소를 입력하고 위의 파란색 박스에 해당하는 코드를 콘솔 창에서 찾아 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)

그럼 이렇게 코드가 작성된다.

trs = soup.select('#old_content > table > tbody > tr')는 Copy Selector로 가져온 코드를 약간 변형하여 붙여넣었다. 원래는 아래 주석(#)과 같지만 이 틀과 같은 것을 여러개 선택하여 반복문으로 돌리기 위해 다들 공통적으로 가지고 있을 tr까지만을 넣었다.

그리고 반복문을 이용하여 제목을 추출한다.

for tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    print(title_tag)

제목만 해당하는 코드를 Copy Selector하여 title_tag에 넣어주는데 이때 앞부분은 겹치므로 그것을 제외한다. 이렇게 코드를 작성하고 출력하면 다음과 같은 결과가 나온다.

중간에 None이 껴있다.

결과를 보니 중간에 None이 들어가 있다. 이는 웹페이지에서 점선이 None의 형태로 나와 있는 것이다. 그래서 텍스트만 출력하려고 해도 이것 때문에 출력되지 않는 오류가 일어난다. 이를 막기 위해서는 None을 제외해야 한다.

r tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    if title_tag is not None:
        title = title_tag.text
        print(title)

If문을 이용해 None을 제외하고 택스트를 추출하면 우리가 원했던 결과가 나온다. 그러면 이제 나머지도 동일한 방법으로 해보자.

for tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    if title_tag is not None:
        title = title_tag.text
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        star = tr.select_one('td.point').text
        print(rank, title, star)

코드를 작성한 결과 우리가 원했던 대로 잘 출력된 것을 확인할 수 있다.

 

 

 

6) DB설치 확인하는 법

몽고 DB를 깔았으면 그것이 제대로 설치되었는지 확인을 하기 위해 페이지에 localhost:27017을 입력한다.

그러면 다음 사진처럼 나오게 된다. 이렇게 나오면 몽고 DB가 제대로 실행되고 있다.

그리고 Robo 3T를 설치하여 실행한다. 

실행하면 이런 창이 뜨는데 왼쪽 상단에 Create를 눌러 이름을 정하고 만들면 된다. 사진에서는 이미 myLocalDB로 만들었기 때문에 리스트에 존재한다.

Robo 3T는 몽고 DB의 데이터를 시각화해주기 때문에 사용한다.

 

 

 

7. DB개괄

데이터베이스의 목적은 잘 찾기 위해 쌓아둔다.

데이터베이스는 크게 두 가지의 종류가 있는데 SQL과 NoSQL(not only SQL의 약자)이다.

SQL은 엑셀에 가깝다고 생각하면 된다. 칸에 무엇을 넣을지 정하고 행과 열을 채워나간다. 그래서 SQL은 정형화 된 방식으로 데이터를 정리하는 방법이다. 그래서 데이터가 일관적이고 분석하기 빠르다.

NoSQL은 정해져있지 않고 객체 형태로 들어간다. 어떤 것에는 이 정보를 넣고 다른 것에는 넣지 않을 수 있다. 그래서 SQL보다 상대적으로 자유롭다. 그래서 데이터를 적재하는데 편하지만 일관성이 부족하다.

예를 들어 회원들의 정보를 입력하는 상황으로 가정해보자

SQL은 이름, 주소, 나이 등을 정하고 그 칸에 맞게 데이터를 입력해나갈 것이다. 그러다가 100명의 회원을 받고 난 뒤 새로운 정보인 주민등록번호를 받고 싶다고 한다면 101번째부터는 채워질 것이고 이전의 100명은 그 칸이 빈칸으로 존재하게 될 것이다. 그래서 중간에 바꾸기가 애매하다.

대표적으로 MS-SQL, My-SQL 등이 있다.

 

반면에 NoSQL은 앞의 100명은 그대로 두고 101명부터 채워넣게 된다. 즉, 앞의 100에 공란이 존재하지 않는다. 그래서 굉장히 유연하다.

대표적으로 MongoDB가 있다.

 

 

 

8. pymongo로 DB조작하기

파이썬에서 pymongo를 추가하고 pymongo의 기본코드를 입력한다.

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

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

doc = {'name':'bobby','age':21}
db.users.insert_one(doc)

이렇게 입력하면 된다.

간단히 해석하면 첫 줄은 pymongo를 불러오는 코드다. 두 번째는 컴퓨터에서 돌아가고 있는 몽고DB에 접촉하는 코드다. 마지막으로 dbsparta라는 DB이름으로 접촉한다는 의미다. 주석은 기본적으로 사용하는 4가지를 적어둔 것이다.

그리고 doc으로 이름과 나이를 저장시켰다. 마지막에는 insert로 doc을 users에 추가하였다.

Robo 3T로 몽고DB를 시각화 해서 본 사진

마지막 코드에서 dbsparta라는 이름으로 접촉하는데 없다면 자동으로 만든다. 그것을 사진으로 확인할 수 있다.

이번에는 doc에 boby가 아닌 27살 John을 넣어보았다 그 결과 Robo 3T에는 다음과 같이 나왔다.

John이 추가되었다.

John이 추가된 것을 확인할 수 있다.

 

이번에는 find를 해보겠다.

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

same_ages = list(db.users.find({'age':21},{'_id':False}))
print(same_ages)

이렇게 코드를 작성하고 실행시키니 다음과 같은 결과가 나왔다.

몽고DB에 저장되어있던 사람들 중 나이가 21인 bobby와 jane을 찾아낸 것이다. 이를 통해 same_ages = list(db.users.find({'age':21},{'_id':False}))가 무슨 의미인지 해석할 수 있다.

db.users에 저장되었있는 데이터 중에서 find하는데 age가 21이고 _id는 나타내지 말아라(False)라고 명령한 코드인 것이다. 만약 _id가 없다면 id값까지 가져오게 된다.

위의 코드는 list를 적었기 때문에 list안에 포함된 상태로 나왔는데 객체만 나오게 하려면 다음과 같이 작성한다.

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

same_ages = list(db.users.find({'age':21},{'_id':False}))

for person in same_ages:
    print(person)

객체만 따로 나와 출력된 것을 확인할 수 있다.

때로는 모든 자료를 가져오라고 명령을 내릴 수 있다. 그럴 때는 age의 같을 빈칸으로 만들어서 실행시키면 모든 자료를 가져온다.

 

찾는 자료 중 하나만 가지고 오라고 명령할 때는 다음과 같이 입력한다

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

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

user = db.users.find_one({'name':'bobby'})
print(user)

결과처럼 bobby가 해당된 객체의 정보를 모두 불러온다. 만약 _id를 불러오고 싶지 않다면 {'_id':False}를 입력하면 된다.

그리고 bobby의 나이만을 불러오고 싶다면 다음과 같이 작성한다.

 

업데이트하는 코드는 다음과 같이 작성한다.

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

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

db.users.update_one({'name':'bobby'},{'$set':{'age':19}})

마지막 코드를 해석하면 name이 bobby인 애를 찾아서 age를 19로 바꾸라는 의미다. 여기서 주의할 것은 앞에 users가 자신이 입력한 콜렉션이 맞는지 확인하는 것이다.

이렇게 코드를 입력하면 Robo 3T에 변경된 값이 나온다.

 

같은 조건에 있는 것들을 변경시킬 때는 update_many를 사용한다.

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

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

db.users.update_many({'name':'bobby'},{'$set':{'age':19}})

이렇게 작성하면 name이 bobby인 것을 모두 찾아서 age를 전부 19로 바꾼다. 그러나 한 번에 다 바꾼다는 것은 굉장히 위험하기 때문에 잘 사용하지는 않는다. 그래서 주로 update_one을 사용한다.

 

마지막으로는 삭제하는 기능인 delete를 사용해보겠다.

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

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

db.users.delete_one({'name':'bobby'})

delete가 들어간 코드가 삭제하라는 명령을 가진 코드이다.

name이 bobby인 사람을 찾아서 지우라는 의미를 갖는다.

그리고 delete_many가 있지만 이것 역시 위험하기에 추천하지 않는다.

 

이제 배운 것을 정리해보자

#몽고DB 기본코드
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta #dbsparta는 임의로 만든 것으로 바꾸어도 된다.

#inseert: 데이터를 넣는 것. / find: 데이터를 찾는 것 / update / delete

# 저장 - 예시
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'})

이것들을 참고해서 작성하도록 하자!

 

 

 

9. 웹스크래핑 결과 저장하기

이제 배운 것을 활용해 앞서 웹스크래핑 했던 것들을 저장해보자.

먼저 기존에 있는 코드를 몽고DB에 저장할 수 있도록 코드를 수정하자.

* 기존 코드

mport 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)

for tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    if title_tag is not None:
        title = title_tag.text
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        star = tr.select_one('td.point').text
        print(rank, title, star)

 

* 수정한 코드

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)

for tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    if title_tag is not None:
        title = title_tag.text
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        star = tr.select_one('td.point').text
        doc = {
            'rank':rank,
            'title':title,
            'star':star
        }
        db.movies.insert_one(doc)

먼저 몽고DB를 사용할 수 있도록 몽고DB의 기본 코드를 삽입하였다.

그리고 doc을 만들어 내부에 rank, title, star을 포함하도록 만든 다음 db의 movies에 doc를 insert하였다.

Robo 3T에서 확인한 모습

우리가 원하던 대로 몽고DB에 저장된 것을 Robo 3T에서 확인할 수 있다.

 

 

 

10. 웹스크래핑 퀴즈

이번에는 영화 평점 사이즈에서 3가지 미션을 해보자.

1. 영화제목 '매트릭스'의 평점을 가져오기

2. '매트릭스'의 평점과 같은 평점의 영화 제목들을 가져오기

3. 매트릭스 영화의 평점을 0으로 만들기

먼저 문제만을 보고 어떻게 풀어야 할지 예상해보자.

1은 평점을 가져와야 한다. 그래서 Copy selector을 이용해 가져온 다음 주변의 태그를 제거해주면 될 것 같다. 그러기 위해서는 .text를 사용해보자.

2는 if를 사용해서 평점이 같은 영화를 출력한다.

3은 update를 이용하여 몽고DB 매트릭스의 평점을 0으로 바꾸면 된다.

그러면 이제 각 코드를 작성해보자.

[1번 코드]

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

total_base = soup.select('')
tag_base = soup.select_one('#old_content > table > tbody > tr:nth-child(18) > td.point')
base = tag_base.text
print(base)

스크래핑 기본 코드를 쓰고 평점에 해당하는 코드를 Copy selector한 다음 .text를 붙여 평점만 출력하였다.

 

그렇다면 변형해서 몽고DB에서 매트릭스의 평점을 가져와보자.

앞서 몽고DB에 데이터를 넣어두었기 때문에 find를 이용해 매트릭스의 모든 데이터를 불러온 다음 거기서 평점만 추출한다.

matrix_score = db.movies.find_one({'title':'매트릭스'})
score = matrix_score['star']
print(score)

이렇게 하면 매트릭스의 평점인 9.39가 출력된다.

 

 

[2번 코드]

이것을 작성하는데 꽤 많은 시간이 걸렸다. 내가 완전히 코드를 이해하지 못했기 때문이다.

먼저 헷갈린 부분부터 짚어보자.

trs = soup.select('#old_content > table > tbody > tr')
#old_content > table > tbody > tr:nth-child(2)

for tr in trs:
    title_tag = tr.select_one('td.title > div > a')
    #old_content > table > tbody > tr:nth-child(2) > td.title > div > a
    if title_tag is not None:
        title = title_tag.text
        rank = tr.select_one('td:nth-child(1) > img')['alt']
        star = tr.select_one('td.point').text

우리가 저번 챕터에서 작성했던 코드다. 여기서 헷갈렸던 것은 if부분.

단순히 생각하면 만약 title_tag가 None이 아니면 아래 코드를 진행해라는 의미다.

여기서 나는 이렇게 생각했다.

❓ if title_tag is not None은 title에만 적용되는 건데 왜 star에도 적용되는거지?

star 안에도 None이 있기 때문에 text가 안되어야 하는데 정상적으로 실행이 되니 의문을 느낀 것이다. 

이것 때문에 많은 고민과 print를 했고 한참이 지난 후에야 이해할 수 있었다.

 

trs의 순서가 a, b, none, c, d, none이라고 가정해보자

그러면 for문 이후 title_tag = tr.select_one에서 title_tag는 a를 먼저 갖게 될 것이다. 그리고 이것은 none이 아니기 때문에 if문을 통과해서 아래의 코드에 적용될 것이다.

b역시 마찬가지로 if문을 통과해서 아래의 코드에 적용될 것이다.

그리고 none의 차례가 오면 title_tag는 none을 갖게 된다. 그러면 if문을 통과하지 못하게 된다. 그러면 자연스럽게 if 아래에 있는 star에 적용되지 못하면서 걸러지게 되는 것이다.

이후 다시 c, d를 적용해 if문 아래까지 통과하고 none이 다시 오면 if를 넘기지 못한다.

 

즉, if문의 조건에 의해 none이 적용되지 않기 때문에 if문 아래에 있는 star역시 none에 영향에서 벗어나게 된다.

이것을 깨닫자 여태까지 날 괴롭히던 의문이 풀리며 그렇게 기쁠 수 없었다.

 

이것을 해결하고 나서야 그 뒤로 2에 맞는 코드를 작성할 수 있었다.

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

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

matrix_score_tag = soup.select_one('#old_content > table > tbody > tr:nth-child(19) > td.point')
matrix_score = matrix_score_tag.text

for total_all in total:
    name_tag = total_all.select_one('td.title > div > a')
    if name_tag is not None:
        name = name_tag.text
        score = total_all.select_one('td.point').text
        if score == matrix_score:
            same_name = name
            print(same_name)

먼저 total로 for문으로 반복할 횟수를 지정해주었다.

그리고 매트릭스의 평점을 matrix_score_tag를 이용하여 matrix_score에 저장하였다.

for문을 이용해 내부 코드를 반복시켰다. name_tag로 영화 제목을 불러왔고 if문으로 none을 빼낸 다음 name과 score로 영화제목과 평점을 텍스트로 저장하였다.

이후 한 번 더 if를 진행하여 영화 점수가 matrix_score와 같을 때 영화 제목을 same_name으로 지정하여 출력하도록 코드를 작성하였다.

출력된 결과

2번 퀴즈를 진행하면서 헷갈하지 말 것은 for문을 사용했기 때문에 작업이 끝날 때까지 한가지 요소를 계속 적용한다는 점이다. 이 글을 작성하는 순간에도 순간 score의 if문을 헷갈려 잠시 다시 보았다. 그러니 주의하도록 하자.

 

2번째 퀴즈도 변형하여 몽고DB에서 매트릭스의 평점과 같은 평점의 영화 제목들을 가져오자.

매트릭스의 점수를 뽑은 다음 이것을 데이터 내에서 비교하여 출력하게 하면 된다.

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

## 코딩 할 준비 ##

target_score = db.movies.find_one({'title':'매트릭스'})['star']

movies = list(db.movies.find({'star':target_score}))

for movie in movies:
    print(movie['title'])

제대로 출력되었다

 

3번째 퀴즈는 몽고DB의 매트릭스 영화 평점을 0으로 바꾸라고 했기 때문에 update를 사용해야 한다.

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

matrix_update = db.movies.update_one({'title':'매트릭스'},{'$set':{'star':0}})

평점이 0으로 바뀐 모습

 

 

*** 후기

3주차는 몽고 DB를 써보았다. 같이 스터디 했던 팀원이 자주 쓰는 프로그램이었는데 당시에는 이게 뭔지 몰라서 '아.. 그렇구나...'하고 들었는데 이것을 배우고 쓰게 되어서 반가웠다.

그리고 이렇게 스크래핑까지 하여 저장하는 과정까지 할 줄 알게되니 이제 몽고DB를 쓸 줄 알게 되는 것 같아 나중에 프로젝트를 진행할 때 크게 도움이 될 것 같다는 생각이 들었다.

3주차에서 파이썬을 배울 때는 js와 비슷하면서도 달라 혼동이 있었다. js는 {}로 구분하는데 파이썬은 띄어쓰기로 구분하니 그 점에서 조금 실수가 많이 나왔다. 그래도 지금은 익숙해져서 코드를 잘 작성하고 있다.

 

이번 수업은 주어진 코드의 틀에서 우리가 사용하고자 하는 대로 수정하여 사용하면 되서 사실 크게 어렵지는 않았다. 중간에 DB에서 오류가 일어나 만든 DB가 보이지 않던 문제를 제외하면 말이다.

이 문제를 해결하기 위해 다음 페이지를 참고하였다.

https://somjang.tistory.com/entry/Python-OSError-Errno-48-Address-already-in-use-Flask

 

솜씨좋은장씨

세상 모든 개발 관련 지식을 담아보자!

somjang.tistory.com

 

이제 4주차에 들어가는데  이것으로 무엇을 더 할건지 기대가 된다.

728x90