[수업 목표]
1. 파이썬 기초 문법을 안다.
2. 원하는 페이지를 크롤링 할 수 있다.
3. pymongo를 통해 mongoDB를 제어할 수 있다.
파이썬 기초 문법
a = 3 # 3을 a에 넣는다
b = a # a를 b에 넣는다
a = a + 1 # a+1을 다시 a에 넣는다
num1 = a*b # a*b의 값을 num1이라는 변수에 넣는다
num2 = 99 # 99의 값을 num2이라는 변수에 넣는다
# 변수의 이름은 마음대로 지을 수 있음!
# 진짜 "마음대로" 짓는 게 좋을까? var1, var2 이렇게?
**숫자, 문자형
name = 'bob' # 변수에는 문자열이 들어갈 수도 있고,
num = 12 # 숫자가 들어갈 수도 있고,
is_number = True # True 또는 False -> "Boolean"형이 들어갈 수도 있습니다.
#########
# 그리고 List, Dictionary 도 들어갈 수도 있죠. 그게 뭔지는 아래에서!
**리스트 형 (Javascript의 배열형과 동일)
a_list = []
a_list.append(1) # 리스트에 값을 넣는다
a_list.append([2,3]) # 리스트에 [2,3]이라는 리스트를 다시 넣는다
# a_list의 값은? [1,[2,3]]
# a_list[0]의 값은? 1
# a_list[1]의 값은? [2,3]
# a_list[1][0]의 값은? 2
**Dictionary 형 (Javascript의 dictionary형과 동일)
a_dict = {}
a_dict = {'name':'bob','age':21}
a_dict['height'] = 178
# a_dict의 값은? {'name':'bob','age':21, 'height':178}
# a_dict['name']의 값은? 'bob'
# a_dict['age']의 값은? 21
# a_dict['height']의 값은? 178
**Dictionary 형과 List형의 조합
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'
**함수의 정의
# 수학문제에서
f(x) = 2*x+3
y = f(2)
y의 값은? 7
# 참고: 자바스크립트에서는
function f(x) {
return 2*x+3
}
# 파이썬에서
def f(x):
return 2*x+3
y = f(2)
y의 값은? 7
**함수의 응용
def sum_all(a,b,c):
return a+b+c
def mul(a,b):
return a*b
result = sum_all(1,2,3) + mul(10,10)
# result라는 변수의 값은?
def oddeven(num): # oddeven이라는 이름의 함수를 정의한다. num을 변수로 받는다.
if num % 2 == 0: # num을 2로 나눈 나머지가 0이면
return True # True (참)을 반환한다.
else: # 아니면,
return False # False (거짓)을 반환한다.
result = oddeven(20)
# result의 값은 무엇일까요?
def is_adult(age):
if age > 20:
print('성인입니다') # 조건이 참이면 성인입니다를 출력
else:
print('청소년이에요') # 조건이 거짓이면 청소년이에요를 출력
is_adult(30)
# 무엇이 출력될까요?
fruits = ['사과','배','감','귤']
for fruit in fruits:
print(fruit)
# 사과, 배, 감, 귤 하나씩 찍힘
리스트 예제
fruits = ['사과','배','배','감','수박','귤','딸기','사과','배','수박']
count = 0
for fruit in fruits:
if fruit == '사과':
count += 1
print(count)
# 사과의 개수를 세어 보여줌
def count_fruits(target):
count = 0
for fruit in fruits:
if fruit == target:
count += 1
return count
subak_count = count_fruits('수박')
print(subak_count) #수박의 개수
gam_count = count_fruits('감')
print(gam_count) #감의 개수
딕셔너리 예제
people = [{'name': 'bob', 'age': 20},
{'name': 'carry', 'age': 38},
{'name': 'john', 'age': 7},
{'name': 'smith', 'age': 17},
{'name': 'ben', 'age': 27}]
# 모든 사람의 이름과 나이를 출력
for person in people:
print(person['name'], person['age'])
# 반복문과 조건문을 응용한 함수
# 이름을 받으면, age를 리턴해주는 함수
def get_age(myname):
for person in people:
if person['name'] == myname:
return person['age']
return '해당하는 이름이 없습니다'
print(get_age('bob'))
print(get_age('kay'))
예제1 IDEX_MVL 값이 60 이상인 구만 찍어주기!
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 > 60:
print(gu_name,gu_mise)
크롤링 기본 세팅
import requests
from bs4 import BeautifulSoup
# 타겟 URL을 읽어서 HTML를 받아오고,
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)
# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
# soup이라는 변수에 "파싱 용이해진 html"이 담긴 상태가 됨
# 이제 코딩을 통해 필요한 부분을 추출하면 된다.
soup = BeautifulSoup(data.text, 'html.parser')
#############################
# (입맛에 맞게 코딩)
#############################
select / select_one의 사용법
import requests
from bs4 import BeautifulSoup
# URL을 읽어서 HTML를 받아오고,
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)
# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
soup = BeautifulSoup(data.text, 'html.parser')
# select를 이용해서, tr들을 불러오기
movies = soup.select('#old_content > table > tbody > tr')
# movies (tr들) 의 반복문을 돌리기
for movie in movies:
# movie 안에 a 가 있으면,
a_tag = movie.select_one('td.title > div > a')
if a_tag is not None:
# a의 text를 찍어본다.
print (a_tag.text)
beautifulsoup 내 select에 미리 정의된 다른 방법
# 선택자를 사용하는 방법 (copy selector)
soup.select('태그명')
soup.select('.클래스명')
soup.select('#아이디명')
soup.select('상위태그명 > 하위태그명 > 하위태그명')
soup.select('상위태그명.클래스명 > 하위태그명.클래스명')
# 태그와 속성값으로 찾는 방법
soup.select('태그명[속성="값"]')
# 한 개만 가져오고 싶은 경우
soup.select_one('위와 동일')
Quiz 웹스크래핑(크롤링) 연습
trs = soup.select('#old_content > table > tbody > tr')
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 = tr.select_one('td.title > div > a')
star = tr.select_one('td.point')
print(rank, title.text, star.text)

