├── 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('