Весенний проект по курсу «Наука о данных», Совместный бакалавриат ВШЭ-РЭШ, 2018-19 учебный год.
Автор работы: Анна Севостьянова.
Этот проект позволяет автоматически производить продвинутый поиск по различным критериям по сайту IMDb. Затем при помощи API программа собирает данные в dataframe и анализирует их с использованием различных математических функций, регрессий и визуализации. Ну и в конце можно поболтать с телеграм-ботом.
Советую проект читать по порядку, к нему везде даются текстовые пояснения.
Использованные возможности: продвинутый веб-скреппинг при помощи Selenium, выгрузка баз данных при помощи OMDb API, выгрузка данных при помощи BeutifulSoup, продвинутые возможности Pandas и Numpy, несколько видов визуализации данных при помощи Plotly, разговаривающий telegram-bot, другие вохможности Python (например, регрессии при помощи библиотеки statsmodels).
В дальнейшем в проекте будут присутствовать текстовые комментарии для более простой навигации.
Сначала импортируем все необходимые библиотеки:
from selenium import webdriver
import requests
from bs4 import BeautifulSoup
import time
import math
import numpy as np
import pandas as pd
import seaborn as sns
import statsmodels.api as sm
import plotly_express as px
import plotly.plotly as py
import plotly.graph_objs as go
import plotly
import matplotlib.pyplot as plt
plotly.tools.set_credentials_file(username='<…>', api_key='<…>')
# тут нужно вставить username и api_key для plotly
Продвинутый поиск с Selenium, позволяющий по выбору клиента отбирать итоговый список фильмов по желаемым критериям. Поиск проходит на странице "https://www.imdb.com/search/title"
#title - text
#min_date - text in format YYYY or YYYY-MM-DD
#max_date - text in format YYYY or YYYY-MM-DD
#companies - list of companies
#genres - list of genres
def advanced_search(browser, title = None, min_date = None, max_date = None, companies = None, genres = None):
main_field = browser.find_element_by_id('main')
#only films
featured_film = main_field.find_element_by_id("title_type-1")
featured_film.click()
TV_Movie = main_field.find_element_by_id("title_type-2")
TV_Movie.click()
#choosing title
if title!= None:
title_field = main_field.find_element_by_name("title")
title_field.clear()
title_field.send_keys(title)
#starting date of film resease
if min_date!= None:
release_date_min_field = browser.find_element_by_name("release_date-min") #год начала поиска
release_date_min_field.clear()
release_date_min_field.send_keys(min_date)
#final date of film resease
if max_date != None:
release_date_max_field = browser.find_element_by_name("release_date-max") #год конца поиска
release_date_max_field.clear()
release_date_max_field.send_keys(max_date)
#choosing filming company
dict_of_companies = {"20th Century Fox":'companies-1',"Sony":'companies-2',"DreamWorks":'companies-3',"MGM":'companies-4',"Paramount":'companies-5', "Universal":'companies-6',"Walt Disney":'companies-7',"Warner Bros.":'companies-8'}
if companies != None:
for company in companies:
checkbox = browser.find_element_by_id(dict_of_companies[company])
checkbox.click()
#choosing genres
dict_of_genres = {"Action":'genres-1',"Adventure":'genres-2',"Animation":'genres-3', "Biography":'genres-4',"Comedy":'genres-5',"Crime":'genres-6',"Documentary":'genres-7',"Drama":'genres-8',"Family":'genres-9',"Fantasy":'genres-10',"Film-Noir":'genres-11',"Game-Show":'genres-12',"History":'genres-13',"Horror":'genres-14',"Music":'genres-15',"Musical":'genres-16',"Mystery":'genres-17',"News":'genres-18',"Reality-TV":'genres-19',"Romance":'genres-20',"Sci-Fi":'genres-21',"Sport":'genres-22',"Talk-Show":'genres-23',"Thriller":'genres-24',"War":'genres-25',"Western":'genres-26'}
if genres != None:
for genre in genres:
checkbox = browser.find_element_by_id(dict_of_genres[genre])
checkbox.click()
#submitting our choice
submit_button = main_field.find_element_by_tag_name('button')
submit_button.click()
time.sleep(5)
Мини-функция, кликающая "next page":
def next_page(browser):
main_field = browser.find_element_by_id('main')
next_button = main_field.find_element_by_xpath('//*[@id="main"]/div/div[4]/a')
next_button.click()
time.sleep(5)
Мини-функция, возвращающая список из двух элементов: первый - количество фильмов в итоговом поиске после вашего запроса, вторая - сколько страниц IMDb потребуется чтобы показать их все (в данной функции используются возможности BeautifulSoup):
def how_many_films_and_pages(browser):
#Start of BeautifulSoup
current_address = browser.current_url
r = requests.get(current_address)
page = BeautifulSoup(r.text, 'html.parser')
all_spans = page.html.body.findAll('span')
how_many_string = all_spans[20].string.strip()
if(len(how_many_string) > 10):
number_of_films = int(how_many_string[8:-8])
number_of_pages = math.ceil(number_of_films/50)
else:
number_of_films = int(how_many_string[:-8])
number_of_pages = 1
return([number_of_films, number_of_pages])
Функция, возращающая список из двух списков: первый - список всех названий фильмов на конкретной странице, второй - список соответствующих им ссылок на страницы.
def parsing_all_titles_from_the_page(browser, list_of_titles, list_of_links, num_of_films_on_the_page):
current_address = browser.current_url
r = requests.get(current_address)
page = BeautifulSoup(r.text, 'html.parser')
all_h3 = page.html.body.findAll('h3')
for h3 in all_h3[:num_of_films_on_the_page]:
film_title = h3.a.string.strip()
list_of_titles.append(film_title)
film_link_0 = h3.a['href'].strip()
film_link="http://www.imdb.com"+film_link_0
list_of_links.append(film_link)
return [list_of_titles, list_of_links]
Функция, возвращающая список названий и ссылок для всех фильмов, которые были найдены по вашему запросу (чтобы в дальнейшем использоваться OMDb API):
def creating_list_of_titles(browser, list_of_titles, list_of_links):
number_of_films_and_pages = how_many_films_and_pages(browser)
if (number_of_films_and_pages[1]-1) ==0:
result_titles = parsing_all_titles_from_the_page(browser, list_of_titles, list_of_links, number_of_films_and_pages[0])
else:
for page in range(number_of_films_and_pages[1]-1):
result_titles = parsing_all_titles_from_the_page(browser, list_of_titles, list_of_links, 50)
next_page(browser)
num_of_last_page_films = number_of_films_and_pages[0]%50
result_titles = parsing_all_titles_from_the_page(browser, list_of_titles, list_of_links, num_of_last_page_films)
return result_titles
Чтобы использовать данное API, нужно иметь код (который находится в открытом доступе). Его можно получить на сайте: https://www.omdbapi.com/apikey.aspx
Код, вставленный в данный момент в строку url ниже дает 1000 использований API в день. Поэтому советую быть осторожнее, иначе придется получать новый ключ.
Как работает это API? Оно выгружает все возможные данные, получая на вход название фильма на английском языке.
def get_info_from_API_by_title(title):
url = 'http://www.omdbapi.com/?i=tt3896198&apikey=31001941'
params = {'t': title, 'format': 'json'}
data = requests.get(url, params = params)
if (data.json()['Response'] == 'True'):
if (data.json()['Title']!= 'N/A'):
Title = data.json()['Title']
else:
Title = 'N/A'
if (data.json()['Year']!= 'N/A'):
Year = int(float(data.json()['Year']))
else:
Year = 'N/A'
if (data.json()['Rated'] != 'N/A'):
Rated = data.json()['Rated']
else:
Rated = 'N/A'
months = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5,'Jun': 6, 'Jul':7, 'Aug':8, 'Sep':9,'Oct':10,'Nov':11,'Dec':12}
if (data.json()['Released'] != 'N/A'):
Released = data.json()['Released'].strip().split(' ')
Released_date = [int(float(Released[2])), months[Released[1]],int(Released[0][0])*10+int(Released[0][1])]
else:
Released_date = 'N/A'
if (data.json()['DVD'] != 'N/A'):
DVD = data.json()['DVD'].strip().split(' ')
DVD_date = [int(DVD[2]), months[DVD[1]],int(DVD[0][0])*10+int(DVD[0][1])]
else:
DVD_date = 'N/A'
if (data.json()['Runtime'] != 'N/A'):
Runtime = int(data.json()['Runtime'].strip()[:-4])
else:
Runtime = 'N/A'
if (data.json()['Genre'] != 'N/A'):
Genre = data.json()['Genre']
else:
Genre = 'N/A'
if (data.json()['Director'] != 'N/A'):
Director = data.json()['Director'].strip().split(',')
Director_list = []
for director in Director:
director = director.strip()
Director_list.append(director)
else:
Director_list = 'N/A'
if (data.json()['Writer'] != 'N/A'):
Writer = data.json()['Writer'].strip().split(',')
Writer_list = []
for writer in Writer:
writer = writer.strip()
Writer_list.append(writer)
else:
Writer_list = 'N/A'
if (data.json()['Actors'] != 'N/A'):
Actors = data.json()['Actors'].strip().split(',')
Actors_list = []
for actors in Actors:
actors = actors.strip()
Actors_list.append(actors)
else:
Actors_list = 'N/A'
if (data.json()['Plot'] != 'N/A'):
Plot = data.json()['Plot']
else:
Plot = 'N/A'
if (data.json()['Awards'] != 'N/A'):
Awards = data.json()['Awards']
else:
Awards = 'N/A'
if (data.json()['Poster'] != 'N/A'):
Poster = data.json()['Poster']
else:
Poster = 'N/A'
if (data.json()['imdbRating'] != 'N/A'):
imdbRating = float(data.json()['imdbRating'])
else:
imdbRating = 'N/A'
if (data.json()['Metascore'] != 'N/A'):
Metascore = int(data.json()['Metascore'])
else:
Metascore = 'N/A'
if (data.json()['BoxOffice'] != 'N/A'):
BoxOffice = int(''.join(data.json()['BoxOffice'][1:].split(",")))
else:
BoxOffice = 'N/A'
result = [Year, Rated, Released_date, DVD_date, Runtime, Genre, Director_list, Writer_list, Actors_list, Plot, Awards, Poster, imdbRating, Metascore, BoxOffice, Title]
else:
result = ['N/A']*15
return result
Для проверки, что он действительно работает, можете протестировать при помощи функции ниже, вбив в него любое название фильма на английском (но только четко как фильм называется на сайте IMDb). Дата выгружается уже в том формате, который будет использваться программой впоследствии.
get_info_from_API_by_title('the Godfather')
[1972, 'R', [1972, 3, 24], [2001, 10, 9], 175, 'Crime, Drama', ['Francis Ford Coppola'], ['Mario Puzo (screenplay by)', 'Francis Ford Coppola (screenplay by)', 'Mario Puzo (based on the novel by)'], ['Marlon Brando', 'Al Pacino', 'James Caan', 'Richard S. Castellano'], 'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.', 'Won 3 Oscars. Another 24 wins & 28 nominations.', 'https://m.media-amazon.com/images/M/MV5BM2MyNjYxNmUtYTAwNi00MTYxLWJmNWYtYzZlODY3ZTk3OTFlXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_SX300.jpg', 9.2, 100, 'N/A', 'The Godfather']
Следующая функция будет использовать список фильмов, которые были выданы по вашим параметрам поиска, чтобы вытащить все возможные данные об этих фильмах при помощи OMDb API:
def parse_all_possible_data_to_lists(list_of_titles):
Titles_list = []
Years_list = []
Rated_list = []
Rated_dict = {}
Released_date_list = []
DVD_date_list = []
Runtime_list = []
Genre_list = []
Directors_list = []
Directors_dict = {}
Writers_list = []
Writers_dict = {}
Actors_list = []
Actors_dict = {}
Plot_list = []
Awards_list = []
Poster_list = []
imdbRating_list = []
Metascore_list = []
BoxOffice_list = []
for title in list_of_titles:
data = get_info_from_API_by_title(title)
Years_list.append(data[0])
Rated_list.append(data[1])
Rated_dict = plus_1_rated(Rated_dict,data[1])
Released_date_list.append(data[2])
DVD_date_list.append(data[3])
Runtime_list.append(data[4])
Genre_list.append(data[5])
Directors_list.append(data[6])
Directors_dict = plus_1(Directors_dict, data[6])
Writers_list.append(data[7])
Writers_dict = plus_1(Writers_dict, data[7])
Actors_list.append(data[8])
Actors_dict = plus_1(Actors_dict, data[8])
Plot_list.append(data[9])
Awards_list.append(data[10])
Poster_list.append(data[11])
imdbRating_list.append(data[12])
Metascore_list.append(data[13])
BoxOffice_list.append(data[14])
result = {'Titles_list': list_of_titles,
'Years_list': Years_list,
'Rated_list':Rated_list,
'Rated_dict': Rated_dict,
'Released_date_list': Released_date_list,
'DVD_date_list' : DVD_date_list,
'Runtime_list' : Runtime_list,
'Genre_list' : Genre_list,
'Directors_list' : Directors_list,
'Directors_dict' : Directors_dict,
'Writers_list' : Writers_list,
'Writers_dict' : Writers_dict,
'Actors_list' : Actors_list,
'Actors_dict' : Actors_dict,
'Plot_list' : Plot_list,
'Awards_list' : Awards_list,
'Poster_list' : Poster_list,
'imdbRating_list' : imdbRating_list,
'Metascore_list' : Metascore_list,
'BoxOffice_list' : BoxOffice_list}
return result
Две небольшие функции, использующиеся в данной функции парсинга, добавляющие элемент в словарь:
def plus_1(dictionary, element_list):
if (element_list != None):
for element in element_list:
element = element.split("(")[0].strip()
n = dictionary.get(element, "No such element")
if n=="No such element":
dictionary[element] = 1
else:
dictionary[element] = dictionary[element]+1
else:
dictionary = dictionary
return dictionary
def plus_1_rated(dictionary, element):
if (element != None):
n = dictionary.get(element, "No such element")
if n=="No such element":
dictionary[element] = 1
else:
dictionary[element] = dictionary[element]+1
else:
dictionary = dictionary
return dictionary
Огромный полученный словарь данных будет удобнее превратить в DataFrame для дальнейших манипуляций, также удалив из него все 'N/A' значения, а также добавив дополнительные столбцы. Именно этим и занимается функция ниже. При помощи различных возможностей Numpy и Pandas она преобразует используемый данные в DataFrame, также считая средневзвешенный индекс между показателями IMDb рейтинга и MetaScore (вы увидите их позже на визуализации), а также вычисляя из дат релиза и выхода фильма на DVD количество дней между этими событиями:
def convert_to_dataframe(data_set):
films_data_0 = pd.DataFrame([data_set['Years_list'],data_set['Rated_list'],data_set['Released_date_list'],data_set['DVD_date_list'],data_set['Runtime_list'],data_set['imdbRating_list'],data_set['Metascore_list'],data_set['BoxOffice_list']], index = ['Years','Rated','Released_date','DVD_date','Runtime', 'imdbRating', 'Metascore','BoxOffice'],columns = data_set['Titles_list'] )
films_data = films_data_0.T
films_data['Runtime'] = films_data['Runtime'].astype('int64')
films_data.replace('N/A', np.nan, inplace=True)
films_data.dropna(inplace=True)
films_data['weighted_Index'] = films_data['Metascore']/10*0.4+films_data['imdbRating']*0.6
Released_day = np.array([int(i[0])*365+int(i[1])*30+int(i[2]) for i in films_data['Released_date']])
DVD_day = np.array([int(i[0])*365+int(i[1])*30+int(i[2]) for i in films_data['DVD_date']])
films_data['days_from_release_to_DVD'] =DVD_day-Released_day
films_data['extra_names'] = films_data.index
return films_data
Итак, теперь мы познакомились с основными функциями проекта и их возможностями. Теперь можно перейти к использованию, анализу, результатам и их визуализации.
Как вы могли понять из функций выше, программа должна работать следующим образом: сначала клиент выбирает желаемые параметры для поиска фильмов, затем программа автоматически ищет их при помощи Selenium и собирает все названия, затем названия поступают в API и превращаются в данные по фильмам, а затем данные конвертируются в готовую для использования таблицу.
Действительно, поиск данных при помощи Selenium работает отлично - приняв вбитые вами пожелания, он способен вернуть соответствующий список всех нужных фильмов с их названиями и ссылками на страницы. Предлагаю протестировать функционал при помощи функции ниже (она автоматически запустит браузер, выгрузит данные и выдаст список. Но после использования не забудьте закрыть открывшееся окно браузера)
def check_Selenium(title, min_date, max_date, companies, genres):
#start safari
browser = webdriver.Safari()
ref = 'https://www.imdb.com/search/title'
browser.get(ref)
#start search
advanced_search(browser, title, min_date, max_date, companies, genres)
#parse all titles
list_of_titles = []
list_of_links = []
titles_and_links = creating_list_of_titles(browser, list_of_titles, list_of_links)
return titles_and_links[0]
#You can change desired function variables using the followign tips:
#title - text
#min_date - text in format YYYY or YYYY-MM-DD
#max_date - text in format YYYY or YYYY-MM-DD
#companies - list of companies
#genres - list of genres
check_Selenium(title = None, min_date ='2000', max_date = '2016', companies = ["DreamWorks"],genres = ["Animation"])
['Шрек', 'Лего. Фильм', 'Шрек 2', 'Мадагаскар', 'Подводная братва', 'Ранго', 'Приключения Тинтина: Тайна Единорога', 'Побег из курятника', 'Монстры против пришельцев', 'Уоллес и Громит: Проклятие кролика-оборотня', 'Дорога на Эльдорадо', 'Спирит: Душа прерий', 'Синдбад: Легенда семи морей', 'Призрак в доспехах 2: Невинность', 'My Little Pony: Smile']
Когда вы запустите функцию и получите результат, вы увидите, что названия фильмов выдаются на русском языке (или другом, если вы используете vpn). Данная функция встроена глубоко в сайт IMDb и не меняется даже если залогиниться и установить предпочитаемый язык английский.
Из-за этого использование API становится чуть более проблематичным, так как оно принимает только названия на английском . Таким образом, связь выгрузки с API в данной программе действительно работает , но, к сожалению, только в странах с официальным английским языком. Мы уже проверили, что работает как API, так и Selenium. Поэтому из-за недоразумения с языком предлагаю в дальнейшем просто использовать базовые списки названий фильмов для анализа и визуализации.
#Все анимационные фильмы DreamWorks с 01.01.2008 по 31.12.2018
DreamWorksAnimation2008_2018 = ['The Lego Movie','The Lego Batman Movie', 'Rango', 'The Adventures of Tintin', 'The Lego Ninjago Movie', 'Monsters vs. Aliens']
#Все анимационные фильмы Walt Disney с 01.01.2008 по 31.12.2018
DisneyAnimation2008_2018 = ['Ralph Breaks the Internet', 'Incredibles 2', 'Christopher Robin', 'Moana', 'Zootopia', 'Coco', 'Frozen', 'Tangled', 'Wreck-It Ralph', 'Brave', 'Inside Out', 'Toy Story 3', 'Up', 'Big Hero 6', 'WALL·E', 'Cars 3', 'The Princess and the Frog', 'G-Force', 'Bolt', 'Rango', 'Monsters University', 'Finding Dory', 'Frankenweenie', 'Cars 2', 'The Good Dinosaur', 'The Boxtrolls', 'Gnomeo & Juliet', 'Strange Magic', 'Winnie the Pooh', 'Mars Needs Moms', 'Planes', 'A Christmas Carol', 'The Peanuts Movie', 'Planes: Fire & Rescue', 'Secret of the Wings']
Теперь, наконец, переходим к анализу и визуализации. В данном проекте можно использовать две опции:
1). Получить данные с конкретного поискового запроса и увидеть по нему различные данные в динамике, а также проанализировать регрессионные зависимости переменных друг от друга
2). Сравнить даные с нескольких поисковых запросов в среднем и в динамике (например, сравнить средний BoxOffice у компаний Disney и DreamWorks за последние 10 лет).
Ниже 2 public переменные search. Если вы хотите исследовать один запрос, вводите желамые параметры только в search_1, если сравнить несколько запросов - то в search_1, search_2 и тд.
#Вы можете поменять переменные на любые значения из списка для анализа, или же аналогично создать свои списки
search_1 = DisneyAnimation2008_2018
search_2 = DreamWorksAnimation2008_2018
search_3 = None
search_4 = None
(особенно интересно для фильмов Тима Бертона и его приверженности дуэту Джони Деппа и Хелены Бонем Картер)
def most_often(list_of_titles):
data_dict = parse_all_possible_data_to_lists(list_of_titles)
directors = data_dict['Directors_dict']
print("Popular directors (directed more than once): ")
for director in directors:
if directors[director] > 1:
print(director, " - ", directors[director])
print(" ")
actors = data_dict['Actors_dict']
print("Popular actors (played more than once): ")
for actor in actors:
if actors[actor] > 1:
print(actor, " - ", actors[actor])
print(" ")
writers = data_dict['Writers_dict']
print("Popular writers (wrote more than twice): ")
for writer in writers:
if writers[writer] > 2:
print(writer, " - " ,writers[writer])
print(" ")
most_often(search_1)
Popular directors (directed more than once): Rich Moore - 3 Ron Clements - 2 John Musker - 2 Don Hall - 3 Chris Williams - 3 Byron Howard - 3 Lee Unkrich - 2 Pete Docter - 2 Andrew Stanton - 2 Roberts Gannaway - 2 Popular actors (played more than once): John C. Reilly - 2 Sarah Silverman - 2 Joan Cusack - 2 Ned Beatty - 2 Owen Wilson - 2 Michael Caine - 2 Dane Cook - 2 Popular writers (wrote more than twice): Phil Johnston - 6 Pamela Ribon - 3 Rich Moore - 3 Jim Reardon - 4 Jared Bush - 3 Ron Clements - 3 John Musker - 3 Jennifer Lee - 4 Adrian Molina - 4 Dan Fogelman - 3 Pete Docter - 5 Meg LeFauve - 3 John Lasseter - 3 Andrew Stanton - 5 Bob Peterson - 6 Robert L. Baird - 3 Daniel Gerson - 3 Jeffrey M. Howard - 3 Roberts Gannaway - 3
def search_for_one(list_of_titles):
data_dict = parse_all_possible_data_to_lists(list_of_titles)
return convert_to_dataframe(data_dict)
search_for_one(search_1)
Years | Rated | Released_date | DVD_date | Runtime | imdbRating | Metascore | BoxOffice | weighted_Index | days_from_release_to_DVD | extra_names | |
---|---|---|---|---|---|---|---|---|---|---|---|
Moana | 2016 | PG | [2016, 11, 23] | [2017, 3, 7] | 107 | 7.6 | 81.0 | 248752120.0 | 7.80 | 109 | Moana |
Zootopia | 2016 | PG | [2016, 3, 4] | [2016, 6, 7] | 108 | 8.0 | 78.0 | 341264012.0 | 7.92 | 93 | Zootopia |
Coco | 2017 | PG | [2017, 11, 22] | [2018, 2, 27] | 105 | 8.4 | 81.0 | 208487719.0 | 8.28 | 100 | Coco |
Frozen | 2013 | PG | [2013, 11, 27] | [2014, 3, 18] | 102 | 7.5 | 74.0 | 400736600.0 | 7.46 | 116 | Frozen |
Tangled | 2010 | PG | [2010, 11, 24] | [2011, 3, 29] | 100 | 7.8 | 71.0 | 200803309.0 | 7.52 | 130 | Tangled |
Wreck-It Ralph | 2012 | PG | [2012, 11, 2] | [2013, 3, 5] | 101 | 7.7 | 72.0 | 189412677.0 | 7.50 | 128 | Wreck-It Ralph |
Brave | 2012 | PG | [2012, 6, 22] | [2012, 11, 13] | 93 | 7.1 | 69.0 | 237282182.0 | 7.02 | 141 | Brave |
Inside Out | 2015 | PG | [2015, 6, 19] | [2015, 11, 3] | 95 | 8.2 | 94.0 | 264317903.0 | 8.68 | 134 | Inside Out |
Toy Story 3 | 2010 | G | [2010, 6, 18] | [2010, 11, 2] | 103 | 8.3 | 92.0 | 414984497.0 | 8.66 | 134 | Toy Story 3 |
Up | 2009 | PG | [2009, 5, 29] | [2009, 11, 10] | 96 | 8.3 | 88.0 | 292979556.0 | 8.50 | 161 | Up |
WALL·E | 2008 | G | [2008, 6, 27] | [2008, 11, 18] | 98 | 8.4 | 95.0 | 223749872.0 | 8.84 | 141 | WALL·E |
Cars 3 | 2017 | G | [2017, 6, 16] | [2017, 11, 7] | 102 | 6.8 | 59.0 | 152603003.0 | 6.44 | 141 | Cars 3 |
The Princess and the Frog | 2009 | G | [2009, 12, 11] | [2010, 3, 16] | 97 | 7.1 | 73.0 | 104374107.0 | 7.18 | 100 | The Princess and the Frog |
G-Force | 2009 | PG | [2009, 7, 24] | [2009, 12, 15] | 88 | 5.1 | 41.0 | 119420252.0 | 4.70 | 141 | G-Force |
Bolt | 2008 | PG | [2008, 11, 21] | [2009, 3, 24] | 96 | 6.9 | 67.0 | 114053579.0 | 6.82 | 128 | Bolt |
Rango | 2011 | PG | [2011, 3, 4] | [2011, 7, 15] | 107 | 7.2 | 75.0 | 123188232.0 | 7.32 | 131 | Rango |
Monsters University | 2013 | G | [2013, 6, 21] | [2013, 10, 29] | 104 | 7.3 | 65.0 | 260300000.0 | 6.98 | 128 | Monsters University |
Finding Dory | 2016 | PG | [2016, 6, 17] | [2016, 11, 15] | 97 | 7.3 | 77.0 | 486292984.0 | 7.46 | 148 | Finding Dory |
Frankenweenie | 2012 | PG | [2012, 10, 5] | [2013, 1, 8] | 87 | 7.0 | 74.0 | 35287788.0 | 7.16 | 98 | Frankenweenie |
Cars 2 | 2011 | G | [2011, 6, 24] | [2011, 11, 1] | 106 | 6.2 | 57.0 | 191450875.0 | 6.00 | 127 | Cars 2 |
The Good Dinosaur | 2015 | PG | [2015, 11, 25] | [2016, 2, 23] | 93 | 6.7 | 66.0 | 123087120.0 | 6.66 | 93 | The Good Dinosaur |
The Boxtrolls | 2014 | PG | [2014, 9, 26] | [2015, 1, 20] | 96 | 6.8 | 61.0 | 52098992.0 | 6.52 | 119 | The Boxtrolls |
Gnomeo & Juliet | 2011 | G | [2011, 2, 11] | [2011, 5, 24] | 84 | 6.0 | 53.0 | 99808609.0 | 5.72 | 103 | Gnomeo & Juliet |
Strange Magic | 2015 | PG | [2015, 1, 23] | [2015, 5, 19] | 99 | 5.8 | 25.0 | 10201634.0 | 4.48 | 116 | Strange Magic |
Winnie the Pooh | 2011 | G | [2011, 7, 15] | [2011, 10, 25] | 63 | 7.2 | 74.0 | 26687172.0 | 7.28 | 100 | Winnie the Pooh |
Mars Needs Moms | 2011 | PG | [2011, 3, 11] | [2011, 8, 9] | 88 | 5.4 | 49.0 | 21379315.0 | 5.20 | 148 | Mars Needs Moms |
Planes | 2013 | PG | [2013, 8, 9] | [2013, 11, 19] | 91 | 5.7 | 39.0 | 71270759.0 | 4.98 | 100 | Planes |
A Christmas Carol | 2009 | PG | [2009, 11, 6] | [2010, 11, 16] | 96 | 6.8 | 55.0 | 137816456.0 | 6.28 | 375 | A Christmas Carol |
The Peanuts Movie | 2015 | G | [2015, 11, 6] | [2016, 3, 8] | 88 | 7.1 | 67.0 | 106132048.0 | 6.94 | 127 | The Peanuts Movie |
Planes: Fire & Rescue | 2014 | PG | [2014, 7, 18] | [2014, 11, 4] | 83 | 6.0 | 48.0 | 42622300.0 | 5.52 | 106 | Planes: Fire & Rescue |
Фильмы правом нижнем углу могут являться полезными находками, которые вы, возможно, не смотрели - они не собрали большой кассы, но имеют высокие рейтинги IMDb и Metascore. Левый верхний угол наоборот будет показывать переоцененные фильмы.
def plot_BoxOffice_vs_index(films_data):
new_films_data = films_data
return(px.scatter(new_films_data, x='weighted_Index', y='BoxOffice', color='Rated', size = 'Runtime', hover_name = 'extra_names', marginal_y='violin'))
plot_BoxOffice_vs_index(search_for_one(search_1))
(Интересный факт, который можно наблюдать на основе анимационных фильмов Диснея 2008-2018: регрессия по каждой из переменных отдельно дает значимую положительную корелляцию. Но если взять регрессию по обеим переменным сразу, то положительный эффект на кассовые сборы будет давать только "интересность" фильма)
def reg_boxoffice_indexes_1(films_data):
Y = np.array(films_data['BoxOffice'])
X = np.array( films_data['weighted_Index']).transpose()
reg = sm.OLS(Y, X).fit()
return reg.summary()
reg_boxoffice_indexes_1(search_for_one(search_1))
Dep. Variable: | y | R-squared: | 0.761 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.753 |
Method: | Least Squares | F-statistic: | 92.54 |
Date: | Mon, 15 Apr 2019 | Prob (F-statistic): | 1.58e-10 |
Time: | 07:13:25 | Log-Likelihood: | -596.56 |
No. Observations: | 30 | AIC: | 1195. |
Df Residuals: | 29 | BIC: | 1197. |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
x1 | 2.662e+07 | 2.77e+06 | 9.620 | 0.000 | 2.1e+07 | 3.23e+07 |
Omnibus: | 6.751 | Durbin-Watson: | 1.544 |
---|---|---|---|
Prob(Omnibus): | 0.034 | Jarque-Bera (JB): | 5.111 |
Skew: | 0.952 | Prob(JB): | 0.0777 |
Kurtosis: | 3.680 | Cond. No. | 1.00 |
def reg_boxoffice_indexes_2(films_data):
Y = np.array(films_data['BoxOffice'])
X = np.array( films_data['Years']).transpose()
reg = sm.OLS(Y, X).fit()
return reg.summary()
reg_boxoffice_indexes_2(search_for_one(search_1))
Dep. Variable: | y | R-squared: | 0.680 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.669 |
Method: | Least Squares | F-statistic: | 61.61 |
Date: | Mon, 15 Apr 2019 | Prob (F-statistic): | 1.18e-08 |
Time: | 07:13:28 | Log-Likelihood: | -600.97 |
No. Observations: | 30 | AIC: | 1204. |
Df Residuals: | 29 | BIC: | 1205. |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
x1 | 8.781e+04 | 1.12e+04 | 7.849 | 0.000 | 6.49e+04 | 1.11e+05 |
Omnibus: | 3.603 | Durbin-Watson: | 1.103 |
---|---|---|---|
Prob(Omnibus): | 0.165 | Jarque-Bera (JB): | 2.848 |
Skew: | 0.754 | Prob(JB): | 0.241 |
Kurtosis: | 2.938 | Cond. No. | 1.00 |
def reg_boxoffice_indexes(films_data):
Y = np.array(films_data['BoxOffice'])
X = np.array([films_data['weighted_Index'], films_data['Years']]).transpose()
reg = sm.OLS(Y, X).fit()
return reg.summary()
reg_boxoffice_indexes(search_for_one(search_1))
Dep. Variable: | y | R-squared: | 0.809 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.795 |
Method: | Least Squares | F-statistic: | 59.28 |
Date: | Mon, 15 Apr 2019 | Prob (F-statistic): | 8.63e-11 |
Time: | 07:13:30 | Log-Likelihood: | -593.23 |
No. Observations: | 30 | AIC: | 1190. |
Df Residuals: | 28 | BIC: | 1193. |
Df Model: | 2 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
x1 | 6.636e+07 | 1.53e+07 | 4.348 | 0.000 | 3.51e+07 | 9.76e+07 |
x2 | -1.406e+05 | 5.33e+04 | -2.640 | 0.013 | -2.5e+05 | -3.15e+04 |
Omnibus: | 6.139 | Durbin-Watson: | 2.473 |
---|---|---|---|
Prob(Omnibus): | 0.046 | Jarque-Bera (JB): | 4.375 |
Skew: | 0.789 | Prob(JB): | 0.112 |
Kurtosis: | 4.004 | Cond. No. | 1.73e+03 |
def search_for_many(list_of_titles_1, list_of_titles_2):
data_dict_1 = parse_all_possible_data_to_lists(list_of_titles_1)
dataFrame_1 = convert_to_dataframe(data_dict_1)
dataFrame_1['Search_num'] = ["Search_1"]*len(dataFrame_1['extra_names'])
data_dict_2 = parse_all_possible_data_to_lists(list_of_titles_2)
dataFrame_2 = convert_to_dataframe(data_dict_2)
dataFrame_2['Search_num'] = ['Search_2']*len(dataFrame_2['extra_names'])
total_df_T = dataFrame_1.T.join(dataFrame_2.T, on=None, how='outer', lsuffix='_search_1', rsuffix='_search_2', sort=False)
return total_df_T.T
def draw_scatterpolar(name1, name2, films_data1, films_data2):
IMDb1 = np.mean(films_data1['imdbRating'])
IMDb2 = np.mean(films_data2['imdbRating'])
Metascore1 = np.mean(films_data1['Metascore']/10)
Metascore2 = np.mean(films_data2['Metascore']/10)
BoxOfficeHDMN1 = np.mean(films_data1['BoxOffice']/100000000)
BoxOfficeHDMN2 = np.mean(films_data2['BoxOffice']/100000000)
Runtime_h1 = np.mean(films_data1['Runtime']/60)
Runtime_h2 = np.mean(films_data2['Runtime']/60)
Months_from_release_to_DVD1 = np.mean(films_data1['days_from_release_to_DVD']/30)
Months_from_release_to_DVD2 = np.mean(films_data2['days_from_release_to_DVD']/30)
Number_of_films1 = (len(films_data1['imdbRating'])/10)
Number_of_films2 = (len(films_data2['imdbRating'])/10)
data = [
go.Scatterpolar(
r = [IMDb1, Metascore1, BoxOfficeHDMN1, Runtime_h1, Months_from_release_to_DVD1,Number_of_films1, IMDb1],
theta = ['IMDb', 'MetaScore', 'BoxOffice_in_hundred_MN_USD', 'Runtime_hours', 'Months_from_release_to_DVD', 'Number_of_films / 10', 'IMDb'],
fill = 'toself',
name = name1),
go.Scatterpolar(
r = [IMDb2, Metascore2, BoxOfficeHDMN2, Runtime_h2, Months_from_release_to_DVD2, Number_of_films2 , IMDb2],
theta = ['IMDb', 'MetaScore', 'BoxOffice_in_hundred_MN_USD', 'Runtime_hours', 'Months_from_release_to_DVD', 'Number_of_films / 10' ,'IMDb'],
fill = 'toself',
name = name2)
]
layout = go.Layout(
polar = dict(
radialaxis = dict(
visible = True
)
),
showlegend = False
)
fig = go.Figure(data=data, layout=layout)
return py.iplot(fig)
draw_scatterpolar("Search_1", "Search_2", search_for_one(search_1), search_for_one(search_2) )
/anaconda3/lib/python3.7/site-packages/IPython/core/display.py:689: UserWarning: Consider using IPython.display.IFrame instead
#def plot_BoxOffice_vs_years(films_data):
#new_films_data = np.array(films_data)
#return(px.scatter(new_films_data, x='Years', y='BoxOffice', color='Search_num', trendline="ols", facet_col='Search_num', hover_name = 'extra_names'))
#plot_BoxOffice_vs_years(search_for_many(search_1, search_2))
url = "https://api.telegram.org/bot874320918:AAEMY99MTkcLXvml-fBaQJDGBruXgDLLO5M/"
#частично скопируем "основу" нашего бота с сайта https://proglib.io/p/telegram-bot/
#по сути данная операция аналогична осознанному перекопированию библиотек ботов, так как функции самые базовые
def be_updated(request):
bot_response = requests.get(request + 'getUpdates')
return bot_response.json()
def last_bot_update(data):
results = data['result']
total_updates = len(results) - 1
return results[total_updates]
def get_chat_id(update_method):
chat_id = update_method['message']['chat']['id']
return chat_id
def send_mess(chat, text):
params = {'chat_id': chat, 'text': text}
bot_response = requests.post(url + 'sendMessage', data=params)
return bot_response
#конец частичного копирования.
Теперь наш бот разумен, умеет обновляться и отправлять нам сообщения. Как это можно использовать? Например, вы работали в данной программе в питоне, выбрали параметры для поиска, нашли списков фильмов - а самый лучший из них вы хотите куда-то записать и запомнить. С этим нам и поможет телеграмм-бот - он пошлет вам сообщение с подробной информацией о самом лучшем фильме из датасета.
Сначала найдем лучший фильм датасета по кумулятивному индексу:
def best_film(dataframe):
dataframe = dataframe.sort_values('weighted_Index', axis = 0, ascending = False)
dataframe.reset_index(drop=True, inplace=True)
return dataframe['extra_names'][0]
title_0 = best_film(search_for_one(search_1))
title_0
'WALL·E'
Теперь быстро выгрузим к нему данные через API:
facts = get_info_from_API_by_title(title_0)
facts
[2008, 'G', [2008, 6, 27], [2008, 11, 18], 98, 'Animation, Adventure, Family, Sci-Fi', ['Andrew Stanton'], ['Andrew Stanton (original story by)', 'Pete Docter (original story by)', 'Andrew Stanton (screenplay by)', 'Jim Reardon (screenplay by)'], ['Ben Burtt', 'Elissa Knight', 'Jeff Garlin', 'Fred Willard'], 'In the distant future, a small waste-collecting robot inadvertently embarks on a space journey that will ultimately decide the fate of mankind.', 'Won 1 Oscar. Another 89 wins & 90 nominations.', 'https://m.media-amazon.com/images/M/MV5BMjExMTg5OTU0NF5BMl5BanBnXkFtZTcwMjMxMzMzMw@@._V1_SX300.jpg', 8.4, 95, 223749872, 'WALL·E']
Теперь можем передать эти знания нашему боту, и они сохранятся в нашей переписке в телеграмме. То есть к этим данным можно будет иметь доступ позднее и даже с телефона.
Для начала напишите боту в телеграмме - @little_imdb_helper .
Затем напишите /start и можете обращаться к нему за советом по фильмам:
chat_id = get_chat_id(last_update(get_updates_json(url)))
send_mess(chat_id, 'Сейчас я расскажу тебе про классный фильм ' + str(facts[0]) + '-ого года. Он называется '+ facts[15] + ". ")
send_mess(chat_id, 'Вот вкратце его сюжет:')
send_mess(chat_id, facts[9])
<Response [200]>
send_mess(chat_id, 'Ты заинтересован? Давай я покажу, как высоко уже был оценен данный фильм: ')
<Response [200]>
send_mess(chat_id, 'Рейтинг IMDb - '+ str(facts[12]))
<Response [200]>
send_mess(chat_id, 'Рейтинг Metascore - '+ str(facts[13]))
<Response [200]>
send_mess(chat_id, 'Кассовые сборы - '+ str(facts[14]) + 'долларов по всему миру')
<Response [200]>
send_mess(chat_id, 'Он также завоевал много наград:' + str(facts[10]))
<Response [200]>
send_mess(chat_id, 'А вот и постер к этому фильму:')
send_mess(chat_id, str(facts[11]))
<Response [200]>
send_mess(chat_id, 'В общем, настоятельн рекомендую посмотреть этот фильм. Еще раз повторю, его название - ' + str(facts[15]))
<Response [200]>
send_mess(chat_id, 'Надеюсь, я был полезен. До новых встреч')
<Response [200]>
В общем, с ботом можно чуток поговорить о фильмецах.
но...
Есть еще дополнительная функция. Она была написана в процессе, но не использовалась в проекте. Но все еще может быть полезна
def sign_in(browser, login, password):
sign_in_button = browser.find_element_by_xpath('//*[@id="imdb-signin-link"]')
sign_in_button.click()
sign_in_with_IMDb_button = browser.find_element_by_xpath('//*[@id="signin-options"]/div/div[1]/a[1]')
sign_in_with_IMDb_button.click()
email_field = browser.find_element_by_xpath('//*[@id="ap_email"]')
email_field.clear()
email_field.send_keys(login)
password_field = browser.find_element_by_xpath('//*[@id="ap_password"]')
password_field.clear()
password_field.send_keys(password)
keep_signed = browser.find_element_by_xpath('//*[@id="authportal-main-section"]/div[2]/div/div/form/div/div/div/div[3]/div/div/label/div/label/input')
keep_signed.click()
sign_in_button = browser.find_element_by_xpath('//*[@id="signInSubmit"]')
sign_in_button.click()
time.sleep(5)