├── log ├── requirements.txt ├── .gitattributes ├── output ├── directors │ ├── 10001 │ ├── 10100 │ ├── 85444 │ ├── 134963 │ └── 171541 ├── bestscripts │ ├── 10001 │ ├── 10100 │ ├── 134963 │ └── 171541 ├── meta │ ├── 85444.json │ ├── 10100.json │ ├── 10001.json │ ├── 171541.json │ └── 134963.json ├── staffs │ ├── 10001 │ ├── 10100 │ ├── 134963 │ └── 171541 ├── actors │ ├── 10001 │ ├── 10100 │ ├── 134963 │ └── 171541 └── comments │ ├── 10001 │ ├── 10100 │ ├── 134963 │ └── 171541 ├── naver_movie_scraper ├── about.py ├── __init__.py ├── script.py ├── detail.py ├── utils.py ├── comments.py ├── basic.py ├── comments_with_userlist.py └── cli.py ├── .gitignore ├── setup.py ├── enhance_comments_user_idx.py └── README.md /log: -------------------------------------------------------------------------------- 1 | All scraping tasks were terminated successfully. 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | lxml 2 | beautifulsoup4==4.7.1 3 | tqdm>=4.46.0 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | output/* linguist-vendored 2 | data/* linguist-vendored -------------------------------------------------------------------------------- /output/directors/10100: -------------------------------------------------------------------------------- 1 | {"id": 1574, "k_name": "토니 스콧", "e_name": "Tony Scott"} 2 | -------------------------------------------------------------------------------- /output/directors/85444: -------------------------------------------------------------------------------- 1 | {"id": 290377, "k_name": "브라이언 하", "e_name": "Bryan Ha"} 2 | -------------------------------------------------------------------------------- /output/directors/134963: -------------------------------------------------------------------------------- 1 | {"id": 175108, "k_name": "데이미언 셔젤", "e_name": "Damien Chazelle"} 2 | -------------------------------------------------------------------------------- /output/directors/171541: -------------------------------------------------------------------------------- 1 | {"id": 161188, "k_name": "파스칼 로지에", "e_name": "Pascal Laugier"} 2 | -------------------------------------------------------------------------------- /output/directors/10001: -------------------------------------------------------------------------------- 1 | {"id": 2462, "k_name": "쥬세페 토르나토레", "e_name": "Giuseppe Tornatore"} 2 | -------------------------------------------------------------------------------- /naver_movie_scraper/about.py: -------------------------------------------------------------------------------- 1 | __author__ = 'lovit' 2 | __name__ = 'naver_movie_scraper' 3 | __version__ = '1.0.0' 4 | -------------------------------------------------------------------------------- /output/bestscripts/171541: -------------------------------------------------------------------------------- 1 | {"text": "우어어억 꾸어엉ㄱ 킁킁", "chracter": "뚱뚱한 남자롭 아처", "description": "만지작", "num_agree": 1} 2 | {"text": "뭐해! 빨리 삭제해", "chracter": "뚱뚱한 남자롭 아처", "description": "", "num_agree": 0} 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS 2 | .DS_Store 3 | 4 | # Python 5 | *.pyc 6 | __pycache__/ 7 | dist/ 8 | build/ 9 | *.egg-info/ 10 | 11 | # Experiments 12 | tmp/ 13 | comment_indices 14 | user_comments/ 15 | data/ 16 | log 17 | 18 | # Jupyter notebook 19 | .ipynb_checkpoints/ 20 | -------------------------------------------------------------------------------- /output/meta/85444.json: -------------------------------------------------------------------------------- 1 | { 2 | "movie_idx": 85444, 3 | "expert_score": -1, 4 | "netizen_score": -1, 5 | "title": "안녕, 북극곰", 6 | "e_title": "The Breaking Point , 2011", 7 | "genres": [ 8 | "애니메이션" 9 | ], 10 | "countries": [ 11 | "미국" 12 | ], 13 | "running_time": 4, 14 | "open_date": [], 15 | "grade": "", 16 | "story": "엄마 북극곰과 아기 북극곰이 환경 파괴와 오염으로 위기를 맞는다. 안타까운 북극곰 모자의 모습은 인간이 자연에 끼치는 영향에 대해 다시 생각하게 만든다.", 17 | "making_note": "", 18 | "box_office": -1 19 | } -------------------------------------------------------------------------------- /output/staffs/10001: -------------------------------------------------------------------------------- 1 | {"id": 2462, "k_name": "쥬세페 토르나토레", "e_name": "Giuseppe Tornatore", "role": "각본"} 2 | {"id": 204659, "k_name": "프랑코 크리스탈디", "e_name": "Franco Cristaldi", "role": "제작"} 3 | {"id": 35223, "k_name": "블라스코 지우라토", "e_name": "Blasco Giurato", "role": "촬영"} 4 | {"id": 407, "k_name": "엔니오 모리꼬네", "e_name": "Ennio Morricone", "role": "음악"} 5 | {"id": 19632, "k_name": "안드레아 크리산티", "e_name": "Andrea Crisanti", "role": "미술"} 6 | {"id": 28777, "k_name": "마리오 모라", "e_name": "Mario Morra", "role": "편집"} 7 | -------------------------------------------------------------------------------- /naver_movie_scraper/__init__.py: -------------------------------------------------------------------------------- 1 | from .about import __author__ 2 | from .about import __name__ 3 | from .about import __version__ 4 | from .basic import scrap_basic 5 | from .comments import scrap_comments 6 | from .comments_with_userlist import scrap_comments_of_a_user 7 | from .comments_with_userlist import scan_comment_indices 8 | from .detail import scrap_casting 9 | from .script import scrap_bestscripts 10 | from .utils import get_soup 11 | from .utils import text_normalize 12 | from .utils import save_list_of_dict 13 | from .utils import load_list_of_dict 14 | from .utils import save_json 15 | -------------------------------------------------------------------------------- /output/actors/171541: -------------------------------------------------------------------------------- 1 | {"id": 241593, "k_name": "크리스탈 리드", "e_name": "Crystal Reed", "part": "주연", "role": "성인 베스 역", "cating_order": 1} 2 | {"id": 413741, "k_name": "아나스타샤 필립스", "e_name": "Anastasia Phillips", "part": "주연", "role": "성인 베라 역", "cating_order": 2} 3 | {"id": 351505, "k_name": "에밀리아 존스", "e_name": "Emilia Jones", "part": "주연", "role": "어린 베스 역", "cating_order": 3} 4 | {"id": 374004, "k_name": "테일러 힉슨", "e_name": "Taylor Hickson", "part": "주연", "role": "어린 베라 역", "cating_order": 4} 5 | {"id": 219804, "k_name": "밀레느 파머", "e_name": "Mylene Farmer", "part": "주연", "role": "폴린 역", "cating_order": 5} 6 | {"id": 66632, "k_name": "앨리시아 존스턴", "e_name": "Alicia Johnston", "part": "조연", "role": "쿠퍼 역", "cating_order": 6} 7 | {"id": 181125, "k_name": "메리암 번스타인", "e_name": "Mariam Bernstein", "part": "조연", "role": "자넷 역", "cating_order": 7} 8 | -------------------------------------------------------------------------------- /output/meta/10100.json: -------------------------------------------------------------------------------- 1 | { 2 | "movie_idx": 10100, 3 | "expert_score": 7.5, 4 | "netizen_score": 9.28, 5 | "title": "탑건", 6 | "e_title": "Top Gun , 1986", 7 | "genres": [ 8 | "드라마", 9 | "액션" 10 | ], 11 | "countries": [ 12 | "미국" 13 | ], 14 | "running_time": 109, 15 | "open_date": [ 16 | "2018-08-29", 17 | "1987-12-19" 18 | ], 19 | "grade": "15세 관람가", 20 | "story": "최고의 파일럿에 도전하는 불타는 젊음!\n마침내 돌아온 전설을 만나라!\n해군 최신 전투기 F-14기를 모는 젊은 조종사 매버릭 대위(톰 크루즈)는 최고의 실력을 자부하는 파일럿으로 최정예 전투기 조종사를 양성하는 ‘탑건’ 훈련학교에 입학하게 된다. 그리고 그곳에서 생도들의 교육을 담당하는 항공물리학 전문가 찰리(켈리 맥길리스)와 사랑에 빠진다. 그러나 비행 훈련 도중 매버릭이 몰던 전투기가 제트 기류에 빠지면서 엔진 고장을 일으키고, 이때 함께 탈출을 시도하던 파트너 구즈가 목숨을 잃게 된다. 의문의 사고로 돌아가신 전투기 조종사였던 아버지에 대한 상처를 이해한 유일한 친구 구즈의 죽음에 충격에 빠진 매버릭은 파일럿의 꿈도 연인과의 사랑도 모두 포기하려 하는데...", 21 | "making_note": "", 22 | "box_office": -1 23 | } -------------------------------------------------------------------------------- /output/actors/10100: -------------------------------------------------------------------------------- 1 | {"id": 1558, "k_name": "톰 크루즈", "e_name": "Tom Cruise", "part": "주연", "role": "매버릭 역", "cating_order": 1} 2 | {"id": 855, "k_name": "켈리 맥길리스", "e_name": "Kelly McGillis", "part": "주연", "role": "찰리 역", "cating_order": 2} 3 | {"id": 1581, "k_name": "발 킬머", "e_name": "Val Kilmer", "part": "조연", "role": "아이스맨 역", "cating_order": 3} 4 | {"id": 5719, "k_name": "안소니 에드워즈", "e_name": "Anthony Edwards", "part": "조연", "role": "구스 역", "cating_order": 4} 5 | {"id": 4739, "k_name": "톰 스커릿", "e_name": "Tom Skerritt", "part": "조연", "role": "바이퍼 역", "cating_order": 5} 6 | {"id": 1053, "k_name": "멕 라이언", "e_name": "Meg Ryan", "part": "조연", "role": "캐롤 역", "cating_order": 6} 7 | {"id": 8095, "k_name": "존 스톡웰", "e_name": "John Stockwell", "part": "조연", "role": "쿠거 역", "cating_order": 7} 8 | {"id": 1075, "k_name": "마이클 아이언사이드", "e_name": "Michael Ironside", "part": "조연", "role": "제스터 역", "cating_order": 8} 9 | -------------------------------------------------------------------------------- /output/meta/10001.json: -------------------------------------------------------------------------------- 1 | { 2 | "movie_idx": 10001, 3 | "expert_score": 8.38, 4 | "netizen_score": 9.47, 5 | "title": "시네마 천국", 6 | "e_title": "Cinema Paradiso , 1988", 7 | "genres": [ 8 | "드라마", 9 | "멜로/로맨스" 10 | ], 11 | "countries": [ 12 | "프랑스", 13 | "이탈리아" 14 | ], 15 | "running_time": 124, 16 | "open_date": [ 17 | "2013-09-26", 18 | "1993-11-13", 19 | "1990-07-07" 20 | ], 21 | "grade": "전체 관람가", 22 | "story": "영화가 세상의 전부인 소년 토토와 낡은 마을 극장의 영사기사 알프레도의 애틋한 우정!\n25년간 전세계를 웃고 울린 감동대작의 부활!\n유명 영화감독으로 활약 중인 토토(자크 페렝)는 고향 마을의 영사기사 알프레도(필립 느와레)의 사망소식에 30년 만에 고향을 찾는다. 어린 시절 영화가 세상의 전부였던 소년 토토(살바토레 카스치오)는 학교 수업을 마치면 마을 광장에 있는 낡은 ‘시네마천국’이라는 극장으로 달려가 영사 기사 알프레도와 친구로 지내며 어깨너머로 영사기술을 배운다. 어느 날 관객들을 위해 광장에서 야외 상영을 해주던 알프레도가 그만 화재 사고로 실명하게 되고, 토토가 그의 뒤를 이어 ‘시네마천국’의 영상기사로 일하게 된다. 실명한 후에도 토토의 친구이자 아버지로 든든한 정신적 지주가 되어준 알프레도는 청년이 된 토토(마코 레오나디)가 사랑하는 여자 엘레나(아그네즈 나노)의 부모님의 반대로 좌절하자 넓은 세상으로 나가서 더 많은 것을 배우라며 권유하고 토토는 고향을 떠나게 되는데...\n영화백과 보기", 23 | "making_note": "", 24 | "box_office": -1 25 | } -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | 5 | with open(os.path.join(os.path.dirname(__file__), 'README.md'), encoding='utf-8') as f: 6 | long_description = f.read() 7 | 8 | 9 | def get_about(): 10 | about = {} 11 | basedir = os.path.abspath(os.path.dirname(__file__)) 12 | with open(os.path.join(basedir, 'naver_movie_scraper', 'about.py')) as f: 13 | exec(f.read(), about) 14 | return about 15 | 16 | 17 | def requirements(): 18 | with open(os.path.join(os.path.dirname(__file__), 'requirements.txt'), encoding='utf-8') as f: 19 | return f.read().splitlines() 20 | 21 | 22 | about = get_about() 23 | setup( 24 | name=about['__name__'], 25 | version=about['__version__'], 26 | author=about['__author__'], 27 | url='https://github.com/lovit/naver_movie_scraper', 28 | description="Naver movie scraper", 29 | long_description=long_description, 30 | long_description_content_type='text/markdown', 31 | install_requires=requirements(), 32 | keywords = [], 33 | packages=find_packages(), 34 | entry_points = { 35 | 'console_scripts': ['naver_movie_scraper=naver_movie_scraper.cli:main'], 36 | } 37 | ) -------------------------------------------------------------------------------- /output/staffs/171541: -------------------------------------------------------------------------------- 1 | {"id": 352727, "k_name": "롭 아처", "e_name": "뚱뚱한 남자", "role": ""} 2 | {"id": 161188, "k_name": "파스칼 로지에", "e_name": "Pascal Laugier", "role": "각본"} 3 | {"id": 426009, "k_name": "이안 다이머맨", "e_name": "Ian Dimerman", "role": "제작"} 4 | {"id": 291661, "k_name": "스콧 케네디", "e_name": "Scott Kennedy", "role": "제작"} 5 | {"id": 413224, "k_name": "장-찰스 레비", "e_name": "Jean-Charles Levy", "role": "제작"} 6 | {"id": 426008, "k_name": "니콜라스 마뉴엘", "e_name": "Nicolas Manuel", "role": "제작"} 7 | {"id": 343582, "k_name": "클레망 미세레즈", "e_name": "Clement Miserez", "role": "제작"} 8 | {"id": 242404, "k_name": "마티유 워터", "e_name": "Matthieu Warter", "role": "제작"} 9 | {"id": 426007, "k_name": "브렌든 사와스키", "e_name": "Brendon Sawatzky", "role": "제작"} 10 | {"id": 413223, "k_name": "그레고아르 멜린", "e_name": "Gregoire Melin", "role": "기획"} 11 | {"id": 130764, "k_name": "대니 뉴아크", "e_name": "Danny Nowak", "role": "촬영"} 12 | {"id": 212181, "k_name": "토드 브라이언턴", "e_name": "Todd Bryanton", "role": "음악"} 13 | {"id": 92185, "k_name": "고든 윌딩", "e_name": "Gordon Wilding", "role": "미술"} 14 | {"id": 214152, "k_name": "사라 맥쿠든", "e_name": "Sara McCudden", "role": "세트"} 15 | {"id": 214010, "k_name": "브렌다 쉔허", "e_name": "Brenda Shenher", "role": "의상"} 16 | {"id": 289596, "k_name": "데브 싱", "e_name": "Dev Singh", "role": "편집"} 17 | {"id": 117977, "k_name": "카멘 코틱", "e_name": "Carmen Kotyk", "role": "배역"} 18 | -------------------------------------------------------------------------------- /output/actors/134963: -------------------------------------------------------------------------------- 1 | {"id": 5751, "k_name": "라이언 고슬링", "e_name": "Ryan Gosling", "part": "주연", "role": "세바스찬 역", "cating_order": 1} 2 | {"id": 135256, "k_name": "엠마 스톤", "e_name": "Emma Stone", "part": "주연", "role": "미아 역", "cating_order": 2} 3 | {"id": 97297, "k_name": "존 레전드", "e_name": "John Legend", "part": "조연", "role": "키이스 역", "cating_order": 3} 4 | {"id": 66630, "k_name": "로즈마리 드윗", "e_name": "Rosemarie DeWitt", "part": "조연", "role": "로라 역", "cating_order": 4} 5 | {"id": 4651, "k_name": "J.K. 시몬스", "e_name": "J.K. Simmons", "part": "조연", "role": "빌- 재즈클럽 사장 역", "cating_order": 5} 6 | {"id": 347222, "k_name": "소노야 미즈노", "e_name": "Sonoya Mizuno", "part": "조연", "role": "케이틀린 역", "cating_order": 6} 7 | {"id": 353879, "k_name": "제시카 로테", "e_name": "Jessica Rothe", "part": "조연", "role": "알렉시스 역", "cating_order": 7} 8 | {"id": 357382, "k_name": "칼리 헤르난데스", "e_name": "Callie Hernandez", "part": "조연", "role": "트레이시 역", "cating_order": 8} 9 | {"id": 1559, "k_name": "톰 에버렛 스콧", "e_name": "Tom Everett Scott", "part": "조연", "role": "데이비드 역", "cating_order": 9} 10 | {"id": 206823, "k_name": "핀 위트록", "e_name": "Finn Wittrock", "part": "조연", "role": "그렉 역", "cating_order": 10} 11 | {"id": 7269, "k_name": "제이슨 푸치스", "e_name": "Jason Fuchs", "part": "조연", "role": "카를로 역", "cating_order": 11} 12 | {"id": 400105, "k_name": "올리비아 해밀턴", "e_name": "Olivia Hamilton", "part": "조연", "role": "브리 역", "cating_order": 12} 13 | -------------------------------------------------------------------------------- /naver_movie_scraper/script.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | from .utils import get_soup 4 | from .utils import text_normalize 5 | 6 | 7 | script_url_form = 'http://movie.naver.com/movie/bi/mi/script.nhn?code={}&page={}' # idx, page 8 | 9 | def scrap_bestscripts(idx, limit=-1, sleep=0.05): 10 | scripts = [] 11 | max_page = num_of_bestscript_pages(idx) 12 | for p in range(1, max_page + 1): 13 | url = script_url_form.format(idx, p) 14 | soup = get_soup(url) 15 | scripts += scrap_from_a_page(soup) 16 | time.sleep(sleep) 17 | if limit > 0 and limit <= p: 18 | break 19 | return scripts 20 | 21 | def scrap_from_a_page(soup): 22 | scripts = [] 23 | for script in soup.select('ul[class=lines] li div[class=lines_area2]'): 24 | try: 25 | text = text_normalize(script.select('p[class=one_line]')[0].text) 26 | character = text_normalize(script.select('p[class=char_part]')[0].text) 27 | description = text_normalize(script.select('p[class=line_desc]')[0].text) 28 | agree = int(script.select('span[class=w_recomm] em')[-1].text) 29 | scripts.append( 30 | {'text': text, 31 | 'chracter': character, 32 | 'description': description, 33 | 'num_agree': agree 34 | }) 35 | except Exception as e: 36 | continue 37 | return scripts 38 | 39 | def num_of_bestscript_pages(idx): 40 | url = script_url_form.format(idx, 1) 41 | soup = get_soup(url) 42 | 43 | try: 44 | num_scripts = int(soup.select('span[class=cnt] em')[0].text.replace(',','')) 45 | return math.ceil(num_scripts / 10) 46 | except Exception as e: 47 | return 0 -------------------------------------------------------------------------------- /output/actors/10001: -------------------------------------------------------------------------------- 1 | {"id": 4374, "k_name": "자끄 페렝", "e_name": "Jacques Perrin", "part": "주연", "role": "중년 살바토레 역", "cating_order": 1} 2 | {"id": 178, "k_name": "브리지트 포시", "e_name": "Brigitte Fossey", "part": "주연", "role": "중년 엘레나 역", "cating_order": 2} 3 | {"id": 3241, "k_name": "필립 느와레", "e_name": "Philippe Noiret", "part": "주연", "role": "알프레도 역", "cating_order": 3} 4 | {"id": 47952, "k_name": "살바토레 카스치오", "e_name": "Salvatore Cascio", "part": "주연", "role": "소년 살바토레 역", "cating_order": 4} 5 | {"id": 47953, "k_name": "안토넬라 아틸리", "e_name": "Antonella Attili", "part": "조연", "role": "", "cating_order": 5} 6 | {"id": 19538, "k_name": "엔조 카나발", "e_name": "Enzo Cannavale", "part": "조연", "role": "", "cating_order": 6} 7 | {"id": 18991, "k_name": "아이사 다니엘리", "e_name": "Isa Danieli", "part": "조연", "role": "", "cating_order": 7} 8 | {"id": 47954, "k_name": "레오 굴로타", "e_name": "Leo Gullotta", "part": "조연", "role": "", "cating_order": 8} 9 | {"id": 6038, "k_name": "마르코 레오나르디", "e_name": "Marco Leonardi", "part": "조연", "role": "청년 살바토레 역", "cating_order": 9} 10 | {"id": 24102, "k_name": "푸펠라 마지오", "e_name": "Pupella Maggio", "part": "조연", "role": "", "cating_order": 10} 11 | {"id": 47955, "k_name": "아그네즈 나노", "e_name": "Agnese Nano", "part": "조연", "role": "젊은 엘레나 역", "cating_order": 11} 12 | {"id": 16903, "k_name": "레오폴도 트리에스테", "e_name": "Leopoldo Trieste", "part": "조연", "role": "", "cating_order": 12} 13 | {"id": 47956, "k_name": "타노 시마로사", "e_name": "Tano Cimarosa", "part": "조연", "role": "", "cating_order": 13} 14 | {"id": 47957, "k_name": "니콜라 디 핀토", "e_name": "Nicola Di Pinto", "part": "조연", "role": "", "cating_order": 14} 15 | {"id": 47958, "k_name": "로버타 레나", "e_name": "Roberta Lena", "part": "조연", "role": "", "cating_order": 15} 16 | {"id": 47959, "k_name": "니노 테르조", "e_name": "Nino Terzo", "part": "조연", "role": "", "cating_order": 16} 17 | -------------------------------------------------------------------------------- /output/staffs/10100: -------------------------------------------------------------------------------- 1 | {"id": 11121, "k_name": "베리 텁", "e_name": "울프만", "role": ""} 2 | {"id": 2953, "k_name": "릭 로소비치", "e_name": "슬라이더", "role": ""} 3 | {"id": 1546, "k_name": "팀 로빈스", "e_name": "메를린", "role": ""} 4 | {"id": 10140, "k_name": "클레런스 길야드 주니어", "e_name": "선다운", "role": ""} 5 | {"id": 11122, "k_name": "윕 허블리", "e_name": "헐리우드", "role": ""} 6 | {"id": 5105, "k_name": "제임스 톨칸", "e_name": "스팅거", "role": ""} 7 | {"id": 16600, "k_name": "아드리안 패스더", "e_name": "치퍼", "role": ""} 8 | {"id": 100551, "k_name": "랜들 브래디", "e_name": "데이비스", "role": ""} 9 | {"id": 204974, "k_name": "듀크 스트로드", "e_name": "존슨", "role": ""} 10 | {"id": 252791, "k_name": "브라이언 쉬핸", "e_name": "스프롤", "role": ""} 11 | {"id": 196426, "k_name": "론 클락", "e_name": "부상당한 사령관", "role": ""} 12 | {"id": 44471, "k_name": "프랭크 퍼스", "e_name": "바텐더", "role": ""} 13 | {"id": 20913, "k_name": "짐 캐쉬", "e_name": "Jim Cash", "role": "각본"} 14 | {"id": 20912, "k_name": "잭 엡스 주니어", "e_name": "Jack Epps Jr.", "role": "각본"} 15 | {"id": 2790, "k_name": "돈 심슨", "e_name": "Don Simpson", "role": "제작"} 16 | {"id": 707, "k_name": "제리 브룩하이머", "e_name": "Jerry Bruckheimer", "role": "제작"} 17 | {"id": 33256, "k_name": "워렌 스카런", "e_name": "Warren Skaaren", "role": "협력프로듀서"} 18 | {"id": 17275, "k_name": "빌 바달라토", "e_name": "Bill Badalato", "role": "기획"} 19 | {"id": 22689, "k_name": "제프리 킴벌", "e_name": "Jeffrey Kimball", "role": "촬영"} 20 | {"id": 103342, "k_name": "해롤드 팰터마이어", "e_name": "Harold Faltermeyer", "role": "음악"} 21 | {"id": 29159, "k_name": "존 드커 주니어", "e_name": "John DeCuir Jr.", "role": "미술"} 22 | {"id": 200166, "k_name": "로버트 R. 벤턴", "e_name": "Robert R. Benton", "role": "세트"} 23 | {"id": 28681, "k_name": "빌리 웨버", "e_name": "Billy Weber", "role": "편집"} 24 | {"id": 17286, "k_name": "크리스 레번즌", "e_name": "Chris Lebenzon", "role": "편집"} 25 | {"id": 9972, "k_name": "마저리 심킨", "e_name": "Margery Simkin", "role": "배역"} 26 | -------------------------------------------------------------------------------- /output/staffs/134963: -------------------------------------------------------------------------------- 1 | {"id": 395074, "k_name": "레시마 게이자", "e_name": "도로위 댄서", "role": ""} 2 | {"id": 150418, "k_name": "에이미 콘", "e_name": "유명한 여배우", "role": ""} 3 | {"id": 214488, "k_name": "테리 월터스", "e_name": "린다- 카페 매니저", "role": ""} 4 | {"id": 197878, "k_name": "트레버 리자우어", "e_name": "발렛", "role": ""} 5 | {"id": 28674, "k_name": "메건 페이", "e_name": "미아 엄마", "role": ""} 6 | {"id": 19339, "k_name": "마일즈 앤더슨", "e_name": "알리스테어- 포토그래퍼", "role": ""} 7 | {"id": 175108, "k_name": "데이미언 셔젤", "e_name": "Damien Chazelle", "role": "각본"} 8 | {"id": 194407, "k_name": "프레드 버거", "e_name": "Fred Berger", "role": "제작"} 9 | {"id": 50303, "k_name": "게리 길버트", "e_name": "Gary Gilbert", "role": "제작"} 10 | {"id": 193715, "k_name": "조던 호로위츠", "e_name": "Jordan Horowitz", "role": "제작"} 11 | {"id": 8843, "k_name": "마크 플랫", "e_name": "Marc Platt", "role": "제작"} 12 | {"id": 26386, "k_name": "마이클 보그", "e_name": "Michael Beugg", "role": "기획"} 13 | {"id": 166551, "k_name": "마이크 잭슨", "e_name": "Mike Jackson", "role": "기획"} 14 | {"id": 97297, "k_name": "존 레전드", "e_name": "John Legend", "role": "기획"} 15 | {"id": 396809, "k_name": "치우윤 롱", "e_name": "Qiuyun Long", "role": "기획"} 16 | {"id": 49828, "k_name": "태드 럭킨빌", "e_name": "Thad Luckinbill", "role": "기획"} 17 | {"id": 285916, "k_name": "제스민 맥글레이드 차젤레", "e_name": "Jasmine McGlade Chazelle", "role": "기획"} 18 | {"id": 175582, "k_name": "몰리 스미스", "e_name": "Molly Smith", "role": "기획"} 19 | {"id": 412688, "k_name": "타이 스타이클로리어스", "e_name": "Ty Stiklorius", "role": "기획"} 20 | {"id": 412689, "k_name": "트렌트 럭킨빌", "e_name": "Trent Luckinbill", "role": "기획"} 21 | {"id": 231357, "k_name": "라이너스 산드그렌", "e_name": "Linus Sandgren", "role": "촬영"} 22 | {"id": 356831, "k_name": "아이-링 리", "e_name": "Ai-Ling Lee", "role": "사운드디자인"} 23 | {"id": 367641, "k_name": "저스틴 허위츠", "e_name": "Justin Hurwitz", "role": "음악"} 24 | {"id": 8553, "k_name": "데이빗 와스코", "e_name": "David Wasco", "role": "미술"} 25 | {"id": 215120, "k_name": "오스틴 고르그", "e_name": "Austin Gorg", "role": "미술"} 26 | {"id": 172602, "k_name": "샌디 레이놀즈-와스코", "e_name": "Sandy Reynolds-Wasco", "role": "미술"} 27 | {"id": 342496, "k_name": "크리스 르두", "e_name": "Chris LeDoux", "role": "시각효과"} 28 | {"id": 342497, "k_name": "팀 르두", "e_name": "Tim LeDoux", "role": "시각효과"} 29 | {"id": 9409, "k_name": "메리 조프레즈", "e_name": "Mary Zophres", "role": "의상"} 30 | {"id": 213709, "k_name": "톰 크로스", "e_name": "Tom Cross", "role": "편집"} 31 | {"id": 22237, "k_name": "데보라 아퀼라", "e_name": "Deborah Aquila", "role": "배역"} 32 | {"id": 26608, "k_name": "메리 트리샤 우드", "e_name": "Mary Tricia Wood", "role": "배역"} 33 | -------------------------------------------------------------------------------- /naver_movie_scraper/detail.py: -------------------------------------------------------------------------------- 1 | from .utils import get_soup 2 | 3 | 4 | detail_url_form = 'http://movie.naver.com/movie/bi/mi/detail.nhn?code={}' # idx 5 | 6 | def scrap_casting(idx): 7 | url = detail_url_form.format(idx) 8 | soup = get_soup(url) 9 | 10 | directors = [parse_director(div) for div in soup.select('div[class=dir_product]')] 11 | directors = [d for d in directors] 12 | actors = [parse_actor(actor, i+1) for i, actor in enumerate(soup.select('div[class=p_info]'))] 13 | actors = [actor for actor in actors if actor] 14 | staffs = [parse_staff(staff) for staff in soup.select('table[class=staff_lst] span')] 15 | staffs = [staff for staff in staffs if staff] 16 | 17 | return { 18 | 'directors': directors, 19 | 'actors': actors, 20 | 'staffs': staffs 21 | } 22 | 23 | def parse_director(director): 24 | try: 25 | director_id = director.select('a') 26 | if director_id: 27 | director_id = int(director_id[0].attrs.get('href','=').split('=')[1]) 28 | k_name = director.select('a[class=k_name]') 29 | k_name = k_name[0].text.strip() if k_name else '' 30 | e_name = director.select('em[class=e_name]') 31 | e_name = e_name[0].text.strip() if e_name else '' 32 | return {'id': director_id, 'k_name': k_name, 'e_name': e_name} 33 | except Exception as e: 34 | return {} 35 | 36 | def parse_actor(actor, i): 37 | try: 38 | actor_id = actor.select('a') 39 | if actor_id: 40 | actor_id = int(actor_id[0].attrs.get('href','=').split('=')[1]) 41 | k_name = actor.select('a[class=k_name]') 42 | k_name = k_name[0].text.strip() if k_name else '' 43 | e_name = actor.select('em[class=e_name]') 44 | e_name = e_name[0].text.strip() if e_name else '' 45 | part = actor.select('em[class=p_part]') 46 | part = part[0].text.strip() if part else '' 47 | role = actor.select('p[class=pe_cmt]') 48 | role = role[0].text.replace('[\n역]','').strip() if role else '' 49 | return {'id': actor_id, 'k_name': k_name, 'e_name': e_name, 'part': part, 'role': role, 'casting_order': i} 50 | except: 51 | return {} 52 | 53 | def parse_staff(staff): 54 | try: 55 | idx = int(staff.select('a')[0].attrs.get('href', '=').split('=')[1]) 56 | k_name = staff.select('a')[0].text 57 | e_name = '' 58 | role = '' 59 | for em in staff.select('em'): 60 | em = em.text 61 | if '(' in em: role = em.replace('(', '').replace(')','') 62 | else: e_name = em 63 | return {'id': idx, 'k_name': k_name, 'e_name': e_name, 'role': role} 64 | except: 65 | return {} -------------------------------------------------------------------------------- /naver_movie_scraper/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import requests 4 | import sys 5 | from bs4 import BeautifulSoup 6 | from pprint import pprint 7 | 8 | 9 | normalize_pattern = re.compile('[\r\n\t]') 10 | doublespcae_pattern = re.compile('[\s]+') 11 | 12 | 13 | def get_soup(url, headers=None, allow_redirects=True): 14 | """ 15 | Argument 16 | -------- 17 | url : str 18 | Web page url 19 | headers : dict or None 20 | Headers 21 | allow_redirects : Boolean 22 | 23 | Returns 24 | ------- 25 | bs4.Beautifulsoup format HTML page 26 | """ 27 | 28 | try: 29 | r = requests.get(url, headers=headers, allow_redirects=allow_redirects).text 30 | return BeautifulSoup(r, 'lxml') 31 | except Exception as e: 32 | exc_type, exc_value, exc_traceback = sys.exc_info() 33 | traceback_details = { 34 | 'filename': exc_traceback.tb_frame.f_code.co_filename, 35 | 'lineno' : exc_traceback.tb_lineno, 36 | 'name' : exc_traceback.tb_frame.f_code.co_name, 37 | 'type' : exc_type.__name__, 38 | 'message' : str(e) 39 | } 40 | pprint(traceback_details) 41 | return '' 42 | 43 | def text_normalize(s): 44 | """ 45 | Arguments 46 | --------- 47 | s : str 48 | Text to normalize 49 | 50 | Returns 51 | ------- 52 | normalized text. Remove \\n, \\r, \\t, double space 53 | """ 54 | 55 | s = s.replace(' ', ' ') 56 | s = s.replace('\xa0', ' ') 57 | s = normalize_pattern.sub(' ', s) 58 | s = doublespcae_pattern.sub(' ', s) 59 | return s.strip() 60 | 61 | 62 | def save_list_of_dict(obj, path, mode='w'): 63 | """ 64 | Arguments 65 | --------- 66 | obj : list of dict 67 | Object to store 68 | path : str 69 | File path 70 | mode : str 71 | File open mode 72 | """ 73 | 74 | with open(path, mode, encoding='utf-8') as f: 75 | for d in obj: 76 | f.write('{}\n'.format(json.dumps(d, ensure_ascii=False))) 77 | 78 | def load_list_of_dict(path): 79 | """ 80 | Arguments 81 | --------- 82 | path : str 83 | File path 84 | 85 | Returns 86 | ------- 87 | obj : list of dict 88 | Object to store 89 | """ 90 | 91 | with open(path, encoding='utf-8') as f: 92 | objs = [json.loads(obj.strip()) for obj in f] 93 | return objs 94 | 95 | def save_json(obj, path): 96 | """ 97 | Arguments 98 | --------- 99 | obj : list of dict 100 | Object to store 101 | path : str 102 | File path 103 | """ 104 | 105 | with open(path, 'w', encoding='utf-8') as f: 106 | json.dump(obj, f, indent=2, ensure_ascii=False) -------------------------------------------------------------------------------- /naver_movie_scraper/comments.py: -------------------------------------------------------------------------------- 1 | import math 2 | import re 3 | import time 4 | from tqdm import trange 5 | from .utils import get_soup 6 | 7 | 8 | comments_url_form = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code={}&order=newest&page={}&onlySpoilerPointYn=N' # idx, type, page 9 | 10 | def scrap_comments(idx, limit=-1, sleep=0.05, last_time=None, i_movie=-1, n_total_movies=-1): 11 | max_page = num_of_comment_pages(idx) 12 | if limit > 0: 13 | max_page = min(limit, max_page) 14 | if max_page <= 0: 15 | return [] 16 | 17 | if n_total_movies < 0 or i_movie < 0: 18 | desc = f'Scrap comments {idx}' 19 | else: 20 | desc = f'Scrap comments {idx} ({i_movie}/{n_total_movies})' 21 | 22 | comments = [] 23 | for p in trange(1, max_page + 1, desc=desc): 24 | url = comments_url_form.format(idx, p) 25 | comments_p, stop = parse_a_page(get_soup(url), last_time) 26 | comments += comments_p 27 | if stop: 28 | print(f'\r movie {idx}. stop scrap comments. found existing comments {p} / {max_page}') 29 | break 30 | return comments[::-1] 31 | 32 | def parse_a_page(soup, last_time=None): 33 | comments = [] 34 | stop = False 35 | for row in soup.select('div[class=score_result] li'): 36 | try: 37 | score = int(row.select('div[class=star_score] em')[0].text.strip()) 38 | text = row.select('div[class=score_reple] p')[0].text.strip() 39 | # detach '관람객' icon 40 | if text[:4] == '관람객\n': 41 | text = text[4:].strip() 42 | # detach '스포일러' icon 43 | if text[:25] == '스포일러가 포함된 감상평입니다. 감상평 보기\n': 44 | text = text[25:].strip() 45 | idx = row.select('a[onclick^=javascript]')[0].attrs.get('onclick', '').split('(')[1].split(',')[0] 46 | masked_user = row.select('div[class=score_reple] em')[0].text.strip() 47 | written_at = re.search(r"\d+\.\d+\.\d+ \d+:\d+", row.text).group() 48 | agree = int(row.select('strong[class^="sympathy"]')[0].text.strip()) 49 | disagree = int(row.select('strong[class^="notSympathy"]')[0].text.strip()) 50 | if (last_time is not None) and (written_at <= last_time): 51 | stop = True 52 | break 53 | comments.append( 54 | {'score': score, 55 | 'text': text, 56 | 'idx': idx, 57 | 'user': masked_user, 58 | 'written_at': written_at, 59 | 'agree': agree, 60 | 'disagree': disagree 61 | }) 62 | except Exception as e: 63 | continue 64 | return comments, stop 65 | 66 | def num_of_comment_pages(idx): 67 | url = comments_url_form.format(idx, 1) 68 | soup = get_soup(url) 69 | 70 | try: 71 | num_comments = int(soup.select('div[class="score_total"] em')[-1].text.replace(',','')) 72 | return math.ceil(num_comments / 5) 73 | except Exception as e: 74 | return -1 75 | -------------------------------------------------------------------------------- /output/bestscripts/10100: -------------------------------------------------------------------------------- 1 | {"text": "You! You're still dangerous. but You can be my wing man any time.", "chracter": "Lt. 아이스맨 발 킬머", "description": "넌 아직 위험해. 하지만 언제든지 내 윙맨이 될수있어.", "num_agree": 20} 2 | {"text": "내가 당신에게 빠졌다는걸 타인에게 알리고 싶지 않아.", "chracter": "찰리 켈리 맥길리스", "description": "", "num_agree": 15} 3 | {"text": "최고중의 최고가 되려면 실수하고도 계속 해야해요.", "chracter": "찰리켈리 맥길리스", "description": "구즈가 죽어서 조종사를 그만두려는 매버릭에게 찰리가 공항에서", "num_agree": 10} 4 | {"text": "I feel...I feel the need....the need for speed!!!", "chracter": "Lt. 매버릭 톰 크루즈", "description": "", "num_agree": 10} 5 | {"text": "\"나의 윙이 되어줘.\"", "chracter": "Lt. 매버릭 톰 크루즈", "description": "영화의 마지막 부분, 평소 라이벌의식을 갖고 있던 두 남자, 매버릭과 아이스맨... 둘의 포옹, 남자의 우정, 남자의 로망, 남자의 감성!", "num_agree": 10} 6 | {"text": "Take me bad or you'll lose me forever", "chracter": "찰리켈리 맥길리스", "description": "지금 나를 침대로 데려가지않으면 영원히 나를 놓칠 수도 있어", "num_agree": 9} 7 | {"text": "암더 탑건 drop another hot one 덤빌수있다면 show me what, what you got 에픽하이", "chracter": "Lt. 매버릭톰 크루즈", "description": "컄캬", "num_agree": 6} 8 | {"text": "I need..... / what? / for speed", "chracter": "Lt. 매버릭톰 크루즈", "description": "", "num_agree": 6} 9 | {"text": "첫 대원이 죽으면 죽고 싶다가도 누군가 또 죽으면 숫자를 세게 되지.", "chracter": "바이퍼톰 스커릿", "description": "(베트남에서 우리 대대는 18대 중에 8대와 10명을 잃었다.)", "num_agree": 6} 10 | {"text": "훌륭한 조종사는 늘 평가를 받고 그것을 교훈으로 삼지..", "chracter": "바이퍼 톰 스케리트", "description": "", "num_agree": 6} 11 | {"text": "미 국방성은 댁의 아들이 멍청해서 전사했단 걸 알림을 유감으로 생각합니다.", "chracter": "Lt. 구즈 안소니 에드워즈", "description": "", "num_agree": 5} 12 | {"text": "I feel the need... the need for speed!", "chracter": "매버릭톰 크루즈", "description": "", "num_agree": 4} 13 | {"text": "첫번째는 실패했죠, (두번째는요?) 잘될거같아요.", "chracter": "Lt. 매버릭톰 크루즈", "description": "", "num_agree": 4} 14 | {"text": "한 가지 잊지 말아야 할 것은 결국엔 우리 모두 한 팀이라는 것이다", "chracter": "바이퍼 톰 스커릿", "description": "강의를 마치며...", "num_agree": 4} 15 | {"text": "희망은 좋은거죠 가장 소중한 것이죠.", "chracter": "메를린 팀 로빈스", "description": "", "num_agree": 4} 16 | {"text": "우린한계를 극복해야해. 그게 우리의 임무지. .", "chracter": "바이퍼톰 스커릿", "description": "", "num_agree": 3} 17 | {"text": "Bull shit!!!", "chracter": "Lt. 아이스맨발 킬머", "description": "헛소리!!", "num_agree": 3} 18 | {"text": "I need, need for speed.", "chracter": "Lt. 매버릭톰 크루즈", "description": "", "num_agree": 3} 19 | {"text": "Finally, We're all one.", "chracter": "바이퍼톰 스커릿", "description": "결국 우리는 모두 하나라는 것이다.", "num_agree": 3} 20 | {"text": "I got a good luck fire!!!", "chracter": "Lt. 매버릭톰 크루즈", "description": "난 운이 좋지 발사!!", "num_agree": 2} 21 | {"text": "Am I your student ? you are my only memery from first.", "chracter": "Lt. 매버릭톰 크루즈", "description": "학생은 맞는 사람입니까 ? 이제부터 때리고 싶은 사람은 군인 대신 선생님을 하면 되겠군요. 당장 다 때려치고 싶은 건 당신이 아니라 나부터일 겁니다.", "num_agree": 2} 22 | {"text": "웃기지 마. 네가 내 윙맨이 될 수 있지.", "chracter": "Lt. 매버릭 톰 크루즈", "description": "", "num_agree": 2} 23 | {"text": "넌 아직도 위험해. 근데 내 윙맨은 될 수 있어.", "chracter": "Lt. 아이스맨 발 킬머", "description": "", "num_agree": 2} 24 | {"text": "난 월남전 때 전투기8대와 열 명의 동료를 잃었다. 처음에는 따라 죽고 싶지. 하지만 극복해야 한다. 사실로 받아들이게. 그를 떠나보내게.", "chracter": "바이퍼 톰 스커릿", "description": "", "num_agree": 2} 25 | {"text": "계속 도망다닐 순 있겠지만 숨을 곳은 없다 애송아", "chracter": "제스터마이클 아이언사이드", "description": "", "num_agree": 1} 26 | {"text": "Banking left (좌측으로 뱅킹선회해!)", "chracter": "매버릭톰 크루즈", "description": "Lt. 매버릭", "num_agree": 1} 27 | {"text": "Take me.. goose...", "chracter": "매버릭톰 크루즈", "description": "페르시아만의 파병지에서 비상대기중 아이스맨의 교전 음성을 들으며 구스를 생각하는 매버릭", "num_agree": 1} 28 | {"text": "자신을 봐요, 당신은 마하 2로 날지 않으면 못 살아요.", "chracter": "찰리 켈리 맥길리스", "description": "", "num_agree": 1} 29 | {"text": "그는 당신과 비행하는 걸 좋아했어요. 근데 날아갔어요... 당신 없이 그러고 싶지는 않았을 거예요.", "chracter": "캐롤 멕 라이언", "description": "", "num_agree": 1} 30 | {"text": "넌 모두의 문제아야. 네가 비행을 할 때마다 안전하지 못하기 때문이야. 위험해서 난 네가 싫어.", "chracter": "Lt. 아이스맨 발 킬머", "description": "", "num_agree": 1} 31 | -------------------------------------------------------------------------------- /output/bestscripts/134963: -------------------------------------------------------------------------------- 1 | {"text": "도서관 앞이라고 했잖아", "chracter": "세바스찬라이언 고슬링", "description": "기습심쿵♡.♡", "num_agree": 666} 2 | {"text": "빠아앙아앙아아아앙~~~", "chracter": "세바스찬라이언 고슬링", "description": "미아 기다릴때 경적소리 ㅎㅎ", "num_agree": 471} 3 | {"text": "그냥 흘러가는 대로 가보자.", "chracter": "세바스찬라이언 고슬링", "description": "우리 지금 어디 있는 거야?", "num_agree": 470} 4 | {"text": "웰컴 투 셉스...", "chracter": "세바스찬라이언 고슬링", "description": "", "num_agree": 440} 5 | {"text": "사람들은 다른 사람들의 열정에 끌리게 되어있어. 자신이 잊은걸 상기시켜 주니까.", "chracter": "미아엠마 스톤", "description": "현실과 타협한 채 밴드에 들어가 원하지 않는 음악을 하는 세바스찬을 설득하며", "num_agree": 410} 6 | {"text": "재능은없고 하려고하는 열정만 가득한사람들 있잖아 나도 그런 사람중 하나였나봐", "chracter": "미아엠마 스톤", "description": "이때 진짜 펑펑울었음..", "num_agree": 373} 7 | {"text": "(마지막 미아와 세바스찬의 서로를 바라보는 눈빛)", "chracter": "세바스찬라이언 고슬링", "description": "여운이 너무 진하게남아서..ㅠㅠ", "num_agree": 334} 8 | {"text": "우리는 어디쯤 있는 거지?", "chracter": "미아엠마 스톤", "description": "", "num_agree": 283} 9 | {"text": "♬City of Stars~♬", "chracter": "세바스찬라이언 고슬링", "description": "엄청난 영화...음원사이트에 있길래 다운 받았는데 들을 때마다 노래 도입부 휘파람 소리 진짜 설렌다.", "num_agree": 227} 10 | {"text": "미아: 사람들이 좋아할까? 세바스찬: 그깟 사람들! (Fuck 'em!)", "chracter": "세바스찬라이언 고슬링", "description": "자신의 무대를 걱정하는 미아와 그녀를 응원하는 세바스찬", "num_agree": 219} 11 | {"text": "차 키를 턱에다 대면 머리가 안테나 역할을 해서 더 빨리 찾을수 있을걸요 암에 걸릴 확률은 늘겟지만", "chracter": "세바스찬라이언 고슬링", "description": "그리고 실제로 됨", "num_agree": 204} 12 | {"text": "난 당신을 계속 사랑할거야", "chracter": "미아엠마 스톤", "description": "미아가 세바스찬을 바라보며 이야기함... 이 대사 다음에 세바스찬이 \" 난 당신을 항상 사랑할거야 \" 라고 해서 더 여운이 남음...", "num_agree": 186} 13 | {"text": "치킨만은 절대 포기 못해", "chracter": "세바스찬라이언 고슬링", "description": "어느나라든 치킨만은 포기못하지..", "num_agree": 146} 14 | {"text": "계산은 됐어요/ 아뇨 할게요.", "chracter": "미아엠마 스톤", "description": "", "num_agree": 125} 15 | {"text": "난 프리재즈 싫어해", "chracter": "보스J.K. 시몬스", "description": "전직 재즈밴드 지휘자", "num_agree": 122} 16 | {"text": "WHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "chracter": "세바스찬라이언 고슬링", "description": "미아가 현실도피중에 모든것을 포기하려고 할 때", "num_agree": 108} 17 | {"text": "자넨 해고야 / (크리스마스잖아요 ㅠㅠ) / 알아 새해엔 잘해보게", "chracter": "보스J.K. 시몬스", "description": "위플래쉬 앤드류에게 당하고 세바스찬에게 화풀이 ㅠㅠ", "num_agree": 108} 18 | {"text": "꿈을 꾸는 그댈 위하여, 비록 바보같다 하여도. 상처입은 가슴을 위하여, 우리의 시행착오를 위하여", "chracter": "미아엠마 스톤", "description": "함께가 아닌 각자의 꿈을 이루는 시작, 미아의 마지막 오디션 노래", "num_agree": 100} 19 | {"text": "were you rushing or dragging?", "chracter": "보스J.K. 시몬스", "description": "싸대기 후리기 전에", "num_agree": 86} 20 | {"text": "그냥 이렇게 흘러가는대로 해보자", "chracter": "세바스찬라이언 고슬링", "description": "별거아닌대사지만 굉장히 와닿았다. 가끔 어떻게도 방법이없을땐 흐름에 맡기는것도 한 방법이니깐.", "num_agree": 77} 21 | {"text": "....하나 둘, 하나 둘 셋 넷", "chracter": "세바스찬라이언 고슬링", "description": "미아를 마지막으로 보낸뒤", "num_agree": 76} 22 | {"text": "커피 사왔어", "chracter": "미아엠마 스톤", "description": "세바스찬 놀램 ㅋㅋ", "num_agree": 76} 23 | {"text": "전통만 고집하면 어떻게 혁명가가 돼?", "chracter": "키이스존 레전드", "description": "전통 재즈를 살리려는 혁명가 세바스찬을 설득하는 키이스의 말", "num_agree": 72} 24 | {"text": "빵!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", "chracter": "세바스찬라이언 고슬링", "description": "", "num_agree": 69} 25 | {"text": "What a waste of lovely night!", "chracter": "세바스찬라이언 고슬링", "description": "밤 공원 언덕에서 아름다운 할리우드 야경을 배경으로 서로 호감을 가진 것에 대한 우회적인 표현", "num_agree": 69} 26 | {"text": "꿈꾸는 바보들을 위하여", "chracter": "미아엠마 스톤", "description": "", "num_agree": 63} 27 | {"text": "울이에요", "chracter": "세바스찬라이언 고슬링", "description": "미아가 춤추며 폴리수트라고 말했을때", "num_agree": 53} 28 | {"text": "t(-_-", "chracter": "미아엠마 스톤", "description": "첫장면 차에서 세바스찬에게", "num_agree": 47} 29 | {"text": "세상에서 제일 쓸모없고 가치없는 말이 '그만하면 잘했어(good job)'야.", "chracter": "재즈클럽 사장J.K. 시몬스", "description": "라이언고슬링이 피아노 연주하다가 삑사리가 났을 때, 싸다구를 후리며", "num_agree": 45} 30 | {"text": "집 앞에 도서관 있댔잖아", "chracter": "세바스찬라이언 고슬링", "description": "여긴 어떻게 알고 왔냐는 말에", "num_agree": 44} 31 | -------------------------------------------------------------------------------- /naver_movie_scraper/basic.py: -------------------------------------------------------------------------------- 1 | import re 2 | from bs4 import BeautifulSoup 3 | from .utils import get_soup 4 | from .utils import text_normalize 5 | 6 | 7 | basic_url_form = 'http://movie.naver.com/movie/bi/mi/basic.nhn?code={}' # idx 8 | 9 | def scrap_basic(idx): 10 | url = basic_url_form.format(idx) 11 | soup = get_soup(url) 12 | infomation = { 13 | 'movie_idx': idx, 14 | 'expert_score': meta_score(soup)[0], 15 | 'netizen_score': meta_score(soup)[1], 16 | 'title': title(soup), 17 | 'e_title': e_title(soup), 18 | 'genres': genres(soup), 19 | 'countries': countries(soup), 20 | 'running_time': running_time(soup), 21 | 'open_date': open_date(soup), 22 | 'grade': grade(soup), 23 | 'story': story(soup), 24 | 'making_note': making_note(soup), 25 | 'box_office': box_office(soup) 26 | } 27 | return infomation 28 | 29 | def meta_score(soup): 30 | try: 31 | main_score = soup.select('div[class=main_score]')[0] 32 | expert_score = main_score.select('div[class=spc_score_area] div[class=star_score]')[0].text.replace('\n','') 33 | netizen_score = main_score.select('div[class=star_score] span[class=st_off]')[0].text.replace('관람객 평점 ', '').replace('점', '') 34 | return float(expert_score), float(netizen_score) 35 | except: 36 | return -1, -1 37 | 38 | def title(soup): 39 | a = soup.select('div[class=mv_info] h3[class=h_movie] a') 40 | if not a: 41 | return '' 42 | return text_normalize(a[0].text) 43 | 44 | def e_title(soup): 45 | strong = soup.select('div[class=mv_info] strong[class=h_movie2]') 46 | if not strong: 47 | return '' 48 | return text_normalize(strong[0].text) 49 | 50 | def genres(soup): 51 | genres = soup.select('a[href^="/movie/sdb/browsing/bmovie.nhn?genre="]') 52 | return list({genre.text for genre in genres}) 53 | 54 | def countries(soup): 55 | countries = soup.select('a[href^="/movie/sdb/browsing/bmovie.nhn?nation="]') 56 | return list({country.text for country in countries}) 57 | 58 | def running_time(soup): 59 | dl = soup.select('dl[class=info_spec]') 60 | try: 61 | return int(re.search(r"\d+분", dl[0].text).group()[:-1]) 62 | except: 63 | return 0 64 | 65 | def open_date(soup): 66 | links = soup.select('dl[class=info_spec] a') 67 | dates = [a.attrs['href'].split('bmovie.nhn?open=')[-1] 68 | for a in links if 'bmovie.nhn?open=' in a.attrs.get('href', '')] 69 | dates = ['{}-{}-{}'.format(d[:4], d[4:6], d[6:]) for d in dates if len(d) == 8] 70 | return dates 71 | 72 | def grade(soup): 73 | a = soup.select('a[href^="/movie/sdb/browsing/bmovie.nhn?grade"]') 74 | if not a: 75 | return '' 76 | return text_normalize(a[0].text) 77 | 78 | def story(soup): 79 | try: 80 | story_soup = BeautifulSoup( 81 | str(soup.select("div[class=story_area]")[0]).replace('
', '\n').replace('\xa0', '\n'), 82 | 'lxml') 83 | sents = story_soup.text.split('\n') 84 | sents = [text_normalize(s) for s in sents if s] 85 | sents = [s for s in sents if s != '줄거리'] 86 | return '\n'.join(sents) 87 | except: 88 | return '' 89 | 90 | def making_note(soup): 91 | try: 92 | note_soup = BeautifulSoup( 93 | str(soup.select('div[class=making_note]')[0]).replace('
', '\n').replace('\xa0', '\n'), 94 | 'lxml') 95 | sents = note_soup.text.split('\n') 96 | sents = [text_normalize(s) for s in sents if s] 97 | sents = [s for s in sents if not s == '펼쳐보기'] 98 | return '\n'.join(sents) 99 | except: 100 | return '' 101 | 102 | def box_office(soup): 103 | p = soup.select('p[class=count]') 104 | if not p: 105 | return -1 106 | return text_normalize(p[0].text) -------------------------------------------------------------------------------- /output/bestscripts/10001: -------------------------------------------------------------------------------- 1 | {"text": "인생은 네가본 영화와는 달라.. 인생이..훨씬 힘들지", "chracter": "알프레도 필립 느와레", "description": "20대 중반의 나에게 지금 너무나도 와닿는 말..", "num_agree": 155} 2 | {"text": "토토, 이것은 분명 네 것이다. 하지만, 지금은 내가 보관하고 있다가 나중에 때가 되면 돌려주마", "chracter": "알프레도 필립 느와레", "description": "잘라낸 키스장면 필름을 달라고 조르던 어린 토토에게 알프레도가 했던말...", "num_agree": 132} 3 | {"text": "토토. 네가 영사실 일을 사랑했던 것처럼 무슨일을 하든 네 일을 사랑하렴", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 106} 4 | {"text": "몸이 무거우면 발자국도 깊은법.. 사랑에 빠지면 괴로울 뿐이야.. 막다른 골목이기 때문이지......", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 65} 5 | {"text": "99일째 되는날, 병사는 의자에서 일어났단다. 그리고 그대로 가버렸지.", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 61} 6 | {"text": "이 지긋지긋한 여름은 언제끝나지? 영화라면 벌써끝났을텐데.. 따분한 여름은 금방사라지고 곧바로 시원하게 비가내리는 장면으로!! 정말 완벽할텐데말이야!..", "chracter": "청년 살바토레 마코 레오나디", "description": "", "num_agree": 48} 7 | {"text": "오늘 거짓말 하고 빠져 나오느라 얼마나 힘들었는지 몰라..", "chracter": "젊은 엘레나아그네즈 나노", "description": "빗속에서 엘레나의 기습 키스신... 언제봐도 명장면이고 볼때마다 전율이 흐른다. 이 키스씬을 능가하는 세계영화는 진짜로 없을거같다..", "num_agree": 42} 8 | {"text": "내 비록 시력은 잃었지만 전보다 훨신 많은것을 본단다..", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 41} 9 | {"text": "극장 사장 만세!!!", "chracter": "소년 살바토레 살바토레 카스치오", "description": "극장에서 처음으로 키스신이 나올때...", "num_agree": 36} 10 | {"text": "영화는 현실이 아니다.. 현실은 영화보다 훨신 혹독하고 잔인하다.. 그래서 인생을 우습게 보아서는 안되는것이다..", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 34} 11 | {"text": "돌아와선 안 돼. 깡그리 잊어버려야해. 편지도 쓰지 마. 향수에 빠져선 안 돼. 잊어버려. 만일 못 참고 돌아오면 널 다신 만나지 않겠어. 알겠지?", "chracter": "알프레도 필립 느와레", "description": "토토를 보내며...", "num_agree": 30} 12 | {"text": "알프레~도", "chracter": "소년 살바토레 살바토레 카스치오", "description": "그 꼬마의 특유의 억양.. 너무 귀엽다 ㅎ", "num_agree": 23} 13 | {"text": "무슨일을 하든 자신의 일을 사랑하렴 네가 천국영사실을 사랑했듯이..\"니가 작은악마일때처럼말이야...\"", "chracter": "알프레도 필립 느와레", "description": "니가작은악마일때처럼말이야.... 이부분이 좋았었음^^", "num_agree": 13} 14 | {"text": "시간이 아무리흘러도 추억은 지워지지않아요", "chracter": "중년 엘레나브리지트 포시", "description": "", "num_agree": 12} 15 | {"text": "너도... 언젠가는 알게 될 거야. 말을 하는 것과 하지 않는 것은 별 차이가 없어.", "chracter": "알프레도필립 느와레", "description": "군대에서 제대한 토토에게 처음 건네는 말...", "num_agree": 10} 16 | {"text": "아니... 누구의 대사도 아니야. 내 대사야..", "chracter": "알프레도필립 느와레", "description": "토토에게 고향을 떠나라고 하자 토토가 이번에는 어느 배우의 대사냐고 물을 때", "num_agree": 9} 17 | {"text": "꼭 너의 상대역이 되고 말꺼야", "chracter": "청년 살바토레마코 레오나디", "description": "벽에 비친 엘레나를 보면서 한 말. 정말 멋진 말이었다... 꼭 너의 상대역이 되어주겠다는 말", "num_agree": 9} 18 | {"text": "이게 가장 좋은 헤피엔딩이야", "chracter": "중년 엘레나 브리지트 포시", "description": "살바토레가 엘레나와 꿈같은 재회를 한 후... 여지없는 아쉬움의 현실이... 느껴지는 말...", "num_agree": 9} 19 | {"text": "광장은 내꺼야", "chracter": "중년 살바토레 자끄 페렝", "description": "", "num_agree": 8} 20 | {"text": "Each of us has a star to follow.. 우리 모두 각자 따라가야할 별이 있기 마련이지..", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 8} 21 | {"text": "친구는 겉모습으로 사귀고 적은 머리로 사귀어야한다..", "chracter": "알프레도 필립 느와레", "description": "", "num_agree": 8} 22 | {"text": "이 마을엔 너를 위해 마련된 게 아무것도 없으니 마을을 떠나서 다시는 돌아오지 마라", "chracter": "알프레도필립 느와레", "description": "", "num_agree": 7} 23 | {"text": "과거는 꿈일 뿐이야", "chracter": "중년 엘레나 브리지트 포시", "description": "", "num_agree": 6} 24 | {"text": "돌아와선 안 돼 깡그리 잊어버려야해 편지도 쓰지 마, 향수에 빠져선 안 돼 잊어버려 만일 못 참고 돌아오면 널 다신 만나지 않겠어 알겠지? 무슨 일을 하든 자신의 일을 사랑하렴 네가 어렸을 때 영사실을 사랑했듯이", "chracter": "알프레도필립 느와레", "description": "", "num_agree": 5} 25 | {"text": "하느님은 세계를 며칠만에 만들었다지. 하지만 며칠이 더 걸리더라도 더 정성스럽게 만들었으면 좋았을텐데.", "chracter": "알프레도필립 느와레", "description": "이거맞나요? 어린 토토에게하는말... ㅠㅠㅠ", "num_agree": 5} 26 | {"text": "얘기 하나 해 드릴게요 대장이 병사한테 물었어요 여기에 풍차가 있었던 거 기억나나? 네, 기억납니다. 풍차가 사라졌는데 바람은 여전히 부는군", "chracter": "청년 살바토레마코 레오나디", "description": "", "num_agree": 4} 27 | {"text": "돌아와선 안돼. 깡그리 잊어버려야 해. 편지도 쓰지 마. 향수에 빠져선 안 돼. 잊어버려. 만일 못 참고 돌아오면 널 다신 만나지 않겠어. 알겠지?", "chracter": "알프레도필립 느와레", "description": "", "num_agree": 4} 28 | {"text": "떠나. 로마로 돌아가. 넌 아직 젊고 앞날이 창창해! 난 늙었어. 너하고도 말하고 싶지 않아. 네 소문을 듣고 싶어.", "chracter": "알프레도필립 느와레", "description": "", "num_agree": 4} 29 | {"text": "인연은 운명이 정하는거야..각자의 길이 따로있는법이지..", "chracter": "알프레도필립 느와레", "description": "토토가 제대후에 알프레도와 바다에서 걸으면서 그녀가 행방불명되어 고뇌에차있으며 대사..", "num_agree": 4} 30 | {"text": "99일째 되는날.병사는..... 어렸을때는 이해하지 못했던 이야기였는데.. 너무 깊이 와닿네요.", "chracter": "알프레도필립 느와레", "description": "", "num_agree": 3} 31 | -------------------------------------------------------------------------------- /output/comments/134963: -------------------------------------------------------------------------------- 1 | {"score": 8, "text": "뮤지컬 영화를 별로 좋아하지 않는 편인데 엔딩이 진짜 다했다. 너무 아프면서 또 설레이기도 한 느낌. 피아노가 계속 떠오를 것 같다 ..", "user": "16488639", "written_at": "2019.12.15 18:17", "agree": 0, "disagree": 0} 2 | {"score": 10, "text": "", "user": "16486063", "written_at": "2019.12.14 20:57", "agree": 0, "disagree": 0} 3 | {"score": 10, "text": "처음에 나온 ost노래의 뜻이 영화한편의 이야기를 설명한것같아 좋았다", "user": "16485726", "written_at": "2019.12.14 18:21", "agree": 0, "disagree": 0} 4 | {"score": 10, "text": "", "user": "16485652", "written_at": "2019.12.14 17:44", "agree": 0, "disagree": 0} 5 | {"score": 9, "text": "감독 나쁘네 관객 하나하나에 비수를 꽂았어", "user": "16483867", "written_at": "2019.12.13 22:40", "agree": 0, "disagree": 0} 6 | {"score": 10, "text": "아름다운 추억은 시간이 흘러도 늘 제자리에", "user": "16473852", "written_at": "2019.12.09 13:19", "agree": 0, "disagree": 0} 7 | {"score": 8, "text": "연애를 함축해놓은 얘기. 연애의 초기 중기 말기를 건너 마지막 10분을 위한 얘기. 연애가 그렇듯 설레기도 지루하기도 슬프기도 그런 느낌.", "user": "16472600", "written_at": "2019.12.08 23:39", "agree": 0, "disagree": 0} 8 | {"score": 10, "text": "", "user": "16467239", "written_at": "2019.12.07 13:47", "agree": 0, "disagree": 0} 9 | {"score": 7, "text": "마지막 7분을 위한 2시간의 워밍업", "user": "16466918", "written_at": "2019.12.07 11:06", "agree": 0, "disagree": 0} 10 | {"score": 10, "text": "", "user": "16466889", "written_at": "2019.12.07 10:54", "agree": 0, "disagree": 0} 11 | {"score": 8, "text": "왜 다른남자랑 결혼한거임?", "user": "16466887", "written_at": "2019.12.07 10:54", "agree": 1, "disagree": 1} 12 | {"score": 10, "text": "마지막장면 미아를 바라보는 세바스챤의 슬픈미소가 잊혀지질않는다ㅠ", "user": "16466888", "written_at": "2019.12.07 10:54", "agree": 0, "disagree": 0} 13 | {"score": 10, "text": "", "user": "16466858", "written_at": "2019.12.07 10:40", "agree": 0, "disagree": 0} 14 | {"score": 1, "text": "나만 별로였나..여주가 바람 피우고 남주만난것도 여주가 쓰레기 같았고남주 버리고 외국에서 결혼한 것도 다시 여주가 쓰레기임을 알게 해준것같은데..보다가 지루해서 잘 뻔도 했지만 다들 재미있데서 존버 탓는데 결굴 바람...", "user": "16466429", "written_at": "2019.12.07 02:45", "agree": 2, "disagree": 2} 15 | {"score": 10, "text": "위대한쇼맨,비긴 어게인 등 뮤지컬영화를 사랑하는 사람이라면 꼭 봐야함!", "user": "16465106", "written_at": "2019.12.06 18:49", "agree": 0, "disagree": 0} 16 | {"score": 10, "text": "보면서 시간 가는 줄 몰랐네요!!", "user": "16460103", "written_at": "2019.12.04 22:02", "agree": 0, "disagree": 0} 17 | {"score": 10, "text": "우리는 살면서 매번 두가지의 갈래길에서 선택을 하게 된다 가지 않은길을 후회할때도 있다 그러나 우린 후회하면서도 선택하지만 그 선택의 결과는 우리의 몫이라는것....9", "user": "16459325", "written_at": "2019.12.04 16:56", "agree": 0, "disagree": 0} 18 | {"score": 10, "text": "그저 내 인생영화 탑 원...", "user": "16454304", "written_at": "2019.12.02 14:43", "agree": 1, "disagree": 0} 19 | {"score": 1, "text": "뮤지컬 영화는 저하고 좀 안맞는것 같네요 지루합니다... 뮤지컬 시러하는사람은 안보는걸 추천합니다", "user": "16450709", "written_at": "2019.12.01 02:45", "agree": 1, "disagree": 0} 20 | {"score": 7, "text": "¿#%#%#%#%?", "user": "16445812", "written_at": "2019.11.29 17:48", "agree": 0, "disagree": 0} 21 | {"score": 10, "text": "여자친구랑 보러가서 창피하게 질질짜면서 나왔어요", "user": "16442477", "written_at": "2019.11.28 13:33", "agree": 0, "disagree": 0} 22 | {"score": 10, "text": "열번도 넘게 봤고 앞으로도 계속 보게 될 인생영화", "user": "16441728", "written_at": "2019.11.28 06:41", "agree": 0, "disagree": 0} 23 | {"score": 10, "text": "", "user": "16438964", "written_at": "2019.11.27 12:56", "agree": 0, "disagree": 0} 24 | {"score": 10, "text": "어느 순간 라이언 고슬링에게 공감하고 이해하고 있는 자신을 발견하게 된다. 시간을 되돌렸다면 우린 행복했을까", "user": "16437341", "written_at": "2019.11.26 19:49", "agree": 0, "disagree": 0} 25 | {"score": 10, "text": "", "user": "16435982", "written_at": "2019.11.26 03:40", "agree": 0, "disagree": 0} 26 | {"score": 10, "text": "연인의 감정을 춤과 음악으로 이렇게 아름답게 잘 표현하다니.. 인생영화중 하나..", "user": "16433288", "written_at": "2019.11.25 10:04", "agree": 0, "disagree": 0} 27 | {"score": 6, "text": "", "user": "16432861", "written_at": "2019.11.25 03:02", "agree": 0, "disagree": 0} 28 | {"score": 10, "text": "", "user": "16432331", "written_at": "2019.11.25 00:09", "agree": 0, "disagree": 0} 29 | {"score": 5, "text": "이런 낭만적인 분위기의 영화를 항상 보고싶어했어서 완전 내스타일일거라 생각했는데 그냥...점개가 지루하고 영화 중간에 음악이 너무 자주나왔음...엄마랑 보다가 둘 다 재미 없어해서 3분의 2 보다 끔", "user": "16431988", "written_at": "2019.11.24 23:11", "agree": 1, "disagree": 0} 30 | {"score": 10, "text": "", "user": "16431309", "written_at": "2019.11.24 20:54", "agree": 0, "disagree": 0} 31 | -------------------------------------------------------------------------------- /output/comments/10001: -------------------------------------------------------------------------------- 1 | {"score": 10, "text": "정말 영화 역사상 가장 위대한 영화. 스토리가 자세히 기억도 안남에도 불구하고 ost만 들어도 눈물나는 영화는 몇 없을 것 같다.", "user": "16485553", "written_at": "2019.12.14 16:59", "agree": 0, "disagree": 0} 2 | {"score": 8, "text": "꿈보다 해몽이 좋은영화,ost가 열일한 영화,나이먹고 보니 오글거리는 영화 ㅋㅋ", "user": "16469873", "written_at": "2019.12.08 06:05", "agree": 0, "disagree": 2} 3 | {"score": 10, "text": "최고의 영화. 진정한 감동을 느꼈습니다.", "user": "16413509", "written_at": "2019.11.20 07:54", "agree": 0, "disagree": 0} 4 | {"score": 10, "text": "삶은 행복도 아니도 불행도 아니다. 삶은 그저 삶일 뿐이다.", "user": "16394098", "written_at": "2019.11.13 02:20", "agree": 3, "disagree": 0} 5 | {"score": 10, "text": "이 정도면 영화계의 문화재아닌가?", "user": "16391504", "written_at": "2019.11.11 23:55", "agree": 1, "disagree": 0} 6 | {"score": 10, "text": "마지막에 토토가 알프레도에게 받은 그 테이프를 보는 눈은 단순 영상을 보는 눈이 아닌 자신의 어린시절속의 시네마천국에 대한 추억을 떠올리는눈이라고 생각한다.말로 표현할 수 없는 감동을 주는 영화이다.", "user": "16380637", "written_at": "2019.11.08 22:10", "agree": 2, "disagree": 0} 7 | {"score": 10, "text": "", "user": "16342837", "written_at": "2019.10.29 22:21", "agree": 0, "disagree": 0} 8 | {"score": 10, "text": "안본사람은있어도,한번만본사람은 없을듯하다", "user": "16341293", "written_at": "2019.10.29 13:26", "agree": 2, "disagree": 0} 9 | {"score": 10, "text": "영화 top3안에 뽑을 정도로 감명깊게 본 영화. 애틋하고 여운은 정말 오래간다 ㅠ.. 토토..", "user": "16338698", "written_at": "2019.10.28 19:28", "agree": 2, "disagree": 0} 10 | {"score": 10, "text": "최애영화 몇번이라도 다시보고싶은영화", "user": "16321856", "written_at": "2019.10.25 21:47", "agree": 2, "disagree": 0} 11 | {"score": 10, "text": "내가 가장 사랑하는 영화", "user": "16279347", "written_at": "2019.10.17 01:13", "agree": 2, "disagree": 0} 12 | {"score": 9, "text": "정말 감동적이에요~@!", "user": "16278832", "written_at": "2019.10.16 21:22", "agree": 1, "disagree": 0} 13 | {"score": 10, "text": "최고의 영화~!!!", "user": "16276874", "written_at": "2019.10.15 18:55", "agree": 1, "disagree": 0} 14 | {"score": 10, "text": "인생에 대해 진지하게 생각해 볼 수 있게 해준 영화ost도 정말 좋았다.", "user": "16273450", "written_at": "2019.10.14 01:13", "agree": 0, "disagree": 0} 15 | {"score": 10, "text": "내인생 최고 아니 인류 최고의 영화", "user": "16253614", "written_at": "2019.10.08 03:02", "agree": 2, "disagree": 0} 16 | {"score": 10, "text": "마지막 씬에서 한동안 울었다. 여러가지 감정을 안고서. 20년 전에도 다시 보는 지금도.", "user": "16209964", "written_at": "2019.09.29 01:49", "agree": 3, "disagree": 1} 17 | {"score": 10, "text": "10점 밖에 줄 수 있는 게 없네", "user": "16207757", "written_at": "2019.09.28 16:54", "agree": 3, "disagree": 1} 18 | {"score": 10, "text": "소중한 사람과 함께 보고 싶은 영화", "user": "16168735", "written_at": "2019.09.16 01:48", "agree": 3, "disagree": 1} 19 | {"score": 10, "text": "이영화에 평가가 필요할까?", "user": "16134238", "written_at": "2019.09.10 02:46", "agree": 3, "disagree": 1} 20 | {"score": 10, "text": "인생영화. 이걸 이제서야보다니", "user": "16131513", "written_at": "2019.09.08 23:11", "agree": 3, "disagree": 1} 21 | {"score": 10, "text": "아름다운 향이 나는 영화", "user": "16036858", "written_at": "2019.08.13 09:53", "agree": 3, "disagree": 1} 22 | {"score": 10, "text": "병사는 99일째 밤이되어서야깨달았다.공주가 일개 병사인 자신과는 어울리지않는사람이라는사실을.자신을 따라 화려하고풍족한 성밖을벗어나면 언젠간 반드시 불행해하리라,그리고 진심으로그녀를사랑한다면 놓아주어야 한다는것을.그렇...", "user": "15988065", "written_at": "2019.08.04 01:06", "agree": 7, "disagree": 0} 23 | {"score": 10, "text": "나의 최고 인생영화 어리석은 선택과 후회 그리움이 밀려들 때마다 한번씩 보면서...지난날을 돌아보게 하는 영화", "user": "15978264", "written_at": "2019.08.02 07:29", "agree": 2, "disagree": 1} 24 | {"score": 10, "text": "감독판을 보든 극장판을 보든 엔딩씬은 언제나 가슴 뭉클한 아련함과 감동을 준다.", "user": "15924972", "written_at": "2019.07.22 04:36", "agree": 3, "disagree": 3} 25 | {"score": 8, "text": "글 남기려다 다른 글들 봤는데 감독판이 있나봐요? 감독판은 얼마나 더 대단할까... 감독판 꼭 보고싶네요.", "user": "15879316", "written_at": "2019.07.06 11:43", "agree": 16, "disagree": 2} 26 | {"score": 10, "text": "나도 저렇게 진정 사랑하는 사람을 위해 알프레도 처럼 아낌없는 조언을 해줄수있을까...", "user": "15876097", "written_at": "2019.07.05 04:10", "agree": 2, "disagree": 2} 27 | {"score": 10, "text": "솔직히 감독판보다 오리지널이 더 여운이 남고 좋은 듯....감독판에서 젊은 엘레나 보다가 늙은 엘레나 보니까 대실망....역시 첫사랑은 추억으로 남아있을 때가 아름답다", "user": "15872870", "written_at": "2019.07.04 00:16", "agree": 2, "disagree": 4} 28 | {"score": 10, "text": "포스터가 모든걸 말해주는", "user": "15862230", "written_at": "2019.06.30 23:19", "agree": 2, "disagree": 2} 29 | {"score": 10, "text": "감히 뭐라 평을..인생이 들어잇다", "user": "15861193", "written_at": "2019.06.30 18:03", "agree": 3, "disagree": 2} 30 | {"score": 10, "text": "1989년 문산극장!이제야 평을 올립니다알프레도! 그곳에서 편안하신가요?십대의 어느날...시네마천국을 만날수 있어 행복했습니다토토! 잘 지내고있니?", "user": "15827801", "written_at": "2019.06.21 08:53", "agree": 6, "disagree": 2} 31 | -------------------------------------------------------------------------------- /output/comments/10100: -------------------------------------------------------------------------------- 1 | {"score": 10, "text": "탐크루즈 레전드..보고 또 봐도..", "user": "16479450", "written_at": "2019.12.11 22:30", "agree": 0, "disagree": 0} 2 | {"score": 8, "text": "탑건의 톰크루즈는 진짜 미친듯이 잘생김 ㅓㅜㅑ", "user": "16479430", "written_at": "2019.12.11 22:24", "agree": 0, "disagree": 0} 3 | {"score": 9, "text": "냉전시대였으니 실제 미그기가 나오면 핵전쟁이 일었났을테니 넘어가고 최고로 거대한 전투기 f-14톰켓은 마크로스같은 에니메이션에 크게 영향을 준 기체로 그 디자인은 최라 생각함", "user": "16470309", "written_at": "2019.12.08 11:26", "agree": 1, "disagree": 0} 4 | {"score": 3, "text": "평점이 너무 높아서 봤는데 이건 뭐 액션도 아니고 로맨스도 아니고 이도저도 아닌 영화. 기대감과는 달리 너무나 지루함", "user": "16443293", "written_at": "2019.11.28 19:08", "agree": 0, "disagree": 1} 5 | {"score": 6, "text": "전형적인 히어로 각본에 개연성 또한 없다. 82년도를 감안한 전투기 씬만 볼 만하다.", "user": "16378181", "written_at": "2019.11.08 00:38", "agree": 0, "disagree": 1} 6 | {"score": 10, "text": "미군 조종사 입대 독려하려고 만든 영화인데 감독도 이렇게 흥행할 줄 몰랐다고 한다. 당시 미국방부 전폭적 지지로 항공모함, 전투기 비행 촬영에 협조해 줬다고 한다.", "user": "16366786", "written_at": "2019.11.03 22:45", "agree": 1, "disagree": 0} 7 | {"score": 7, "text": "상당히 고퀄리티의 전투기씬은 86년도에 나온거라곤 믿기 힘든 퀄리티이지만 내용이 촌스러운건 어쩔수 없었다", "user": "16256654", "written_at": "2019.10.09 02:12", "agree": 1, "disagree": 1} 8 | {"score": 4, "text": "정말 기대한것만큼은 아닌듯.. 지루 음악이 좋고 전투기 액션은 좋았지만", "user": "16187146", "written_at": "2019.09.21 23:19", "agree": 1, "disagree": 1} 9 | {"score": 3, "text": "오늘 다시보니깐 ... 뭔가 게이물 같은 느낌이....", "user": "16055155", "written_at": "2019.08.16 18:47", "agree": 0, "disagree": 3} 10 | {"score": 8, "text": "스토리부분은 약간 아쉽지만 그당시를 생각해보면 전투기 연출은 대단했다.", "user": "16007584", "written_at": "2019.08.07 23:07", "agree": 1, "disagree": 0} 11 | {"score": 9, "text": "톰크루즈의 잘생김에 한번 놀라고, 토니스콧의 세련된 연출력에 두번 놀랜다. 여주인공도 웃을때 정말 매력적. 고전명작!", "user": "15954147", "written_at": "2019.07.29 01:23", "agree": 2, "disagree": 0} 12 | {"score": 10, "text": "말이 필요한가 피가 끓는다", "user": "15951155", "written_at": "2019.07.28 10:55", "agree": 1, "disagree": 0} 13 | {"score": 8, "text": "매력적인 드라마와 스토리", "user": "15949351", "written_at": "2019.07.27 22:16", "agree": 1, "disagree": 0} 14 | {"score": 1, "text": "mmmnnmnnmmn", "user": "15946557", "written_at": "2019.07.27 07:09", "agree": 0, "disagree": 3} 15 | {"score": 10, "text": "저아저씨는 분명 불가능 미션을 수행하기 위해서 바이크 묘기, 빌딩을 나라다니고, 달리기의 1인자가 됩니다. 아 그리고 핼리콥터도 손수 몰아요? 대단하죠? 그리고 34후에 탑컨 2 개봉할꺼입.. 장담함.", "user": "15925765", "written_at": "2019.07.22 14:39", "agree": 3, "disagree": 0} 16 | {"score": 10, "text": "개봉년도보다 17년 후에 태어났지만 얼마나 재밌게 본 지 모른다", "user": "15919752", "written_at": "2019.07.20 20:35", "agree": 2, "disagree": 0} 17 | {"score": 10, "text": "톰 크루즈 외모 리즈시절 실화냐.. 남자가 봐도 잘생겼다", "user": "15918716", "written_at": "2019.07.20 14:08", "agree": 2, "disagree": 0} 18 | {"score": 10, "text": "2020년 여름에 탑건2 개봉합니다!!", "user": "15915621", "written_at": "2019.07.19 10:00", "agree": 3, "disagree": 0} 19 | {"score": 10, "text": "10띵작 + 톰 형님 리즈시절", "user": "15874536", "written_at": "2019.07.04 16:36", "agree": 2, "disagree": 0} 20 | {"score": 10, "text": "OST와 영화전개가 합을 맞춘 영화", "user": "15816660", "written_at": "2019.06.17 07:07", "agree": 1, "disagree": 0} 21 | {"score": 1, "text": "jkuhyjthjtr", "user": "15756562", "written_at": "2019.06.02 00:26", "agree": 0, "disagree": 4} 22 | {"score": 10, "text": "‘전투기 공중씬, 청춘, 로맨스, 도전, OST’ 이 모든 것의 바이블 (오프닝 씬은 단연 세월이 흘러도 압권)", "user": "15661002", "written_at": "2019.05.11 00:32", "agree": 4, "disagree": 0} 23 | {"score": 5, "text": "하이틴스타의 탄생 비교적 생생한 고공 촬영 그뿐", "user": "15575440", "written_at": "2019.04.27 13:46", "agree": 0, "disagree": 3} 24 | {"score": 10, "text": "또 봐도 또 멋진 영화~~♥", "user": "15574068", "written_at": "2019.04.27 10:43", "agree": 2, "disagree": 0} 25 | {"score": 10, "text": "아니 이형은 얼굴에 똥을 발라놔도 나보다 1억배는 잘생길거 같다 ㅋㅋ", "user": "15559875", "written_at": "2019.04.25 18:07", "agree": 3, "disagree": 0} 26 | {"score": 1, "text": "kjlkluyikytgui", "user": "15528176", "written_at": "2019.04.21 01:10", "agree": 0, "disagree": 3} 27 | {"score": 8, "text": "스토리는 그저 그렇지만 30년이 더 지난 후인 지금봐도 공중 전투는 괜찮고 음악은 좋습니다. 그리고 젊은 시절 톰형 멋지네요. 맥라이언이 조연으로 출연해서 놀랐네요ㅋㅋ", "user": "15525835", "written_at": "2019.04.20 10:36", "agree": 2, "disagree": 0} 28 | {"score": 7, "text": "음..명성에 비해서는 좀..", "user": "15497506", "written_at": "2019.04.08 21:28", "agree": 0, "disagree": 1} 29 | {"score": 8, "text": "지금 봐도 뛰어난 토니 스콧의 명작. 우리가 알고 있는 톰크루즈의 캐릭터는 이 때부터 완성. \"어디 가요?\" \"샤워하러요\" \"(훗, 넘어왔구나)\" \"고마워요, 즐거웠...", "user": "15486500", "written_at": "2019.04.05 05:43", "agree": 1, "disagree": 0} 30 | {"score": 10, "text": "이야 이건 해군 홈보영화인데... 지금봐도 멋있잖아.", "user": "15463491", "written_at": "2019.03.29 10:32", "agree": 1, "disagree": 0} 31 | -------------------------------------------------------------------------------- /enhance_comments_user_idx.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import time 4 | from datetime import datetime 5 | from naver_movie_scraper import save_list_of_dict 6 | from naver_movie_scraper import scan_comment_indices 7 | from naver_movie_scraper import scrap_comments_of_a_user 8 | 9 | 10 | def check_dir(dirname): 11 | if not os.path.exists(dirname): 12 | os.makedirs(dirname) 13 | 14 | def make_indices_list(directory, debug): 15 | return sorted(scan_comment_indices(directory)) 16 | 17 | def save_index_list(indices, path): 18 | dirname = os.path.dirname(path) 19 | if (dirname != '') and (not os.path.exists(dirname)): 20 | os.makedirs(dirname) 21 | with open(path, 'w', encoding='utf-8') as f: 22 | for idx in sorted(indices): 23 | f.write(f'{idx}\n') 24 | 25 | def load_indices(path): 26 | with open(path, encoding='utf-8') as f: 27 | indices = [int(idx.strip()) for idx in f] 28 | return indices 29 | 30 | def main(): 31 | parser = argparse.ArgumentParser() 32 | parser.add_argument('--comment_directory', type=str, default='./output/', help='Output directory') 33 | parser.add_argument('--enhanced_comment_directory', type=str, default='./user_comments/', help='Output directory') 34 | parser.add_argument('--index_list', type=str, default='./comment_indices', help='Remained comment indices') 35 | parser.add_argument('--sleep', type=float, default=0.1, help='Sleep time') 36 | parser.add_argument('--index_update_interval', type=int, default=1000, help='Save index list per round') 37 | parser.add_argument('--debug', dest='debug', action='store_true', help='Find indices from 10 movies') 38 | parser.add_argument('--noscrap', dest='noscrap', action='store_true', help='No scraip, only make index list') 39 | parser.add_argument('--force_make', dest='force_make', action='store_true', help='Remake index list if exist the index list file') 40 | 41 | args = parser.parse_args() 42 | directory = args.comment_directory 43 | data_dir = args.enhanced_comment_directory 44 | index_list = args.index_list 45 | sleep = args.sleep 46 | index_update_interval = args.index_update_interval 47 | debug = args.debug 48 | noscrap = args.noscrap 49 | force_make = args.force_make 50 | 51 | # load or make index list 52 | if (not os.path.exists(index_list)) or (force_make): 53 | indices = make_indices_list(directory, debug) 54 | save_index_list(indices, index_list) 55 | else: 56 | indices = load_indices(index_list) 57 | print(f'Found {len(indices)} indices') 58 | 59 | if noscrap: 60 | return None 61 | 62 | # scraping 63 | diff = n_indices = n_remains = len(indices) 64 | n_exceptions = 0 65 | n_rounds = 0 66 | 67 | # hard-coding 68 | exists = set() 69 | exists.add(("bbq2****", 1107)) 70 | 71 | while indices and diff > 0: 72 | n_rounds += 1 73 | seed_idx = str(indices.pop()) 74 | print(f'seed = {seed_idx} ') 75 | 76 | try: 77 | comments, n_exceptions_, flag, username, max_page = scrap_comments_of_a_user(seed_idx, sleep, exists) 78 | except Exception as e: 79 | print(f'\nUnexpected errors. skip the seed {seed_idx}\n') 80 | n_exceptions += 1 81 | 82 | n_exceptions += n_exceptions_ 83 | 84 | # update scraped user 85 | if (max_page > 10) and (flag): 86 | exists.add((username, max_page)) 87 | 88 | dirname = f'{data_dir}/{seed_idx[:-4]}' 89 | check_dir(dirname) 90 | path = f'{dirname}/{seed_idx}' 91 | save_list_of_dict(comments, path) 92 | 93 | # integer 94 | removals = {c['idx'] for c in comments} 95 | # integer 96 | indices = [idx for idx in indices if not (idx in removals)] 97 | 98 | # save remain indices 99 | if (n_rounds % index_update_interval == 0): 100 | save_index_list(indices, index_list) 101 | print('\rsaved seed indices') 102 | time.sleep(1.0) 103 | 104 | diff = n_remains - len(indices) 105 | n_remains = len(indices) 106 | progress = 100 * (1 - n_remains / n_indices) 107 | now = str(datetime.now())[:19] 108 | print(f'\rscrap comments of a user ({progress:.4}%), -{diff} indices, {n_remains} remains, exceptions={n_exceptions} @ {now}') 109 | time.sleep(sleep) 110 | 111 | print('done') 112 | 113 | if __name__ == '__main__': 114 | main() 115 | -------------------------------------------------------------------------------- /output/comments/171541: -------------------------------------------------------------------------------- 1 | {"score": 6, "text": "마터스의 여주가 홀로 싸워봐야 헛수고. 너나 많이 싸워라. 우리안에 갇힌 무력한 동물들이 현실을 도피하고 있는 것 같아 한심하고 우스워?", "user": "16467274", "written_at": "2019.12.07 14:04", "agree": 0, "disagree": 0} 2 | {"score": 8, "text": "", "user": "16453066", "written_at": "2019.12.02 00:00", "agree": 0, "disagree": 0} 3 | {"score": 10, "text": "진짜 너무 소름돋고 연출좋고 너무 재밌음 ㅠㅠ 같이 본 언니들 4명도 다 만족함 재밌다고 ㅋㅋㅋ꼭 보셈", "user": "16448826", "written_at": "2019.11.30 17:55", "agree": 0, "disagree": 0} 4 | {"score": 4, "text": "마터스에 못 미치는 영화신선함이 떨어져 살짝 실망..", "user": "16446877", "written_at": "2019.11.30 00:35", "agree": 0, "disagree": 0} 5 | {"score": 10, "text": "헐..대박재미짐..반전도 죽이고 꼭 보세요.절대루회안함.강추", "user": "16433441", "written_at": "2019.11.25 11:20", "agree": 0, "disagree": 0} 6 | {"score": 8, "text": "베스와 베라는 아직도 못 빠져나온 거 같다. 마지막에 유리창에 엄마와 인사를 하는데.. 현실에서 엄마는 보인 적이 없었고 항상 언니가 만들어낸 상상 속에서만 있었다. 그러니 용감하게 탈출해낸것도 아직 그들은 빠져나오...", "user": "16430185", "written_at": "2019.11.24 17:04", "agree": 2, "disagree": 1} 7 | {"score": 2, "text": "평점 높아서 봤더니 무서운 것은 전혀 없고 소리 질러서 귀가 아프다. 재미도 없다. 공포는 무슨 시시하기만 할뿐. 그래도 다 봤으니 1점 주려다 2점 줌.", "user": "16421210", "written_at": "2019.11.22 19:13", "agree": 0, "disagree": 1} 8 | {"score": 5, "text": "마터스 감독작이라는 게 믿어지지않을정도로 별거없는 고문 포르노.(네이버 별점평가 아주 ㅄ 됐네)", "user": "16374034", "written_at": "2019.11.06 13:37", "agree": 0, "disagree": 0} 9 | {"score": 10, "text": "마지막까지 엿맥일까봐 조마조마 했다. 베쓰인가? 격투씬에서 고기씹을때 조큼 부족한 특수효과여서 텐션 떨어질뻔했는데. 조큼 찝찌르하지만 해피하게 마무리 해줘서 참는다.", "user": "16366534", "written_at": "2019.11.03 21:58", "agree": 0, "disagree": 0} 10 | {"score": 8, "text": "오우거 연기가 좀 아쉽긴한데 재밌었음", "user": "16357359", "written_at": "2019.11.01 22:57", "agree": 0, "disagree": 0} 11 | {"score": 10, "text": "마터스를 보고는 3일동안 밥을못먹고 베스와베라를 보고는 3일동안 악몽을꿨다", "user": "16228095", "written_at": "2019.10.03 14:26", "agree": 0, "disagree": 0} 12 | {"score": 10, "text": "스토리,스타일 다 세련", "user": "16222603", "written_at": "2019.10.02 20:33", "agree": 0, "disagree": 0} 13 | {"score": 5, "text": "배우 얼굴엔 흉터 안겨주고 감독은 이후로 신작 소식이 없네 ㅋㅋ", "user": "16192681", "written_at": "2019.09.23 17:57", "agree": 0, "disagree": 0} 14 | {"score": 10, "text": "오랜만에 재밌는 공포영화였습니다.", "user": "16116790", "written_at": "2019.09.03 12:44", "agree": 0, "disagree": 0} 15 | {"score": 9, "text": "몰입도 최고오로지 국내 제목때문에 별 하나 뺌 ㅡㅡ베스와 베라...;;;;", "user": "16021102", "written_at": "2019.08.10 10:18", "agree": 0, "disagree": 0} 16 | {"score": 10, "text": "연기너무잘하는데요어린베스 무서워하는연기 너무 잘하는듯너무 재밌는영화", "user": "15941905", "written_at": "2019.07.26 06:34", "agree": 1, "disagree": 0} 17 | {"score": 10, "text": "여름 공포영화임 말이 필요 없음 굿.", "user": "15925934", "written_at": "2019.07.22 16:01", "agree": 1, "disagree": 0} 18 | {"score": 10, "text": "반전의반전?너무 무서워서 중간에 포기하려고했던영화?세상에서 제일 무서운건 인간이라는것을 확인시켜주는영화.", "user": "15922788", "written_at": "2019.07.21 16:05", "agree": 1, "disagree": 0} 19 | {"score": 5, "text": "마터스나 이거나 그냥 고문포르노. 감독은 어떻게든 주인공을 괴롭히고 싶은 듯. 트라우마에 굴복하지 말고 이겨내라 같은 메시지를 주려 했으나 가학적인 영상의 반복이 메시지 전달력을 높여준 마터스와는 달리 불필요하게 가...", "user": "15921431", "written_at": "2019.07.21 03:11", "agree": 2, "disagree": 2} 20 | {"score": 1, "text": "밑에 댓글들 다 뭐하시는 분들이심ㅋㅋ여러분 포스터부터 각 나오지만 진짜 재미없어요.. 반전도 반전같지않고 소름돋거나 그런 거 없어요 개답답해서 진짜 걍 보다가 끌뻔했는데 돈 아까워서 겨우 봄.. 잠들뻔하기도 하고 갑...", "user": "15903347", "written_at": "2019.07.14 22:20", "agree": 1, "disagree": 3} 21 | {"score": 10, "text": "반전도 재밌고 자매애도 멋졌다 역시 실망시키지 않는 마터스 감독!", "user": "15894309", "written_at": "2019.07.12 00:20", "agree": 1, "disagree": 0} 22 | {"score": 1, "text": "영화 초중반부터 반전 예상 쌉가능ㅋ 진짜 처음부터 끝까지 개연성도 스토리도 없이 컨셉적인 자극만 잔뜩임ㅋㅋㅋㅋ라면먹다가 눈에 국물 튀긴 기분; 영화를 이렇게 만들거면 배우 케어라도 잘 하든가", "user": "15888686", "written_at": "2019.07.09 22:52", "agree": 0, "disagree": 2} 23 | {"score": 8, "text": "Incident in a Ghost Land,2018", "user": "15870306", "written_at": "2019.07.03 07:45", "agree": 0, "disagree": 0} 24 | {"score": 5, "text": "스릴러 빠진 싸이코 영화였음 한줄평 보고 기대했는데 기대 이하! 조낸 어렵게 만들고 싶었던 거 같은데 굳이 먼 대단한 영화 만든다고 이렇게 어렵게 만든 건지 노이해 (싸이코에게 구금 구타 당하여 충격으로 미쳐버린 동...", "user": "15848198", "written_at": "2019.06.26 22:53", "agree": 0, "disagree": 3} 25 | {"score": 5, "text": "내가 비급 공포영화를 머리까지 써서 봐야하는지 고민", "user": "15840092", "written_at": "2019.06.24 12:14", "agree": 1, "disagree": 2} 26 | {"score": 9, "text": "감독의 전작인 마터스와 비슷한 설정이 많음. 고문 받는 두 소녀, 탈출하는 두 소녀, 외상후 스트레스 장애를 표현한 것까지. 전반과 후반이 전혀 다른 영화가 되는 것도 마터스와 똑같음. 다만, 다행히(?) 불쾌감이 ...", "user": "15835210", "written_at": "2019.06.23 03:07", "agree": 2, "disagree": 2} 27 | {"score": 1, "text": "기분 개더러워요 보지마세요", "user": "15827000", "written_at": "2019.06.21 00:26", "agree": 0, "disagree": 0} 28 | {"score": 1, "text": "여성을 갈아서 가학적인것에만 젭중한 영화 나는 보는 내내 눈물밖에 안나왔다 미친것들 이게 무슨 공포수작이야 여성을 이용한 쓰레기임", "user": "15820285", "written_at": "2019.06.18 22:48", "agree": 2, "disagree": 4} 29 | {"score": 10, "text": "공포매니아인데 요즘본영화중 1408보다 최고. 혼자불끄고볼륨크게보세요 지존임 ㅋ언니도추천했다가 완전짱이라고 ㅋ ㅋ", "user": "15797181", "written_at": "2019.06.10 14:55", "agree": 2, "disagree": 1} 30 | {"score": 8, "text": "파스칼 작품답게 같은 스토리로도 고급스럽고 호기심을 일어나게 작품을 잘 이끌어 나간다.", "user": "15793577", "written_at": "2019.06.09 16:14", "agree": 1, "disagree": 1} 31 | -------------------------------------------------------------------------------- /output/meta/171541.json: -------------------------------------------------------------------------------- 1 | { 2 | "movie_idx": 171541, 3 | "expert_score": 6.0, 4 | "netizen_score": 7.8, 5 | "title": "베스와 베라", 6 | "e_title": "Incident in a Ghost Land , 2018", 7 | "genres": [ 8 | "공포" 9 | ], 10 | "countries": [ 11 | "프랑스", 12 | "캐나다" 13 | ], 14 | "running_time": 91, 15 | "open_date": [ 16 | "2019-01-24" 17 | ], 18 | "grade": "15세 관람가", 19 | "story": "정체불명의 괴한으로부터 끔찍한 일을 겪은 어린 ‘베스’와 ‘베라’.\n사고 이후, 언니 ‘베스’는 자전적 소설을 출간하며 성공하지만,\n동생 ‘베라’는 여전히 그날의 공포에 사로잡힌 채 괴로워한다.\n제발 자신을 버리지 말라고 절규하는 ‘베라’ 곁으로 다시 돌아온 ‘베스’.\n하지만 끝내 끝나지 않고 되풀이되는 악몽 같은 현실과 엇갈린 진실은\n두 자매를 점점 더 깊은 혼란에 빠뜨리는데…\n제작노트 보기", 20 | "making_note": "ABOUT MOVIE 1\n2019 첫 번째 웰메이드 공포 스릴러 ,\n파스칼 로지에 감독의 화려한 귀환!\n충격과 논란의 호러 마스터피스 으로 전 세계를 매료시킨 공포영화 명장 파스칼 로지에 감독이 7년 만에 스크린 복귀를 알렸다. 바로 1월 24일 개봉하는 2019 첫 번째 웰메이드 공포 스릴러 다. 영화는 어린 시절 정체불명의 괴한에게 감금되는 끔찍한 사건 이후, 자전적 소설로 베스트셀러 작가가 된 언니 ‘베스’와 트라우마에 갇혀 사는 동생 ‘베라’가 엇갈린 기억 속에서 충격적인 공포를 또다시 겪게 되며 벌어지는 이야기를 그린 작품이다. 현실과 망상의 경계를 모호하게 오가며 지적 호기심을 불러일으키는 반전 전개는 물론, 숨통을 조여오는 팽팽한 긴장감, 매 컷마다 관객을 압도하는 파스칼 로지에 감독 특유의 과감하고 강렬한 연출력은 더할 나위 없는 2019 첫 번째 웰메이드 공포 스릴러의 탄생을 알린다.\n자신만의 독보적인 공포 세계를 구축해 온 파스칼 로지에 감독의 는 공포영화 명장의 신작답게, 공개 직후 유수 영화제에 줄지어 초청되어 뜨거운 호응과 갈채를 받았다. 세계 3대 판타스틱 영화제로 꼽히는 시체스 국제영화제에서 베스트 판타스틱 경쟁부문에 노미네이트 되었으며, 제라르메 국제판타스틱영화제에서는 최우수작품상, 관객상, 심사위원상 등 3관왕을 석권, 몰린스 호러영화제 각본상 수상과 전주 국제영화제까지 다수의 영화제에서 초청과 수상을 거두며 작품성을 인정받았다. 완성도 보장된 그의 신작이 과연 또 한 번 센세이션을 일으킬 수 있을지 국내 평단과 예비 관객들의 기대감이 높아지고 있다.\nABOUT MOVIE 2\n“공포 영화에 대한 새로운 시도!”? Hollywood Reporter\n영리한 스토리텔링과 관객을 압도하는 공포에 쏟아지는 극찬 세례!\n2019년 새해 첫 포문을 여는 웰메이드 공포로 주목 받고 있는 영화 가 개봉 전부터 만장일치 극찬 세례를 받고 있어 화제다. 충격적인 공포로 세계를 경악시킨 파스칼 로지에 감독의 대표작 이후, 7년 만의 신작 소식은 일찌감치 세간의 이목을 집중시키기에 충분했다. 가 공개된 직후 해외 언론과 평단의 뜨거운 호평이 쏟아진 것은 물론, 호러 마니아들 사이에서 입소문을 타며 한층 더 확장된 파스칼 로지에 표 공포 세계에 대한 찬사가 이어지고 있다. “공포영화에 대한 새로운 시도!”(Hollywood Reporter), “전형적이지 않고, 영리한 작품!”(VODzilla.co), “신선하고 짜임새 있는 플롯!”(Starbust), “다른 슬래셔 영화들과는 구분되는 신선한 작품”(Cine Premiere), “파스칼 로지에의 저력을 다시 한 번 보여준 작품”(The Hollywood News) 등 반응을 보이며 영리한 스토리텔링과 집요하고 치밀한 연출에 대한 칭찬을 아끼지 않아 이번 신작에 대한 기대감을 증폭시킨다. 여기에 지난 전주 영화제 공식 초청을 통해 영화를 먼저 만난 영화 팬들 역시 “별 6개를 주지 못하는 게 아쉽다”(김**), “신선함에 가까운 뷰티풀 호러”(한**), “여전한 감독의 과감한 호러 리듬과 연출!”(Swe**), “빈틈 없이 잘 만든 영화”(숲**), “완성도 높은 호러 스릴러의 면모”(바**), “자극적이고 흡입력 있다”(임**) 등 댓글을 남기며 역대급 공포에 대한 뜨거운 찬사를 더했다.\n‘파스칼 로지에 감독 작품’이라는 네임밸류를 한 층 더 끌어올리는 신선한 공포로 예비 관객들의 시선을 압도하고 있는 는 새해 극장가를 완벽히 매료시킬 단 하나의 웰메이드 공포 스릴러로 모두의 기대를 한 몸에 받고 있다.\nABOUT MOVIE 3\n2017 , 2018 을 잇는 2019 웰메이드 호러!\n탄탄한 스토리 & 소름 돋는 반전으로 극장가 장악 예고!\n‘미국판 곡성’으로 불리며 관객의 허를 찌르는 스토리로 2017 극장가를 장악한 , 밀폐된 공간 속에서 펼쳐지는 소름 끼치는 가족 이야기 은 모두 탄탄한 스토리와 소재, 독특한 연출 스타일로 극장가를 매료시킨 화제작이다. 이런 두 작품의 매력을 모두 갖추며 공포영화의 흥행 계보를 이어갈 2019년 기대작으로 파스칼 로지에 감독의 가 주목받고 있어 화제다.\n는 두 소녀가 어린 시절 괴한에게 감금되는 끔찍한 경험을 한 뒤, 끝난 줄 알았던 악몽, 되풀이되는 참혹한 공포로부터 벗어나기 위한 자매의 필사의 탈출을 그린다. 영화제에서 선공개된 뒤 “연출력에 숨이 막힌다!”라는 압도적 평가를 받은 영화는 두 자매의 엇갈린 기억 속에서 현실과 망상의 경계를 아슬아슬하게 오가며 관객의 모든 추측을 반전시키는 영리한 스토리텔링을 선보인다. 또한 카메라 앵글, 소품부터 사운드까지 다방면에서 빈틈없는 치밀한 연출로 완벽한 공포를 선사한다. 파스칼 로지에 감독 특유의 반전 결말, 정교한 서스펜스와 압도적 스릴감은 관객들을 순식간에 스크린 속으로 끌어들여 생생한 공포감을 느끼게 할 것으로 예상된다. 과 못지않은 캐릭터 매력 또한 주목할만한 포인트인데, 이 흑인 남자친구 ‘크리스’, 이 저주를 파헤치는 엄마 ‘애니’ 캐릭터로 관객들을 사로잡았다면 는 악몽을 딛고 소설가로 성공한 언니 ‘베스’와 트라우마에 갇힌 동생 ‘베라’의 상반된 성격, 어느 것이 진실인지 추측할 수 없는 팽팽한 갈등 구도로 극의 재미를 더할 예정이다. 예측불가한 스토리와 소름 돋는 반전 전개가 돋보이는 는 오는 1월 유일무이 웰메이드 공포 스릴러로 극장가를 사로잡을 준비를 마쳤다.\nINTERVIEW\n7년 만에 돌아온 공포 마스터 파스칼 로지에 감독!\n비하인드 인터뷰 A to Z\nQ. 이 영화의 탄생 과정이 궁금하다.\nA. 내 첫 직감은 ‘누군가의 꿈을 현실처럼 촬영하는 것’이었다. 모두 쉽게 말하지만, 아무도 제대로 정의할 수 없는 ‘현실’ 말이다. 나만의 스타일을 찾는데 시간이 좀 걸렸지만, 인물의 내면세계만으로 영화를 진행시킬 수 있다는 것을 깨달은 순간 순식간에 시나리오가 나왔다.\nQ. 주로 영감을 어디서 받는지?\nA. 나는 엄청난 영화광이라, 좋아하는 작품에서 받은 영향에서 벗어나기 힘들어하는 편이다.(웃음). 를 찍으면서 내게 영감을 준 사람이 있는데, 바로 ‘토브 후퍼’ 감독이다. 같은 영화에서 볼 수 있는, 상식을 파괴하는 섬뜩한 광기는 내게 내가 원하는 작품을 만들 수 있도록 자신감을 주었다. 특히 호러 장르의 경우, 작업 도중 결국 다 비슷한 작품인 게 아닌가 의심이 들 때가 많은데, 그럴 땐 ‘존재하지 않는 것을 만든다’고 생각하는 게 나아가는 데 도움이 된다. 는 나의 가장 억압되고 어두운 면들의 귀환이라고 볼 수도 있는 작품이다. (웃음)\nQ. 영화의 대부분이 집을 둘러싸고 벌어진다. 촬영지에 대한 에피소드가 있는지?\nA. 아주 오래 수소문한 끝에 1880년대에 지어진 전형적인 미국식 농가를 찾았다. 관객들을 아늑하고 익숙한 장소에 놓고 깜짝 놀라게 해주고 싶었다. 벽을 부수고 새로 도배해서 3층짜리 집인 것처럼 리모델링 했다. 실제 집은 2층까지만 있고, 3층은 영화의 마지막 부분에서 철저히 가상으로 존재한다.\nQ. 그 집은 마치 미로를 연상시킨다. 악몽 같은 배경을 조성하기 위해 어떤 선택을 했나.\nA. 소녀들을 기다리고 있던 완벽한 놀이터 같은 느낌이 들도록, 악당들에게 더 걸맞은 집으로 보이길 원했다. 인위적인 스튜디오보다는 진짜 집에서 벽 사이를 마구 돌아다니며 와이드샷을 찍고픈 유혹도 있었고, 그 집에서 느껴진 여러 충동들도 잊고 싶지 않았다. 결국 작은 집에 30명이나 들어가는 바람에 촬영이 꽤 힘들었지만. (웃음) 촬영의 경우 ‘베스’는 자기가 보고 싶은 것만 본다는 점을 강조하기 위해, 2.35미리 카메라를 선택해 시선들의 주관성과 단편성을 더 부각시켰다. 와이드 포맷으로 거기에 폐쇄공포적인 느낌을 더했고, 실제 집에서 촬영함으로써 카메라의 시야와 각도를 제한해서 ‘베스’가 제한된 공간만 상상하는 것을 재현했다.\nQ. 각 방들의 소품들은 어떻게 구상했는지?\nA. 처음에는 단출한 장식들을 생각했는데, 프로덕션 디자이너인 고든이 발품을 팔아 개인 소품을 가져오면서 그 집을 좀 더 상식 밖의 장소로 만들어갔다. 고든은 동물 머리가 얹어진 아이의 나체 같은 것들을 제작하고, 퇴폐적이고 비상식적인 아수라장에 모순적인 요소들을 더해 영화 내내 불쾌함을 유발하곤 했다. 영화의 가장 악몽스럽고 끔찍한 장면들이 사실은 현실의 모습이라는 걸 깨달은 순간, 나는 이 방향으로 더 나아가야 된다고 확신했다. 정상적으로 보이는 것들이 사실은 가장 미친 법이고, 이렇게 끔찍하고 괴기스러운 것들이 현실을 제일 정확하게 표현하니까.\nQ. 카메라가 영화 내내 계단, 통로, 문고리 사이로 끊임없이 움직이던데…\nA. 인물들의 생각과 마음 속 움직임들의 시각적 표현이기 때문에 카메라는 계속 움직여야 했다. ‘베스’가 자신의 정신으로 하여금 현실을 탈출해 계속 움직이게 하는 동안, 카메라도 끊임없이 그녀를 따라 움직였다. 집 외에는 캐나다 매니토바 주의 버려진 목초지에서도 촬영한 적이 있다. 온도가 영하 60도까지 떨어져서 촬영을 중단해야 했지만. (웃음)\nQ. 영화 내에서 공포는 소리나 관객이 느끼는 존재감에서 오는데, 이런 것들은 대부분 화면 밖에서 일어난다. 관객들의 상상력에 기대는 이러한 공포스러운 효과들은 어떻게 작업한 건가?\nA. 영화에서 소리는 두 가지로 나뉜다. 괴물의 울부짖는 소리에서 나오는 조금 더 억압되고 본능적인 소리, 그리고 서정적인 음악이다. 사운드 믹서가 30개가 넘는 소리들을 섞어 괴물의 목소리를 재현했다. 울부짖음이나 숨소리, ‘베스’의 망상과 지하실 내 현실 사이의 전환 등에서 비롯된 소리들이다. 이러한 본능적인 소리들은 유년기와 작별하는 모습, (현실이던 상징적이던) 엄마의 죽음, 어쩔 수 없이 찾아오는 현실 등 서정적인 음악으로 표현된 소리들과 의도치 않은 대조를 만들어낸다. 폭력적이고 불쾌한 공포영화를 만들고 있지만 동시에 작가가 되길 꿈꾸는 어린 소녀에 대한 영화를 만들고 있다는 사실을 계속 일깨우려 노력했다.", 21 | "box_office": -1 22 | } -------------------------------------------------------------------------------- /naver_movie_scraper/comments_with_userlist.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | from glob import glob 3 | import json 4 | import math 5 | import re 6 | import requests 7 | import time 8 | 9 | 10 | url_base = 'https://movie.naver.com/movie/point/af/list.nhn?st=nickname&target=after&sword={}' 11 | 12 | def scrap_comments_of_a_user(seed_idx, sleep=0.1, exists=None): 13 | """ 14 | Usage 15 | ----- 16 | >>> seed_idx = '123467' # comment seed idx 17 | >>> comments, n_exceptions, flag, username, max_page = scrap_comments_of_a_user(seed_idx) 18 | """ 19 | url = url_base.format(seed_idx) 20 | print('check username ... ', end='') 21 | soup, max_page, username = get_comment_soup(url) 22 | 23 | comments = [] 24 | n_exceptions = 0 25 | 26 | if (exists is not None) and (max_page > 5) and ((username, max_page) in exists): 27 | print(f'skip exists user {username}, max_page = {max_page}, seed = {seed_idx}') 28 | return comments, n_exceptions, False, username, max_page 29 | print('done', end='') 30 | 31 | comments_, n_exceptions_ = parse_comments(soup) 32 | comments += comments_ 33 | n_exceptions += n_exceptions_ 34 | 35 | # available only to 1000 page 36 | max_page = min(max_page, 999) 37 | 38 | if max_page > 1: 39 | for page in range(2, max_page+1): 40 | time.sleep(sleep) 41 | try: 42 | url_ = f'{url}&page={page}' 43 | soup, max_page, _ = get_comment_soup(url_) 44 | comments_, n_exceptions_ = parse_comments(soup) 45 | comments += comments_ 46 | n_exceptions += n_exceptions_ 47 | print(f'\rscraping with seed = {seed_idx}, page = {page}/{max_page}', end='') 48 | except: 49 | comments, n_exceptions, False, username, max_page 50 | return comments, n_exceptions, True, username, max_page 51 | 52 | normalize_pattern = re.compile('[\r\n\t]') 53 | doublespace_pattern = re.compile('[\s]+') 54 | 55 | def normalize_text(text): 56 | """ 57 | Usage 58 | ----- 59 | >>> normalize_text('이건 \t\t\t정말\n 짱야 !! ') 60 | $ '이건 정말 짱야 !!' 61 | """ 62 | text = normalize_pattern.sub(' ', text) 63 | text = doublespace_pattern.sub(' ', text) 64 | return text.strip() 65 | 66 | def get_comment_soup(url): 67 | r = requests.get(url, allow_redirects=True) 68 | max_page = get_max_page(r) 69 | 70 | b = r.text.index('') 71 | e = r.text.index('') + 15 72 | soup = BeautifulSoup(r.text[b:e], 'lxml') 73 | 74 | try: 75 | b = r.text.index('
') + 30 76 | e = r.text.index('<', b) 77 | e = min(e, b+30) 78 | username = r.text[b:e].strip() 79 | except: 80 | username = 'unknown' 81 | return soup, max_page, username 82 | 83 | def get_max_page(r): 84 | e = r.text.index("개의 평점이 있습니다") 85 | b = r.text.rindex('>', 0, e) + 1 86 | count = int(r.text[b:e].replace(',','').replace(' ', '').strip()) 87 | max_page = math.ceil(count/10) 88 | return max_page 89 | 90 | def parse_comments(soup): 91 | def parse(tr): 92 | movie_link = tr.select('a[href^="?st=mcode&sword="]')[0].attrs['href'] 93 | b = movie_link.index('sword=')+6 94 | e = movie_link.index('&', b) 95 | movie_idx = int(movie_link[b:e]) 96 | idx, _, score, text, timestamp = tr.text.strip().split('\n\n') 97 | idx = int(idx) 98 | score = int(score.split()[-1].replace('중', '')) 99 | text = text.strip() 100 | # remove "신고" 101 | text = text[:-2].strip() 102 | # normalize 103 | text = normalize_text(text) 104 | timestamp = timestamp.split('**')[-1].strip() 105 | return {'idx': idx, 'movie_idx':movie_idx, 'score': score, 'written_at': timestamp, 'text': text} 106 | 107 | trs = soup.select('tr')[1:] 108 | comments = [] 109 | n_exceptions = 0 110 | for tr in trs: 111 | try: 112 | comments.append(parse(tr)) 113 | except Exception as e: 114 | n_exceptions += 1 115 | return comments, n_exceptions 116 | 117 | def scan_comment_indices(comments_dir, debug=False): 118 | """ 119 | Usage 120 | ----- 121 | >>> indices = scan_comment_indices('./comments/') 122 | """ 123 | paths = glob(f'{comments_dir}/*') 124 | if debug: 125 | paths = paths[:10] 126 | indices = set() 127 | n_exceptions = 0 128 | n_paths = len(paths) 129 | for i, path in enumerate(paths): 130 | with open(path, encoding='utf-8') as f: 131 | for line in f: 132 | try: 133 | indices.add(int(json.loads(line.strip())['idx'])) 134 | except: 135 | n_exceptions += 1 136 | continue 137 | if i % 1000 == 0: 138 | n_indices = len(indices) 139 | percent = 100 * (i+1) / n_paths 140 | print(f'\rscanning from {i+1} / {n_paths} ({percent:.4}%): found {n_indices} indices, {n_exceptions} exceptions', end='') 141 | 142 | n_indices = len(indices) 143 | suffix = ' ' * 20 144 | print(f'\rscanning has been done. found {n_indices} indices, {n_exceptions} exceptions{suffix}') 145 | return indices 146 | -------------------------------------------------------------------------------- /naver_movie_scraper/cli.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import time 5 | from tqdm import tqdm 6 | 7 | from .basic import scrap_basic 8 | from .detail import scrap_casting 9 | from .script import scrap_bestscripts 10 | from .comments import scrap_comments 11 | from .utils import save_list_of_dict 12 | from .utils import save_json 13 | from .utils import load_list_of_dict 14 | 15 | 16 | def main(): 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('--output', type=str, default='./output/', help='Output directory') 19 | parser.add_argument('--begin_idx', type=int, default=134963, help='Index of first movie') 20 | parser.add_argument('--end_idx', type=int, default=134963, help='Index of last movie') 21 | parser.add_argument('--specific_idx', type=str, nargs='+', default='', help='Index of specific movies') 22 | parser.add_argument('--limit', type=int, default=-1, help='Page limitation for comments & best scripts') 23 | parser.add_argument('--sleep', type=float, default=0.1, help='Sleep time per each page in comments & best scripts') 24 | parser.add_argument('--basic', dest='basic', action='store_true') 25 | parser.add_argument('--casting', dest='casting', action='store_true') 26 | parser.add_argument('--bestscripts', dest='bestscripts', action='store_true') 27 | parser.add_argument('--comments', dest='comments', action='store_true') 28 | parser.add_argument('--debug', dest='debug', action='store_true', help='limit -> 3') 29 | parser.add_argument('--fast_update', dest='fast_update', action='store_true') 30 | 31 | args = parser.parse_args() 32 | directory = args.output 33 | begin_idx = args.begin_idx 34 | end_idx = args.end_idx 35 | specific_idx = args.specific_idx 36 | limit = args.limit 37 | sleep = args.sleep 38 | debug = args.debug 39 | fast_update = args.fast_update 40 | 41 | if debug: 42 | limit = 3 43 | 44 | idxs = range(begin_idx, end_idx + 1) 45 | if specific_idx: 46 | idxs = [int(idx) for idx in specific_idx] 47 | 48 | n = len(idxs) 49 | exceptions = [] 50 | 51 | if (not args.basic) and (not args.casting) and (not args.bestscripts) and (not args.comments): 52 | raise ValueError("Check one more options from[--basic, --casting, --best_scripts, --comments") 53 | 54 | if args.basic: 55 | os.makedirs(f'{directory}/meta/', exist_ok=True) 56 | for idx in tqdm(idxs, desc='Scrap basic meta', total=n): 57 | path = f'{directory}/meta/{idx}.json' 58 | if (fast_update) and (os.path.exists(path)): 59 | continue 60 | try: 61 | save_json(scrap_basic(idx), path) 62 | except Exception as e: 63 | exceptions.append(f'Scrap basic {idx}: {str(e)}') 64 | 65 | if args.casting: 66 | os.makedirs(f'{directory}/actors/', exist_ok=True) 67 | os.makedirs(f'{directory}/directors/', exist_ok=True) 68 | os.makedirs(f'{directory}/staffs/', exist_ok=True) 69 | for idx in tqdm(idxs, desc='Scrap casting', total=n): 70 | path = f'{directory}/actors/{idx}' 71 | if (fast_update) and (os.path.exists(path)): 72 | continue 73 | try: 74 | castings = scrap_casting(idx) 75 | for key in ['actors', 'directors', 'staffs']: 76 | path = f'{directory}/{key}/{idx}' 77 | if castings.get(key, []): 78 | save_list_of_dict(castings[key], path) 79 | except Exception as e: 80 | exceptions.append(f'Scrap casting {idx}: {str(e)}') 81 | 82 | if args.bestscripts: 83 | os.makedirs(f'{directory}/bestscripts/', exist_ok=True) 84 | for idx in tqdm(idxs, desc='Scrap best-scripts', total=n): 85 | path = f'{directory}/bestscripts/{idx}' 86 | if (fast_update) and (os.path.exists(path)): 87 | continue 88 | try: 89 | scripts = scrap_bestscripts(idx, limit, sleep) 90 | if scripts: 91 | save_list_of_dict(scripts, path) 92 | except Exception as e: 93 | exceptions.append(f'Scrap best-scripts {idx}: {str(e)}') 94 | 95 | if args.comments: 96 | os.makedirs(f'{directory}/comments/', exist_ok=True) 97 | for i_movie, idx in enumerate(tqdm(idxs, desc='Scrap comments', total=n)): 98 | path = f'{directory}/comments/{idx}' 99 | last_time = None 100 | comments_ = [] 101 | if fast_update and os.path.exists(path): 102 | comments_ = load_list_of_dict(path) 103 | last_time = comments_[0]['written_at'] 104 | comments_new = scrap_comments(idx, limit, sleep, last_time, i_movie, n) 105 | if comments_new: 106 | comments_ += comments_new 107 | comments_ = {json.dumps(obj, ensure_ascii=False) for obj in comments_} 108 | comments_ = [json.loads(obj) for obj in comments_] 109 | comments_ = sorted(comments_, key=lambda x:x['written_at'], reverse=True) 110 | save_list_of_dict(comments_, path) 111 | 112 | with open('./log', 'w', encoding='utf-8') as f: 113 | if not exceptions: 114 | f.write('All scraping tasks were terminated successfully.\n') 115 | else: 116 | f.write('Exist exceptions\n\n') 117 | for idx, e in exceptions: 118 | f.write('movie id = {}'.format(idx)) 119 | f.write('{}\n'.format(e)) 120 | 121 | 122 | if __name__ == '__main__': 123 | main() 124 | -------------------------------------------------------------------------------- /output/meta/134963.json: -------------------------------------------------------------------------------- 1 | { 2 | "movie_idx": 134963, 3 | "expert_score": 8.34, 4 | "netizen_score": 8.9, 5 | "title": "라라랜드", 6 | "e_title": "La La Land , 2016", 7 | "genres": [ 8 | "드라마", 9 | "뮤지컬", 10 | "멜로/로맨스" 11 | ], 12 | "countries": [ 13 | "미국" 14 | ], 15 | "running_time": 127, 16 | "open_date": [ 17 | "2017-12-08", 18 | "2016-12-07" 19 | ], 20 | "grade": "12세 관람가", 21 | "story": "황홀한 사랑, 순수한 희망, 격렬한 열정…\n이 곳에서 모든 감정이 폭발한다!\n꿈을 꾸는 사람들을 위한 별들의 도시 ‘라라랜드’.\n재즈 피아니스트 ‘세바스찬’(라이언 고슬링)과 배우 지망생 ‘미아’(엠마 스톤),\n인생에서 가장 빛나는 순간 만난 두 사람은\n미완성인 서로의 무대를 만들어가기 시작한다.\n제작노트 보기", 22 | "making_note": "ABOUT MOVIE 1.\n“이 영화는 마법이다”\n올 겨울, 당신의 꿈이 이루어지는 완벽한 경험\n2015년 충격에 가까운 전율을 선사한 영화 로 전 세계 영화상을 휩쓸며 천재적인 재능을 인정받은 다미엔 차젤레 감독이 신작으로 돌아왔다. 감독의 새 작품인 는 인생의 가장 빛나는 순간, 서로의 무대를 완성해가는 배우 지망생과 재즈 피아니스트를 통해 꿈을 좇는 청춘의 열정과 사랑을 그린 뮤직 로맨스로 올해 가장 황홀한 경험을 선사한다.\n인터뷰를 통해 다미엔 차젤레 감독은 “는 보다 먼저 만들고 싶었던 영화”라고 밝힌 바 있다. 이미 2006년 각본을 완성했지만 당시 신인이었던 그가 원하는 대로 영화를 만들기란 쉽지 않았고, 차선책으로 의 각본을 썼다. 절치부심으로 만든 이 작품의 흥행과 비평에서의 세계적인 성공에 힘입어 다미엔 차젤레 감독은 마침내 를 세상에 내보일 수 있었다.\n영화가 처음 공개된 뒤 영화비평사이트 로튼토마토에서 신선도 96%를 기록하는 등 전 세계 언론과 관객들의 끊임없는 극찬이 우후죽순 쏟아졌다. 또한 세계 유수 영화제에 연달아 초청되면서 개봉 전부터 화제의 중심이 되고 있다. 제73회 베니스영화제 개막작으로 선정되어 엠마 스톤이 여우주연상을 수상하고, 제41회 토론토국제영화제에서 관객상을 수상한 데 이어 제52회 시카고국제영화제 개막작으로도 선정되었다. 국내에서도 올해 부산국제영화제에 초청되어 예매 오픈 1분만에 매진을 이루며 뜨거운 관심 속에 상영된 뒤 그야말로 폭발적인 반응을 이끌어냈다.\n는 현실적인 공감을 얻을 이야기를 바탕으로 아름다운 LA의 사계절 아래 감미로운 선율의 음악과 다채로운 색채의 향연, 화려한 의상과 최고의 프로덕션을 선사하며 관객들을 황홀경으로 이끈다. 다미엔 차젤레 감독은 다양한 고전 영화에 대한 오마주는 물론 할리우드를 향한 애정과 존경을 영화 전반에 걸쳐 가감 없이 드러냈다. 또한 1950년대 할리우드 영화들이 많이 사용하던 2.55:1 비율의 시네마스코프 사이즈로 촬영해 고전 영화적인 느낌을 살렸다.\n특히 이 와이드스크린의 화면 비율은 일반적인 화면들 보다 가로로 길어진 만큼 많은 정보를 넣어야 하다 보니 더욱 많은 공이 들어갈 수 밖에 없는 작업이다. 하지만 또 이 때문에 더욱 웅장한 배경을 담아낼 수 있었고, 이는 화려한 배경들이 대거 등장하는 의 독특한 영상미를 선보이는 데 적합한 환경이 되었다.\n마치 한 편의 잘 짜인 고전 영화를 연상시키는 는 고전이 주는 황홀한 매력과 감정에 대한 서정시이자, 21세기 새로운 클래식을 창조할 영화라는 찬사 속에 2017년 아카데미 작품상과 감독상, 주연상 등 주요부문의 수상이 점쳐지고 있다.\nABOUT MOVIE 2.\n베니스영화제 여우주연상 엠마 스톤&아카데미 노미네이트 라이언 고슬링\n아카데미 남우조연상 J.K.시몬스, R&B 소울의 대가 존 레전드까지\n재즈 피아니스트와 배우 지망생으로 분한 엠마 스톤과 라이언 고슬링은 열정을 가득 안고 함께 꿈을 좇으며 사랑을 키워나가는 커플로 열연을 펼친다. 이들이 만들어낸 환상적인 시너지는 아름다운 멜로디가 가득한 신나는 무대 위를 물들인다.\n으로 지난해 아카데미 등 유수의 영화제에서 여우조연상에 노미네이트 되었던 엠마 스톤이 배우 지망생 ‘미아’ 역을 맡아 열연을 펼쳤다. 미아는 할리우드와 연기에 대한 열정으로 가득 찬 인물로 계속되는 실패에 좌절하고 다시 일어서며 꿈을 이루기 위해 끊임없이 노력한다. 엠마 스톤은 이 역할을 위해 오랜 시간 동안 철저한 준비를 했고 노래와 탭댄스, 왈츠까지 완벽하게 소화해내며 제73회 베니스영화제 여우주연상을 수상했다. 다미엔 차젤레 감독은 “엠마 스톤이 선보이는 연기와 노래, 춤의 수준, 그리고 감정의 변화를 표현하는 방식은 그저 놀라울 뿐이다. 현 시대 가장 훌륭한 여배우 중 하나임에 틀림없다”라고 극찬을 표했다.\n, 의 라이언 고슬링이 곧은 신념을 지닌 재즈 피아니스트 ‘세바스찬’으로 분했다. 그는 재즈의 전통이 사라지는 것을 안타까워하며 언젠가는 재즈를 부활시키고, 자신의 클럽을 차리겠다는 꿈을 가지고 살아간다. 특히 라이언 고슬링은 몇 개월 동안 피아노 연습에만 매진해 모든 피아노 연주를 대역 없이 소화해내 감탄을 자아냈다. 함께 출연한 세계적인 톱가수 존 레전드는 “질투가 났다. 그가 연주하는 걸 보고 있으면 감탄이 흘러나왔다. 지난 몇 개월 동안 피아노를 배웠을 뿐인데, 굉장히 놀라운 일이다”라고 평했다. 라이언 고슬링 역시 노래와 탭댄스를 연마해 엠마 스톤과 함께 완벽한 무대를 선보인다.\n10번의 그래미 어워드 수상, 제87회 아카데미 주제가상에 빛나는 존 레전드가 로 첫 메이저 영화에 도전한다. 극 중 라이언 고슬링의 친구이자 재즈 스타 ‘키이스’로 분한 존 레전드는 본업이 배우라고 해도 믿어질 정도로 안정적인 연기를 펼쳤다. 의 제작자 프레드 버거는 “처음 캐스팅 소식을 듣고 믿을 수 없었다. 그는 모든 면에서 훌륭했다”고 극찬을 아끼지 않았다.\n의 폭군 선생 J.K. 시몬스가 라이언 고슬링을 가차없이 해고하는 레스토랑의 사장으로 깜짝 출연한다. 극중 “프리 재즈가 싫다”는 대사로, 를 절묘하게 패러디해 관객들의 웃음을 자아낸다.\nABOUT MOVIE 3.\n라이브로 부른 배우들의 노래, 오감만족 뮤직 로맨스\n를 잇는 음악영화 열풍\n의 가장 큰 매력 포인트 중 하나는 바로 듣는 순간 뇌리에 꽂혀 잊혀지지 않는 음악이다. 이미 개봉 전부터 영화에 삽입된 곡들이 예고편을 통해 공개된 후, 온라인에서 엄청난 화제를 일으키고 있어 , 에 이어 또 한 번 대한민국에 음악영화 열풍을 예고하고 있다.\n주연 배우인 라이언 고슬링이 부른 ‘City of stars’와 엠마 스톤이 부른 ‘Audition’은 감미로운 멜로디에 서정적인 가사가 더해져 단숨에 관객들의 마음을 사로잡았다. 이 두 곡은 영화에서 가장 중요한 순간마다 흐르며 극의 분위기를 이끌어간다.\n다미엔 차젤레 감독은 배우들이 그 순간에 완벽하게 존재하도록 이 두 곡을 모두 현장에서 라이브로 진행해 더욱 세밀하게 감정을 담아낼 수 있었다. 뿐만 아니라 존 레전드는 최고의 R&B 싱어송라이터인 만큼 OST에 직접 참여했는데, 극중 그가 맡은 역할인 키이스가 속한 밴드의 대표곡인 ‘Start a fire’라는 곡을 선보인다.\n다미엔 차젤레 감독은 “를 통해 음악과 노래, 춤에 대해 이야기하고 싶었다. 뮤지컬은 꿈과 현실 사이의 균형잡기를 표현하기에 더할 나위 없는 장르라고 생각한다”라고 말했다. 그는 고전 뮤지컬 영화의 매력을 아이폰과 유튜브 세대의 관객들에게 어필하기 위해 안무에도 중점을 두었다. 안무를 맡은 맨디 무어는 “그가 가진 엄청난 지식에 매료됐다. 다미엔 차젤레만큼 뮤지컬 장르에 대해 자세히 알고 있는 감독은 어디에도 없을 것이다”라고 감탄을 표했다.\nPRODUCTION NOTE 1.\nLA의 사계절을 담은 40일간의 로케이션\n50벌의 주문 제작, 고전미 넘치는 특별한 의상이 주는 아름다움\n는 꿈과 사랑, 열정과 희망이 가득한 영화임과 동시에 LA라는 도시에 대한 송가이기도 하다. 다미엔 차젤레 감독은 영화를 봄, 여름, 가을, 겨울, 총 4개의 챕터로 구성해 40일 동안 LA의 사계절 구석구석을 빠짐없이 담아냈다.\n배우와 스탭들은 1949년에 첫 문을 연 재즈 클럽과, 레돈도 해변의 역사적인 라이트하우스 카페, 그리피스 공원 천문대와 같은 전설적인 장소에서 경외심에 말을 잃었다. 시간은 현대를 배경으로 하지만 속 모든 장소들은 과거와 현재를 오간다.\n“창의력이 넘치는 감독과 함께 새롭게 LA를 볼 수 있는 기회였다. 때문에 아직 보여지지 않은 면을 발굴하려 노력했다. 왼쪽에는 1940년대의 할리우드가, 오른쪽에는 2016년이 기다리고 있는 셈이다. 도시 자체에 깃든 보편성을 이용하는 것은 감독의 생각이었다”고 프로덕션 디자이너 데이비드 와스코는 말했다.\n또한 다미엔 차젤레 감독은 감정의 매개물인 색에 완전히 집중했다. 노란색을 가장 강조하는 동시에 영화 속 남성들은 대체로 흑백으로, 여성들은 컬러로 색을 입힘으로써 장면을 중립적으로 만들기 위해 노력했다.\n의상도 마찬가지였다. 라이언 고슬링이 연기하는 세바스찬은 그의 성격처럼 특별한 고유성과 전통과 형식에 대한 존경이 느껴지도록, 거의 모두 주문 제작한 의상만을 고집했다. 의상 디자이너 메리 조프레즈는 50벌이 넘는 두 주연 배우의 의상을 주문 제작해 의상의 색채만으로도 캐릭터의 심리를 표현하는 경지를 선보인다. 특히 미아를 연기한 엠마 스톤에 대해 극찬했는데, “더할 나위 없는 뮤즈”라면서 “너무 사랑스러웠다. 마치 클래식 뮤지컬 속 여자 주인공 같이 빈티지부터 원색의 드레스까지 모든 의상을 완벽하게 소화해냈다”고 전하기도 했다.\nPRODUCTION NOTE 2.\n단 한 번의 촬영으로 완성한 역대급 오프닝 장면\n별들의 도시 위 실제로 벌어진 마법 같은 무대\n눈을 즐겁게 하는 화려하고 다채로운 색의 향연\n는 마치 금방이라도 튀어나올 듯한 생동감과 눈을 황홀하게 하는 다채로운 색상들을 스크린 위에 펼쳐놓는다. 다미엔 차젤레 감독은 예전부터 마음 속에 간직하고 있던 장면을 실제로 구현하기 위해 과 의 촬영 감독인 라이너스 산드그렌과 작업에 임했다. 그는 “다미엔 차젤레의 촬영에 대한 아이디어는 일반 영화의 상식을 훨씬 초월하는 것이었고, 굉장히 매력적이었다”고 촬영 소감을 밝혔다.\n다미엔 차젤레 감독은 모든 것이 촬영 현장에서 이루어지길 바랐고, 특수효과를 추가하고 싶어하지 않았다. 때문에 모든 스탭들은 엄청나게 많은 계획을 세워야 했고, 실현 가능한 무대를 만들기 위해 끊임없이 노력해야 했다.\n특히 그는 영화 고유의 마법과 같은 파란 밤하늘로 빛나는 밤장면을 실제로 담아내고자 했다. 이에 라이너스 산드그렌은 그러한 감독의 요구에 따라 쿨블루, 그린, 핑크를 강조하기 위해 색광 퍼레이드를 펼치기도 했다.\n영화 속 가장 인상적인 장면 중 하나인 오프닝은 LA의 한 고속도로에서 완성됐다. 촬영할 수 있는 기회가 제한되어 있었기 때문에 어떠한 실수도 용납되지 않았고, 3개월에 걸친 연습과 무한 반복되는 리허설을 통해 결국 단 한 번의 촬영으로 완벽한 장면을 만들어냈다.\n두 주인공이 도시를 내려다보는 언덕에서 탭댄스를 추는 장면은 서로에게 한 발짝 다가서며 처음으로 빠져드는 중요한 장면이었다. 감독과 배우들은 미리 충분한 대화를 거쳤고, 결국 6분 동안의 원테이크 촬영으로 특별한 장면을 완성했다.\n플라네타리움에서 두 주인공이 왈츠를 추는 장면 또한 가히 압도적이다. 제작진은 세바스찬과 미아가 아름다운 왈츠에 빠지는 순간 관객들도 함께 빠지길 원했다. 와이어에 매달려 허공에서 두 사람이 왈츠를 추는 이 장면을 위해 특별히 카메라 워크에 신경을 써야 했다.", 23 | "box_office": -1 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 네이버 영화 정보 및 사용자 작성 영화평/평점 데이터 수집기 2 | 3 | ## Install 4 | 5 | ``` 6 | git clone https://github.com/lovit/naver_movie_scraper 7 | cd naver_movie_scraper 8 | python setup.py install 9 | ``` 10 | 11 | ## Script 를 이용한 데이터 수집 12 | 13 | CLI 를 이용하여 위의 함수들을 모두 이용할 수 있습니다. 사용 가능한 arguments 는 아래와 같습니다. 14 | 15 | | Argument | Type | Default | Description | 16 | | --- | --- | --- | --- | 17 | | output | str | './output/' | Output directory | 18 | | begin_idx | int | 134963 | Index of first movie | 19 | | end_idx | int | 134963 | Index of last movie | 20 | | specific_idx | str | '' | Index of specific movies white space separated idx
eg. 134963 10100 | 21 | | limit | int | -1 | Page limitation for comments & best scripts | 22 | | sleep | float | 0.1 | Sleep time per each page in comments & best scripts | 23 | | basic | store_true | False | If use, scrap basic meta | 24 | | casting | store_true | False | If use, scrap castings | 25 | | comments | store_true | False | If use, scrap comments | 26 | | bestscripts | store_true | False | If use, scrap best scripts | 27 | | debug | store_true | False | If use, it sets limit as '3' | 28 | | fast_update | store_true | False | If use, it stops when finding existing comments | 29 | 30 | ``` 31 | naver_movie_scraper --output ./output/ --begin_idx 134963 --end_idx 134963 --specific_idx '' --sleep 0.1 --basic --casting --comments --bestscripts --debug 32 | ``` 33 | 34 | ``` 35 | naver_movie_scraper --output ./naver_movie/ --specific_idx 38899 134963 --comments --debug 36 | ``` 37 | 38 | 39 | 위 코드를 실행시키면 현재 폴더 아래 `output` 가 생성되며, 각각의 하위 폴더에 해당 정보들이 수집됩니다. 40 | 41 | ``` 42 | |-- output 43 | |-- actors 44 | |-- bestscripts 45 | |-- comments 46 | |-- directors 47 | |-- meta 48 | |-- staffs 49 | ``` 50 | 51 | ## Components of scraper 52 | 53 | `naver_movie_scraper` 폴더는 다음의 파일들로 구성되어 있습니다. 54 | 55 | | File | Note | Function | 56 | | --- | --- | --- | 57 | | basic.py | 메인 화면에서 영화 이름, 장르 등의 정보를 수집 | scrap_basic | 58 | | comments.py | 사용자 작성 댓글과 평점을 수집 | scrap_comments | 59 | | detail.py | 감독, 배우, 스탭 정보를 수집 | scrap_casting | 60 | | script.py | 명대사를 수집 | scrap_bestscripts | 61 | | utils.py | utils | get_soup
text_normalize
save_list_of_dict
load_list_of_dict | 62 | 63 | 영화 `라라랜드` 의 정보를 수집하는 예시로 사용법을 설명합니다. 라라랜드의 영화 아이디는 `134963` 입니다. 64 | 65 | ## scrap_basic 66 | 67 | | Argument | Type | Note | 68 | | --- | --- | --- | 69 | | idx | int or str | 영화 아이디 | 70 | 71 | ```python 72 | from naver_movie_scraper import scrap_basic 73 | 74 | idx = 134963 75 | scrap_basic(idx) 76 | ``` 77 | 78 | Return 값은 dict 형식입니다. 79 | 80 | | Key | Type | Example value | Note | 81 | | --- | --- | --- | --- | 82 | | grade | str | '12세 관람가' | . | 83 | | movie_idx | int | 134963 | . | 84 | | title | str | '라라랜드' | . | 85 | | e_title | str | 'La La Land , 2016' | 영어 제목에는 연도가 포함됨 | 86 | | genres | list of str | ['멜로/로맨스', '뮤지컬', '드라마'] | 여러 개의 장르일 수 있음 | 87 | | countries | list of str | ['미국'] | 여러 나라에서 공동 제작할 수 있음 | 88 | | expert_score | float | 8.34 | 전문가 평점 | 89 | | netizen_score | float | 8.9 | 네티즌이 작성한 장문의 리뷰 평균 평점 | 90 | | open_date | list of str | ['2017-12-08', '2016-12-07'] | 재개봉 날짜가 포함될 수 있음 | 91 | | running_time | int | 127 | . | 92 | | story | str | 황홀한 사랑, 순수한 희망, 격렬한 열정... | 줄거리 | 93 | | making_note | str | ABOUT MOVIE 1.
“이 영화는 마법이다”
올 겨울, 당신의 꿈이 이루어지는 ... | 매이킹 노트 | 94 | | box_office | str | '3,598,929명(01.17 기준)' | 영화진흥위원회에서 제공하는 국내 관객 수 | 95 | 96 | ``` 97 | {'box_office': '3,598,929명(01.17 기준)' 98 | 'countries': ['미국'], 99 | 'e_title': 'La La Land , 2016', 100 | 'expert_score': 8.34, 101 | 'genres': ['멜로/로맨스', '뮤지컬', '드라마'], 102 | 'grade': '12세 관람가', 103 | 'making_note': 'ABOUT MOVIE 1.\n“이 영화는 마법이다”\n올 겨울, 당...', 104 | 'movie_idx': 134963, 105 | 'netizen_score': 8.9, 106 | 'open_date': ['2017-12-08', '2016-12-07'], 107 | 'running_time': 127, 108 | 'story': '황홀한 사랑, 순수한 희망, 격렬한 열정…\n이 곳에서 모든...', 109 | 'title': '라라랜드'} 110 | ``` 111 | 112 | ## scrap_comments 113 | 114 | | Argument | Type | Note | 115 | | --- | --- | --- | 116 | | idx | int or str | 영화 아이디 | 117 | | limit | int | 명대사를 수집하는 페이지의 개수를 설정할 수 있습니다.
-1 이면 모든 페이지로부터 수집합니다.
기본값은 -1 입니다. | 118 | | sleep | float | 과도한 데이터 수집은 서버에 부하를 줍니다.
sleep 은 수집하는 페이지마다 쉬는 간격입니다.
**sleep 을 작게 설정할 경우, 서버로부터 접근이 차단될 수 있습니다.**
여유가 되는대로 큰 값 (0.5 이상)으로 설정하십시요.| 119 | 120 | ```python 121 | from naver_movie_scraper import scrap_comments 122 | 123 | scrap_comments(idx, limit=3) 124 | ``` 125 | 126 | 이전의 평점 시스템에 존재하던 (개봉 전, 개봉 후) 구분은 사라졌습니다. 대신 평점 시스템에 `관람객` 마커를 다는 기능과 `스포일러가 포함된 댓글입니다` 라는 마커를 다는 기능이 추가되었습니다. 업데이트 된 스크래퍼에는 이들을 제거하여 데이터를 수집합니다. 또한 댓글없이 평점만 부여하는 글들이 다수 존재합니다. 이들은 `text` 의 값이 빈 str 입니다. 수집 상황을 20 페이지 단위로 프린트 합니다. 127 | 128 | ``` 129 | movie 134963, after, 3 / 3 done 130 | movie 134963, before, 3 / 3 done 131 | ``` 132 | 133 | Return 값은 list of dict 형식입니다. 134 | 135 | | Key | Type | Example value | Note | 136 | | --- | --- | --- | --- | 137 | 138 | ``` 139 | [{'agree': 0, 140 | 'disagree': 0, 141 | 'score': 10, 142 | 'text': '꿈꾸는 모든 사람들, 그리고 그들과 삶의 화해를 위하여', 143 | 'idx': '15137658', 144 | 'user': 'abcd****', 145 | 'written_at': '2019.01.18 13:45'}, 146 | ``` 147 | 148 | ## scrap_casting 149 | 150 | | Argument | Type | Note | 151 | | --- | --- | --- | 152 | | idx | int or str | 영화 아이디 | 153 | 154 | ```python 155 | from naver_movie_scraper import scrap_casting 156 | 157 | castings = scrap_casting(idx) 158 | castings.keys() 159 | ``` 160 | 161 | Return 값은 list of dict 형식입니다. scrap_casting 의 return 값은 `actors`, `directors`, `staffs` 세 종류의 캐스팅 정보가 포함되어 있습니다. 162 | 163 | ``` 164 | dict_keys(['directors', 'actors', 'staffs']) 165 | ``` 166 | 167 | ### actors 168 | 169 | `actors` 는 배우 캐스팅에 관련된 정보입니다. 170 | 171 | | Key | Type | Example value | Note | 172 | | --- | --- | --- | --- | 173 | | id | int | 5751 | . | 174 | | k_name | str | '라이언 고슬링' | 한국 이름 | 175 | | e_name | str | 'Ryan Gosling' | 영어 이름 | 176 | | part | str | '주연' | 주연 혹은 조연 | 177 | | role | str | '세바스찬 역' | 극중 역 이름 | 178 | | casting_order | int | 1 | 숫자가 작을수록 영화 내에서 비중이 큰 역할 | 179 | 180 | ```python 181 | scrap_casting(idx)['actors'] 182 | ``` 183 | 184 | `라라랜드`의 두 주인공 `라이언 고슬링`과 `엠마 스톤`의 정보가 각각 dict 형태로 저장되어 있으며, 이들은 list 로 정렬되어 있습니다. 185 | 186 | ``` 187 | [{'casting_order': 1, 188 | 'e_name': 'Ryan Gosling', 189 | 'id': 5751, 190 | 'k_name': '라이언 고슬링', 191 | 'part': '주연', 192 | 'role': '세바스찬 역'}, 193 | {'casting_order': 2, 194 | 'e_name': 'Emma Stone', 195 | 'id': 135256, 196 | 'k_name': '엠마 스톤', 197 | 'part': '주연', 198 | 'role': '미아 역'}, 199 | ... 200 | ``` 201 | 202 | ### directors 203 | 204 | `directors` 는 감독 캐스팅에 관련된 정보입니다. 205 | 206 | | Key | Type | Example value | Note | 207 | | --- | --- | --- | --- | 208 | | id | int | 175108 | . | 209 | | k_name | str | '데이미언 셔젤' | 한국 이름 | 210 | | e_name | str | 'Damien Chazelle' | 영어 이름 | 211 | 212 | ```python 213 | scrap_casting(idx)['directors'] 214 | ``` 215 | 216 | ``` 217 | [{'e_name': 'Damien Chazelle', 'id': 175108, 'k_name': '데이미언 셔젤'}] 218 | ``` 219 | 220 | ### staffs 221 | 222 | `staffs` 는 스탭 혹은 우정출연 캐스팅에 관련된 정보입니다. 223 | 224 | | Key | Type | Example value | Note | 225 | | --- | --- | --- | --- | 226 | | Key | Type | Example value | Note | 227 | | id | int | 367641 | . | 228 | | k_name | str | '저스틴 허위츠' | 한국 이름 | 229 | | e_name | str | 'Justin Hurwitz' | 영어 이름 | 230 | | role | str | '음악' | 영화의 역할로, 조연이 아닐 경우 제작, 음악, 기획 등의 값이 부여됨 | 231 | 232 | ```python 233 | scrap_casting(idx)['staffs'] 234 | ``` 235 | 236 | ``` 237 | [{'e_name': '도로위 댄서', 'id': 395074, 'k_name': '레시마 게이자', 'role': ''}, 238 | {'e_name': '유명한 여배우', 'id': 150418, 'k_name': '에이미 콘', 'role': ''}, 239 | ... 240 | {'e_name': 'Fred Berger', 'id': 194407, 'k_name': '프레드 버거', 'role': '제작'}, 241 | {'e_name': 'Gary Gilbert', 'id': 50303, 'k_name': '게리 길버트', 'role': '제작'}, 242 | ... 243 | {'e_name': 'Justin Hurwitz', 'id': 367641, 'k_name': '저스틴 허위츠', 'role': '음악'}, 244 | ... 245 | ] 246 | ``` 247 | 248 | ## scrap_bestscripts 249 | 250 | | Argument | Type | Note | 251 | | --- | --- | --- | 252 | | idx | int or str | 영화 아이디 | 253 | | limit | int | 명대사를 수집하는 페이지의 개수를 설정할 수 있습니다.
-1 이면 모든 페이지로부터 수집합니다.
기본값은 -1 입니다. | 254 | | sleep | float | 과도한 데이터 수집은 서버에 부하를 줍니다.
sleep 은 수집하는 페이지마다 쉬는 간격입니다.
**sleep 을 작게 설정할 경우, 서버로부터 접근이 차단될 수 있습니다.**
여유가 되는대로 큰 값 (0.5 이상)으로 설정하십시요.| 255 | 256 | ```python 257 | from naver_movie_scraper import scrap_bestscripts 258 | 259 | scrap_bestscripts(idx, limit=3) 260 | ``` 261 | 262 | list of dict 형식의 값을 return 합니다. 263 | 264 | | Key | Type | Example value | Note | 265 | | --- | --- | --- | --- | 266 | | chracter | str | '세바스찬라이언 고슬링' | 대사의 주인공 | 267 | | description | str | '우리 지금 어디 있는 거야?' | 사용자가 기술한 상황 혹은 메모 | 268 | | num_agree | int | 403 | 사용자 동의 개수 | 269 | | text | str | '그냥 흘러가는 대로 가보자.' | 영화 대사 | 270 | 271 | ``` 272 | [{'chracter': '세바스찬라이언 고슬링', 273 | 'description': '기습심쿵♡.♡', 274 | 'num_agree': 571, 275 | 'text': '도서관 앞이라고 했잖아'}, 276 | {'chracter': '세바스찬라이언 고슬링', 277 | 'description': '미아 기다릴때 경적소리 ㅎㅎ', 278 | 'num_agree': 405, 279 | 'text': '빠아앙아앙아아아앙~~~'}, 280 | {'chracter': '세바스찬라이언 고슬링', 281 | 'description': '우리 지금 어디 있는 거야?', 282 | 'num_agree': 403, 283 | 'text': '그냥 흘러가는 대로 가보자.'}, 284 | ... 285 | ] 286 | ``` 287 | --------------------------------------------------------------------------------