데이터베이스 NoSQL 기본 (mongoDB) - 크롤링과 함께 쓰는 mongoDB 예제

6. 크롤링과 함께 쓰는 mongoDB 예제

import requests
import re
import datetime
from bs4 import BeautifulSoup
from pymongo import MongoClient
import pymongo
username = 'davelee'
password = 'happy91'
connection = pymongo.MongoClient('mongodb://%s:%s@www.funcoding.xyz' % (username, password))
mongodb = connection.cine21
actor_collection = mongodb.actor_collection
actor_list = actor_collection.find()
for actor in actor_list:
    print(actor['actor'])
마동석
진선규
윤계상
조재윤
최귀화
허성태
임형준
조우진
홍기준
송영창
조진웅
허동원
김윤석
이병헌
박해일
정인기
고수
예정화
박희순
박지환
이하늬
이동휘
이다윗
나문희
김소진
이제훈
정연주
엄지성
신하균
김법래
염혜란
이상희
민경진
손숙
박철민
유순웅
김구택
박지일
송상은
윤병희
최민식
현빈
최종률
김래원
이지훈
도경수
박신혜
유지태
정재진
박성웅
문창길
박해준
김해숙
하준
조한철
이선균
배성우
김혜수
김중기
성동일
김일웅
심규혁
이수경

cine21 인물 랭킹 알아내기