이제 robo3T 이렇게 크롤링한 자료들을 DataBase에 저장하는 방법을 배우자!
MongoDB: 데이터 베이스 기본적으로 데이터를 쌓아 두는 것, 컴퓨터에는 돌아가고 있어도 내 눈에 보이지가 않게 돌아감.
robo3T: 그래서 robo3T를 가지고 이 MongoDB의 데이터를 우리 눈에 보일 수 있게 하는 것
pymongo 로 DB 조작하기
pymongo 기본 코드
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta
# 코딩 시작
-DB연결하기 & 데이터 넣기: pymongo(insert)
//name 은 bobby, age 21이라는 딕셔너리를 하나 만들었음
doc = {'name':'bobby','age':21}
//그거를 users에 넣는 것: db안에 users라는 collection안에 insert해라
db.users.insert_one(doc)
from pymongo import MongoClient # pymongo를 임포트 하기(패키지 인스톨 먼저 해야겠죠?)
client = MongoClient('localhost', 27017) # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta # 'dbsparta'라는 이름의 db를 만듭니다.
# MongoDB에 insert 하기
# 'users'라는 collection에 {'name':'bobby','age':21}를 넣습니다.
db.users.insert_one({'name':'bobby','age':21})
db.users.insert_one({'name':'kay','age':27})
db.users.insert_one({'name':'john','age':30})
-모든 결과 값을 보기 : pymongo(find)
from pymongo import MongoClient # pymongo를 임포트 하기(package install comes first)
client = MongoClient('localhost', 27017) # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta # 'dbsparta'라는 이름의 db를 만듭니다.
# MongoDB에서 데이터 모두 보기
all_users = list(db.users.find({}))
# 참고) MongoDB에서 특정 조건의 데이터 모두 보기
// '_id':False의 뜻은 _id값은 나타내지 말아라
same_ages = list(db.users.find({'age':21},{'_id':False}))
print(all_users[0]) # 0번째 결과값을 보기
print(all_users[0]['name']) # 0번째 결과값의 'name'을 보기
for user in all_users: # 반복문을 돌며 모든 결과값을 보기
print(user)
-특정 결과 값을 뽑아 보기: pymongo(find_one)
user = db.users.find_one({'name':'bobby'})
print(user)
-수정하기: pymongo(update_one)
# 생김새
db.people.update_many(찾을조건,{ '$set': 어떻게바꿀지 })
# 예시
db.users.update_one({'name':'bobby'},{'$set':{'age':19}})
user = db.users.find_one({'name':'bobby'})
print(user)
삭제하기 (거의 안 씀): pymongo(delete_one)
db.users.delete_one({'name':'bobby'})
user = db.users.find_one({'name':'bobby'})
print(user)
pymongo 사용법. 코드요약
# 저장 - 예시
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'})
#1 : 매트릭스의 평점 가져오기
from pymongo import MongoClient # pymongo를 임포트 하기(패키지 인스톨 먼저 해야겠죠?)
client = MongoClient('localhost', 27017) # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta # 'dbsparta'라는 이름의 db를 만듭니다.
rank = db.movies.find_one({'title':'매트릭스'},{'_id':False})
print(rank['star'])
#2 : '매트릭스'의 평점과 같은 평점의 영화 제목들을 가져오기
from pymongo import MongoClient # pymongo를 임포트 하기(패키지 인스톨 먼저 해야겠죠?)
client = MongoClient('localhost', 27017) # mongoDB는 27017 포트로 돌아갑니다.
db = client.dbsparta # 'dbsparta'라는 이름의 db를 만듭니다.
#2 : '매트릭스'의 평점과 같은 평점의 영화 제목들을 가져오기
same_ranks = list(db.movies.find({'star': '9.39'}, {'_id': False}))
for rank in same_ranks:
print(rank['title'])
#3 : 매트릭스 영화의 평점을 0으로 만들기
db.movies.update_one({'title':'매트릭스'},{'$set':{'star':0}})
#ANSWER
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta
target_movie = db.movies.find_one({'title':'매트릭스'})
print (target_movie['star'])
target_movie = db.movies.find_one({'title':'매트릭스'})
//타겟 영화의 별이 9.39점
target_star = target_movie['star']
movies = list(db.movies.find({'star':target_star}))
for movie in movies:
print(movie['title'])
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta
db.movies.update_one({'title':'매트릭스'},{'$set':{'star':'0'}})
3주차 숙제: 지니뮤직의 1~50위 곡을 스크래핑 해보세요. 순위 / 곡 제목 / 가수를 스크래핑 하면 됩니다.
TIP: 파이썬 문자열 자르기. 파이썬 공백 제거 구글링 해보기
Quiz 웹스크래핑(크롤링) 연습를 많이 참고하면 될것같다.
elements 를 열어서 곡 하나하나를 copy > copy selector를 해보면
#body-content > div.newest-list > div > table > tbody > tr:nth-child(1)
#body-content > div.newest-list > div > table > tbody > tr:nth-child(2)
tr 뒷 부분들만 다름 그러므로 이렇게 경로를 설정
trs = soup.select('#body-content > div.newest-list > div > table > tbody > tr')
크롤링해와야 하는것은 제목
#body-content > div.newest-list > div > table > tbody > tr:nth-child(3) > td.info > a.title.ellipsis
그다음엔 가수
#body-content > div.newest-list > div > table > tbody > tr:nth-child(2) > td.info > a.artist.ellipsis
마지막으로 순위
#body-content > div.newest-list > div > table > tbody > tr:nth-child(2) > td.number

문제점1. 순위 제목 가수의 배열 사이에 큰 공백까지 같이 나옴.
문제점2. 순위 뿐만 아니라 순위 상승 하강의 값 까지 크롤링이 됨.
첫번째 문제점을 먼저 해결해보기위해 파이썬 공백 제거를 검색,
문자열 양 끝에 있는 공백을 제거해주고,공백을 제거한 새로운 문자열을 반환해주는 .strip() 함수 발견!

이제 문제점2를 해결하기 위해 파이썬 문자열 자르기를 구글링
순위 상승, 하강과 같은 정보는 td.number안에서 같이 나오는것.
그렇기 때문에 문자열 앞에서 원하는 글자수만큼 자를 수 있는string[start:end] 사용!
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')
trs = soup.select('#body-content > div.newest-list > div > table > tbody > tr')
for tr in trs:
rank = tr.select_one('td.number').text[0:2].strip()
title = tr.select_one('td.info > a.title.ellipsis').text.strip()
artist = tr.select_one('td.info > a.artist.ellipsis').text
print(rank, title, artist)

완성!