cine21_url = 'http://www.cine21.com/rank/person/content'
month = "2017-10"
conditions = dict()
conditions['section'] = 'actor'
conditions['period_start'] = month
conditions['gender'] = 'all'
conditions['page'] = 1
response = requests.post(cine21_url, data = conditions)
response
Out[66]:
<Response [200]>
response.content
soup = BeautifulSoup(response.content.decode('utf-8'), 'html.parser')
soup
actors = soup.select('li.people_li div.name')
actors
Out[71]:
[<div class="name"><a href="/db/person/info/?person_id=64614">마동석(3편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=82350">진선규(2편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=19889">윤계상(3편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=84000">조재윤(2편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=102264">최귀화(3편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=27131">허성태(3편)</a></div>,
 <div class="name"><a href="/db/person/info/?person_id=34884">임형준(2편)</a></div>]
실습
actors 리스트에서 배우 이름만 추출하기 예) 마동석(3편) --> 마동석
import re
for actor in actors:
    print(re.sub("\(\w+\)", "", actor.text))
마동석
진선규
윤계상
조재윤
최귀화
허성태
임형준

각 배우별 상세 정보를 document에 넣고 싶다.

  • 각 배우별 상세 정보를 별도 컬럼으로 만들려했더니, 각 배우별 상세 정보 항목이 다르다!
  • 모든 상세 정보 항목을 컬럼으로 만들고, 각 컬럼에 매칭되는 컬럼값을 넣기가 쉽지 않다. 코드도 복잡하고!
  • Mongodb는 NoSQL -> 통째로 집어넣자.!
  • embedded document
    • document 의 컬럼값으로 document를 넣을 수 있다.

크롤링해서, embedded document 로 각 배우별 상세 정보를 통째로 만들고, document 컬럼에 넣기

actor_detail_info = list()

for actor in actors:

    actor_info_dict = dict()
    
    actor_info = 'http://www.cine21.com' + actor.select_one('a').attrs['href']
    
    response_actor = requests.get(actor_info)
    
    soup_actor = BeautifulSoup(response_actor.content.decode('utf-8'), 'html.parser')
    
    actor_datas = soup_actor.select('ul.default_info')
    for actor_data in soup_actor.select('ul.default_info'):
        for actor_item in actor_data.select('li'):
            actor_item_text = re.sub('<span.*?>.*?</span>', '', str(actor_item))
            actor_item_text = re.sub('<.+?>', '', actor_item_text)
            actor_info_dict[actor_item.select_one('span.tit').text] = actor_item_text.strip()
    actor_detail_info.append(actor_info_dict)
print(actor_detail_info)
[{'직업': '배우', '생년월일': '1971-03-01', '성별': '남', '홈페이지': 'https://www.instagram.com/madongseok_/\nhttps://twitter.com/madongseok12'}, {'직업': '배우', '생년월일': '1977-09-13', '성별': '남'}, {'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}, {'직업': '배우', '생년월일': '1974-09-15', '성별': '남', '홈페이지': 'https://www.instagram.com/jojaeyun/'}, {'직업': '배우', '생년월일': '1978-03-03', '성별': '남', '신장/체중': '181cm, 72kg'}, {'직업': '배우', '성별': '남'}, {'직업': '배우', '생년월일': '1974-05-10', '성별': '남', '신장/체중': '177cm, 71kg', '학교': '서울예술대학 연극과', '취미': '스쿼시, 피아노', '특기': '작곡, 노래', '소속사': '나무액터스'}]
actor_info_dict = dict()

actor_info = 'http://www.cine21.com/db/person/info/?person_id=19889'

response_actor = requests.get(actor_info)

soup_actor = BeautifulSoup(response_actor.content.decode('utf-8'), 'html.parser')

actor_datas = soup_actor.select('ul.default_info')
for actor_data in soup_actor.select('ul.default_info'):
    for actor_item in actor_data.select('li'):
        actor_item_text = re.sub('<span.*?>.*?</span>', '', str(actor_item))
        actor_item_text = re.sub('<.+?>', '', actor_item_text)
        actor_info_dict[actor_item.select_one('span.tit').text] = actor_item_text.strip()
print(actor_info_dict)
{'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}
actor_info_dict = dict()

actor_info = 'http://www.cine21.com/db/person/info/?person_id=19889'

response_actor = requests.get(actor_info)

soup_actor = BeautifulSoup(response_actor.content.decode('utf-8'), 'html.parser')
soup_actor
actor_datas = soup_actor.select('ul.default_info')
actor_datas
Out[76]:
[<ul class="default_info">
 <li><span class="tit">다른 이름</span>지오디;god</li>
 <li><span class="tit">직업</span>배우</li>
 <li><span class="tit">생년월일</span>1978-12-20</li>
 <li><span class="tit">성별</span>남</li>
 <li><span class="tit">홈페이지</span>
 <a href="https://www.facebook.com/saram.yoonkyesang" target="_blank">https://www.facebook.com/saram.yoonkyesang</a><br/>
 <a href="https://www.instagram.com/kyesang78/" target="_blank">https://www.instagram.com/kyesang78/</a><br/>
 </li>
 <li><span class="tit">신장/체중</span>182cm, 62kg</li>
 <li><span class="tit">학교</span>경희대학교 포스트모던학과 휴학</li>
 <li><span class="tit">취미</span>컴퓨터게임, 스노우보드</li>
 <li><span class="tit">특기</span>표정연기, 춤추기</li>
 </ul>]
for actor_data in soup_actor.select('ul.default_info'):
    for actor_item in actor_data.select('li'):
        actor_item_text = re.sub('<span.*?>.*?</span>', '', str(actor_item))
        actor_item_text = re.sub('<.+?>', '', actor_item_text)
        actor_info_dict[actor_item.select_one('span.tit').text] = actor_item_text.strip()
print(actor_info_dict)
{'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}

흥행지수 뽑기

actor_rates = soup.select('li.people_li ul.num_info strong')
for actor_rate in actor_rates:
    print(actor_rate.text)
59,361
42,733
40,181
37,957
35,754
33,659
33,487

흥행지수 숫자로 만들어주기

actor_rate_list = list()
for actor_rate in actor_rates:
    actor_rate_list.append(int(actor_rate.text.replace(",","")))  # int() 로 해주지 않으면, 문자열로 됩니다.
actor_rate_list
Out[84]:
[59361, 42733, 40181, 37957, 35754, 33659, 33487]
actor_list = list()
for actor in actors:
    actor_list.append(re.sub("\(\w+\)", "", actor.text))
actor_list
Out[87]:
['마동석', '진선규', '윤계상', '조재윤', '최귀화', '허성태', '임형준']

각 배우별 출연 영화를 document에 저장하고 싶다.

  • 출연 영화는 한 개가 될 수도 있고, 여러 개가 될 수도 있음
  • 파이썬은 리스트, mongodb document는 컬럼에 배열(array)로 넣으면 됨
  • 어떻게? 다음과 같이 합니다.
movie_list = list()
movies = soup.select('li.people_li ul.mov_list')
for movie in movies:
    actor_movie = list()
    movie_titles = movie.select('li a span')
    for movie_title in movie_titles:
        actor_movie.append(movie_title.text)
    movie_list.append(actor_movie)
movie_list
Out[91]:
[['범죄도시', '부라더', '부산행'],
 ['범죄도시', '남한산성'],
 ['범죄도시', '발레 교습소', '죽여주는 여자'],
 ['범죄도시', '역모 - 반란의 시대'],
 ['범죄도시', '택시운전사', '조작된 도시'],
 ['남한산성', '범죄도시', '부라더'],
 ['범죄도시', '이웃집 스타']]
  • 이렇게 만든 리스트를 넣으면 됩니다.

insert_one() 로 하나씩 데이터 입력하기 (반복문과 함께 사용하면, 여러 데이터를 넣을 수 있음)

  • actor_list: 배우 이름
  • actor_details: 배우 상세 정보
  • actor_rate: 흥행 지수
  • date: 기준월
  • movie_list: 출연 영화 리스트!
for num, actor in enumerate(actor_list):
    actor_collection.insert_one(
        {"actor":actor_list[num], 
         "actor_details": actor_detail_info[num], 
         "actor_rate":actor_rate_list[num], 
         "date":month, 
         "movie_list":movie_list[num]})
docs = actor_collection.find()
for doc in docs:
    print(doc)

collection 삭제하기

actor_collection.drop()
docs = actor_collection.find()
for doc in docs:
    print(doc)

Dictionary 타입으로 만들어서 한번에 insert_many() 로 데이터 입력하기

actor_info = list()
for num, actor in enumerate(actor_list):
    actor_info.append(
        {"actor":actor_list[num], 
         "actor_details": actor_detail_info[num], 
         "actor_rate":actor_rate_list[num], 
         "date":month, 
         "movie_list":movie_list[num]}
    )
actor_info
actor_collection.insert_many(actor_info)
Out[101]:
<pymongo.results.InsertManyResult at 0x106acbdc8>
docs = actor_collection.find()
for doc in docs:
    print(doc)
{'_id': ObjectId('5a0ae08481f6402f8ff056bc'), 'actor': '마동석', 'actor_details': {'직업': '배우', '생년월일': '1971-03-01', '성별': '남', '홈페이지': 'https://www.instagram.com/madongseok_/\nhttps://twitter.com/madongseok12'}, 'actor_rate': 59361, 'date': '2017-10', 'movie_list': ['범죄도시', '부라더', '부산행']}
{'_id': ObjectId('5a0ae08481f6402f8ff056bd'), 'actor': '진선규', 'actor_details': {'직업': '배우', '생년월일': '1977-09-13', '성별': '남'}, 'actor_rate': 42733, 'date': '2017-10', 'movie_list': ['범죄도시', '남한산성']}
{'_id': ObjectId('5a0ae08481f6402f8ff056be'), 'actor': '윤계상', 'actor_details': {'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}, 'actor_rate': 40181, 'date': '2017-10', 'movie_list': ['범죄도시', '발레 교습소', '죽여주는 여자']}
{'_id': ObjectId('5a0ae08481f6402f8ff056bf'), 'actor': '조재윤', 'actor_details': {'직업': '배우', '생년월일': '1974-09-15', '성별': '남', '홈페이지': 'https://www.instagram.com/jojaeyun/'}, 'actor_rate': 37957, 'date': '2017-10', 'movie_list': ['범죄도시', '역모 - 반란의 시대']}
{'_id': ObjectId('5a0ae08481f6402f8ff056c0'), 'actor': '최귀화', 'actor_details': {'직업': '배우', '생년월일': '1978-03-03', '성별': '남', '신장/체중': '181cm, 72kg'}, 'actor_rate': 35754, 'date': '2017-10', 'movie_list': ['범죄도시', '택시운전사', '조작된 도시']}
{'_id': ObjectId('5a0ae08481f6402f8ff056c1'), 'actor': '허성태', 'actor_details': {'직업': '배우', '성별': '남'}, 'actor_rate': 33659, 'date': '2017-10', 'movie_list': ['남한산성', '범죄도시', '부라더']}
{'_id': ObjectId('5a0ae08481f6402f8ff056c2'), 'actor': '임형준', 'actor_details': {'직업': '배우', '생년월일': '1974-05-10', '성별': '남', '신장/체중': '177cm, 71kg', '학교': '서울예술대학 연극과', '취미': '스쿼시, 피아노', '특기': '작곡, 노래', '소속사': '나무액터스'}, 'actor_rate': 33487, 'date': '2017-10', 'movie_list': ['범죄도시', '이웃집 스타']}

Update (컬럼명 변경 예제)

actor_collection.update_many( {}, { "$rename": { "actor": "actor_name" } } )
Out[104]:
<pymongo.results.UpdateResult at 0x106c07088>
docs = actor_collection.find()
for doc in docs:
    print(doc)
{'_id': ObjectId('5a0ae08481f6402f8ff056bc'), 'actor_details': {'직업': '배우', '생년월일': '1971-03-01', '성별': '남', '홈페이지': 'https://www.instagram.com/madongseok_/\nhttps://twitter.com/madongseok12'}, 'actor_rate': 59361, 'date': '2017-10', 'movie_list': ['범죄도시', '부라더', '부산행'], 'actor_name': '마동석'}
{'_id': ObjectId('5a0ae08481f6402f8ff056bd'), 'actor_details': {'직업': '배우', '생년월일': '1977-09-13', '성별': '남'}, 'actor_rate': 42733, 'date': '2017-10', 'movie_list': ['범죄도시', '남한산성'], 'actor_name': '진선규'}
{'_id': ObjectId('5a0ae08481f6402f8ff056be'), 'actor_details': {'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}, 'actor_rate': 40181, 'date': '2017-10', 'movie_list': ['범죄도시', '발레 교습소', '죽여주는 여자'], 'actor_name': '윤계상'}
{'_id': ObjectId('5a0ae08481f6402f8ff056bf'), 'actor_details': {'직업': '배우', '생년월일': '1974-09-15', '성별': '남', '홈페이지': 'https://www.instagram.com/jojaeyun/'}, 'actor_rate': 37957, 'date': '2017-10', 'movie_list': ['범죄도시', '역모 - 반란의 시대'], 'actor_name': '조재윤'}
{'_id': ObjectId('5a0ae08481f6402f8ff056c0'), 'actor_details': {'직업': '배우', '생년월일': '1978-03-03', '성별': '남', '신장/체중': '181cm, 72kg'}, 'actor_rate': 35754, 'date': '2017-10', 'movie_list': ['범죄도시', '택시운전사', '조작된 도시'], 'actor_name': '최귀화'}
{'_id': ObjectId('5a0ae08481f6402f8ff056c1'), 'actor_details': {'직업': '배우', '성별': '남'}, 'actor_rate': 33659, 'date': '2017-10', 'movie_list': ['남한산성', '범죄도시', '부라더'], 'actor_name': '허성태'}
{'_id': ObjectId('5a0ae08481f6402f8ff056c2'), 'actor_details': {'직업': '배우', '생년월일': '1974-05-10', '성별': '남', '신장/체중': '177cm, 71kg', '학교': '서울예술대학 연극과', '취미': '스쿼시, 피아노', '특기': '작곡, 노래', '소속사': '나무액터스'}, 'actor_rate': 33487, 'date': '2017-10', 'movie_list': ['범죄도시', '이웃집 스타'], 'actor_name': '임형준'}
실습
actor_details 필드 이름을 actor_info 로 변경하기
actor_collection.update_many( {}, { "$rename": { "actor_details": "actor_info" } } )
Out[106]:
<pymongo.results.UpdateResult at 0x106c07188>
docs = actor_collection.find()
for doc in docs:
    print(doc)
{'_id': ObjectId('5a0ae08481f6402f8ff056bc'), 'actor_rate': 59361, 'date': '2017-10', 'movie_list': ['범죄도시', '부라더', '부산행'], 'actor_name': '마동석', 'actor_info': {'직업': '배우', '생년월일': '1971-03-01', '성별': '남', '홈페이지': 'https://www.instagram.com/madongseok_/\nhttps://twitter.com/madongseok12'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056bd'), 'actor_rate': 42733, 'date': '2017-10', 'movie_list': ['범죄도시', '남한산성'], 'actor_name': '진선규', 'actor_info': {'직업': '배우', '생년월일': '1977-09-13', '성별': '남'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056be'), 'actor_rate': 40181, 'date': '2017-10', 'movie_list': ['범죄도시', '발레 교습소', '죽여주는 여자'], 'actor_name': '윤계상', 'actor_info': {'다른 이름': '지오디;god', '직업': '배우', '생년월일': '1978-12-20', '성별': '남', '홈페이지': 'https://www.facebook.com/saram.yoonkyesang\nhttps://www.instagram.com/kyesang78/', '신장/체중': '182cm, 62kg', '학교': '경희대학교 포스트모던학과 휴학', '취미': '컴퓨터게임, 스노우보드', '특기': '표정연기, 춤추기'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056bf'), 'actor_rate': 37957, 'date': '2017-10', 'movie_list': ['범죄도시', '역모 - 반란의 시대'], 'actor_name': '조재윤', 'actor_info': {'직업': '배우', '생년월일': '1974-09-15', '성별': '남', '홈페이지': 'https://www.instagram.com/jojaeyun/'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056c0'), 'actor_rate': 35754, 'date': '2017-10', 'movie_list': ['범죄도시', '택시운전사', '조작된 도시'], 'actor_name': '최귀화', 'actor_info': {'직업': '배우', '생년월일': '1978-03-03', '성별': '남', '신장/체중': '181cm, 72kg'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056c1'), 'actor_rate': 33659, 'date': '2017-10', 'movie_list': ['남한산성', '범죄도시', '부라더'], 'actor_name': '허성태', 'actor_info': {'직업': '배우', '성별': '남'}}
{'_id': ObjectId('5a0ae08481f6402f8ff056c2'), 'actor_rate': 33487, 'date': '2017-10', 'movie_list': ['범죄도시', '이웃집 스타'], 'actor_name': '임형준', 'actor_info': {'직업': '배우', '생년월일': '1974-05-10', '성별': '남', '신장/체중': '177cm, 71kg', '학교': '서울예술대학 연극과', '취미': '스쿼시, 피아노', '특기': '작곡, 노래', '소속사': '나무액터스'}}
  • 컬렉션 객체 이름도 바꿀 수 있겠지요
actor = actor_collection