Статьи

Python 101: как получить данные с RottenTomatoes

Сегодня мы рассмотрим, как получить данные с популярного сайта фильма, Rotten Tomatoes. Чтобы подписаться, вам нужно зарегистрировать ключ API здесь . Когда вы получите свой ключ, запишите ваш лимит использования, если он есть. Вы не хотите делать слишком много вызовов их API, или вы можете отозвать свой ключ. Наконец, всегда полезно прочитать документацию по API, который вы будете использовать. Вот несколько ссылок:

Как только вы изучите это или решите сохранить его на потом, мы продолжим наше путешествие.

Начиная шоу

API Rotten Tomatoes предоставляет набор каналов json, из которых мы можем извлекать данные. Мы будем использовать запросы и simplejson для обработки данных и их обработки. Давайте напишем небольшой сценарий, который может получить в настоящее время воспроизводимые фильмы

#----------------------------------------------------------------------
def getInTheaterMovies():
    """
    Get a list of movies in theaters. 
    """
    key = "YOUR API KEY"
    url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=%s"
    res = requests.get(url % key)
 
    data = res.content
 
    js = simplejson.loads(data)
 
    movies = js["movies"]
    for movie in movies:
        print movie["title"]
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    getInTheaterMovies()

Если вы запустите этот код, вы увидите список фильмов, напечатанных на стандартный вывод. Когда этот скрипт был запущен во время написания этой статьи, я получил следующий вывод:

Free Birds

Gravity

Ender’s Игра

Jackass представляет: Плохой дедушка

Лас-Вегас

Советник

Облачно с вероятностью фрикаделек 2
План побега

капитана Филлипса

Кэрри
Достаточно Сказал
Коварный: Глава 2
12 лет Раб
Мы Мельники
Заключенные
Требование багажа







В приведенном выше коде мы создаем URL-адрес, используя наш ключ API, и используем запросы для загрузки канала. Затем мы загружаем данные в simplejson, который возвращает вложенный словарь Python. Затем мы перебираем словарь фильмов и распечатываем название каждого фильма. Теперь мы готовы создать функцию для извлечения дополнительной информации из Rotten Tomatoes о каждом из этих фильмов.

import requests
import simplejson
import urllib
 
#----------------------------------------------------------------------
def getMovieDetails(key, title):
    """
    Get additional movie details
    """
    if " " in title:
        parts = title.split(" ")
        title = "+".join(parts)
 
    link = "http://api.rottentomatoes.com/api/public/v1.0/movies.json"
    url = "%s?apikey=%s&q=%s&page_limit=1"
    url = url % (link, key, title)
    res = requests.get(url)
    js = simplejson.loads(res.content)
 
    for movie in js["movies"]:
        print "rated: %s" % movie["mpaa_rating"]
        print "movie synopsis: " + movie["synopsis"]
        print "critics_consensus: " + movie["critics_consensus"]
 
        print "Major cast:"
        for actor in movie["abridged_cast"]:
            print "%s as %s" % (actor["name"], actor["characters"][0])
 
        ratings = movie["ratings"]
        print "runtime: %s"  % movie["runtime"]
        print "critics score: %s" % ratings["critics_score"]
        print "audience score: %s" % ratings["audience_score"]
        print "for more information: %s" % movie["links"]["alternate"]
    print "-" * 40
    print
 
#----------------------------------------------------------------------
def getInTheaterMovies():
    """
    Get a list of movies in theaters. 
    """
    key = "YOUR API CODE"
    url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=%s"
    res = requests.get(url % key)
 
    data = res.content
 
    js = simplejson.loads(data)
 
    movies = js["movies"]
    for movie in movies:
        print movie["title"]
        getMovieDetails(key, movie["title"]) 
    print
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    getInTheaterMovies()

Этот новый код извлекает много данных о каждом из фильмов, но потоки json содержат немного больше, чем не показано в этом примере. Вы можете увидеть, чего вам не хватает, просто распечатав словарь js на стандартный вывод, или вы можете увидеть пример канала json на странице документации Rotten Tomatoes., Если вы уделяете пристальное внимание, вы заметите, что Rotten Tomatoes API не охватывает большую часть данных на их веб-сайте. Например, нет способа получить информацию об актере. Например, если мы хотим узнать, в каких фильмах снялся Джим Керри, нет конечной точки URL, к которой можно было бы обратиться. Вы также не можете искать кого-либо еще в актерском составе, например, режиссера или продюсера. Информация находится на веб-сайте, но не предоставляется API. Для этого нам нужно обратиться к базе данных фильмов в Интернете (IMDB), но это будет тема другой статьи.

Давайте потратим некоторое время на улучшение этого примера. Одним из простых улучшений было бы поместить ключ API в файл конфигурации. Другой вариант — хранить информацию, которую мы загружаем, в базу данных. Третье улучшение заключается в добавлении кода, который проверяет, загрузили ли мы уже сегодняшние фильмы, потому что на самом деле нет веских причин загружать сегодняшние выпуски чаще, чем раз в день. Давайте добавим эти функции!

Добавление файла конфигурации

Я предпочитаю и рекомендую ConfigObj для работы с файлами конфигурации. Давайте создадим простой файл «config.ini» со следующим содержимым:

api_key = API KEY
last_downloaded =

Теперь давайте изменим наш код для импорта ConfigObj и изменим функцию getInTheaterMovies, чтобы использовать его:

import requests
import simplejson
import urllib
 
from configobj import ConfigObj
 
#----------------------------------------------------------------------
def getInTheaterMovies():
    """
    Get a list of movies in theaters. 
    """
    config = ConfigObj("config.ini")
    key = config["Settings"]["api_key"]
    url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=%s"
    res = requests.get(url % key)
 
    data = res.content
 
    js = simplejson.loads(data)
 
    movies = js["movies"]
    for movie in movies:
        print movie["title"]
        getMovieDetails(key, movie["title"]) 
    print
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    getInTheaterMovies()

Как видите, мы импортируем configobj и передаем ему наше имя файла. Вы также можете пройти по этому пути полностью. Затем мы извлекаем значение api_key и используем его в нашем URL. Поскольку в нашей конфигурации есть значение last_downloaded , мы должны добавить его к нашему коду, чтобы предотвратить загрузку данных несколько раз в день.

import datetime
import requests
import simplejson
import urllib
 
from configobj import ConfigObj
 
#----------------------------------------------------------------------
def getInTheaterMovies():
    """
    Get a list of movies in theaters. 
    """
    today = datetime.datetime.today().strftime("%Y%m%d")
    config = ConfigObj("config.ini")
 
    if today != config["Settings"]["last_downloaded"]:
        config["Settings"]["last_downloaded"] = today
 
        try: 
            with open("config.ini", "w") as cfg:
                config.write(cfg)
        except IOError:
            print "Error writing file!"
            return
 
        key = config["Settings"]["api_key"]
        url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=%s"
        res = requests.get(url % key)
 
        data = res.content
 
        js = simplejson.loads(data)
 
        movies = js["movies"]
        for movie in movies:
            print movie["title"]
            getMovieDetails(key, movie["title"]) 
        print
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    getInTheaterMovies()

Здесь мы импортируем модуль даты и времени Python и используем его для получения текущей даты в следующем формате: ГГГГММДД. Затем мы проверяем, равно ли значение last_downloaded файла конфигурации сегодняшней дате. Если это так, мы ничего не делаем. Однако, если они не совпадают, мы устанавливаем last_downloaded на сегодняшнюю дату, а затем загружаем данные фильма. Теперь мы готовы научиться сохранять данные в базе данных.

Сохранение данных с помощью SQLite

Python изначально поддерживает SQLite начиная с версии 2.5, поэтому, если вы не используете действительно старую версию Python, вы сможете без проблем следовать этой части статьи. По сути, нам просто нужно добавить функцию, которая может создавать базу данных и сохранять в нее наши данные. Вот функция:

#----------------------------------------------------------------------
def saveData(movie):
    """
    Save the data to a SQLite database
    """
    if not os.path.exists("movies.db"):
        # create the database
        conn = sqlite3.connect("movies.db")
 
        cursor = conn.cursor()
 
        cursor.execute("""CREATE TABLE movies 
        (title text, rated text, movie_synopsis text,
        critics_consensus text, runtime integer,
        critics_score integer, audience_score integer)""")
 
        cursor.execute("""
        CREATE TABLE cast
        (actor text, 
        character text)
        """)
 
        cursor.execute("""
        CREATE TABLE movie_cast
        (movie_id integer, 
        cast_id integer,
        FOREIGN KEY(movie_id) REFERENCES movie(id),
        FOREIGN KEY(cast_id) REFERENCES cast(id)
        )
        """)
    else:
        conn = sqlite3.connect("movies.db")
        cursor = conn.cursor()
 
    # insert the data
    print
    sql = "INSERT INTO movies VALUES(?, ?, ?, ?, ?, ?, ?)"
    cursor.execute(sql, (movie["title"],
                         movie["mpaa_rating"],
                         movie["synopsis"],
                         movie["critics_consensus"],
                         movie["runtime"],
                         movie["ratings"]["critics_score"],
                         movie["ratings"]["audience_score"]
                         )
                   )
    movie_id = cursor.lastrowid
 
    for actor in movie["abridged_cast"]:
        print "%s as %s" % (actor["name"], actor["characters"][0])
        sql = "INSERT INTO cast VALUES(?, ?)"
        cursor.execute(sql, (actor["name"],
                             actor["characters"][0]
                             )
                       )
        cast_id = cursor.lastrowid
 
        sql = "INSERT INTO movie_cast VALUES(?, ?)"
        cursor.execute(sql, (movie_id, cast_id) )
 
    conn.commit()
    conn.close()

Этот код сначала проверяет, существует ли файл базы данных. Если этого не произойдет, то он создаст базу данных вместе с 3 таблицами. В противном случае функция saveData создаст соединение и объект курсора. Затем он вставит данные, используя словарь фильмов, который передается ему. Мы вызовем эту функцию и передадим словарь фильмов из функции getMovieDetails . Наконец, мы передадим данные в базу данных и закроем соединение.

Вам, наверное, интересно, как выглядит полный код. Ну, вот оно:

import datetime
import os
import requests
import simplejson
import sqlite3
import urllib
 
from configobj import ConfigObj
 
#----------------------------------------------------------------------
def getMovieDetails(key, title):
    """
    Get additional movie details
    """
    if " " in title:
        parts = title.split(" ")
        title = "+".join(parts)
 
    link = "http://api.rottentomatoes.com/api/public/v1.0/movies.json"
    url = "%s?apikey=%s&q=%s&page_limit=1"
    url = url % (link, key, title)
    res = requests.get(url)
    js = simplejson.loads(res.content)
 
    for movie in js["movies"]:
        print "rated: %s" % movie["mpaa_rating"]
        print "movie synopsis: " + movie["synopsis"]
        print "critics_consensus: " + movie["critics_consensus"]
 
        print "Major cast:"
        for actor in movie["abridged_cast"]:
            print "%s as %s" % (actor["name"], actor["characters"][0])
 
        ratings = movie["ratings"]
        print "runtime: %s"  % movie["runtime"]
        print "critics score: %s" % ratings["critics_score"]
        print "audience score: %s" % ratings["audience_score"]
        print "for more information: %s" % movie["links"]["alternate"]
        saveData(movie)
    print "-" * 40
    print
 
#----------------------------------------------------------------------
def getInTheaterMovies():
    """
    Get a list of movies in theaters. 
    """
    today = datetime.datetime.today().strftime("%Y%m%d")
    config = ConfigObj("config.ini")
 
    if today != config["Settings"]["last_downloaded"]:
        config["Settings"]["last_downloaded"] = today
 
        try: 
            with open("config.ini", "w") as cfg:
                config.write(cfg)
        except IOError:
            print "Error writing file!"
            return
 
        key = config["Settings"]["api_key"]
        url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=%s"
        res = requests.get(url % key)
 
        data = res.content
 
        js = simplejson.loads(data)
 
        movies = js["movies"]
        for movie in movies:
            print movie["title"]
            getMovieDetails(key, movie["title"]) 
        print
 
#----------------------------------------------------------------------
def saveData(movie):
    """
    Save the data to a SQLite database
    """
    if not os.path.exists("movies.db"):
        # create the database
        conn = sqlite3.connect("movies.db")
 
        cursor = conn.cursor()
 
        cursor.execute("""CREATE TABLE movies 
        (title text, rated text, movie_synopsis text,
        critics_consensus text, runtime integer,
        critics_score integer, audience_score integer)""")
 
        cursor.execute("""
        CREATE TABLE cast
        (actor text, 
        character text)
        """)
 
        cursor.execute("""
        CREATE TABLE movie_cast
        (movie_id integer, 
        cast_id integer,
        FOREIGN KEY(movie_id) REFERENCES movie(id),
        FOREIGN KEY(cast_id) REFERENCES cast(id)
        )
        """)
    else:
        conn = sqlite3.connect("movies.db")
        cursor = conn.cursor()
 
    # insert the data
    print
    sql = "INSERT INTO movies VALUES(?, ?, ?, ?, ?, ?, ?)"
    cursor.execute(sql, (movie["title"],
                         movie["mpaa_rating"],
                         movie["synopsis"],
                         movie["critics_consensus"],
                         movie["runtime"],
                         movie["ratings"]["critics_score"],
                         movie["ratings"]["audience_score"]
                         )
                   )
    movie_id = cursor.lastrowid
 
    for actor in movie["abridged_cast"]:
        print "%s as %s" % (actor["name"], actor["characters"][0])
        sql = "INSERT INTO cast VALUES(?, ?)"
        cursor.execute(sql, (actor["name"],
                             actor["characters"][0]
                             )
                       )
        cast_id = cursor.lastrowid
 
        sql = "INSERT INTO movie_cast VALUES(?, ?)"
        cursor.execute(sql, (movie_id, cast_id) )
 
    conn.commit()
    conn.close()
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    getInTheaterMovies()

Если вы используете Firefox, есть забавный плагин SQLite Manager, который вы можете использовать для визуализации созданной нами базы данных. Вот скриншот того, что было сделано во время написания:

rotten_tomatoes_db

Завершение

Есть еще много вещей, которые следует добавить. Например, нам нужен некоторый код в функции getInTheaterMovies, который будет загружать детали из базы данных, если мы уже получили текущие данные. Нам также необходимо добавить некоторую логику в базу данных, чтобы мы не могли добавлять одного и того же актера или фильм несколько раз. Было бы хорошо, если бы у нас был какой-то графический интерфейс или веб-интерфейс. Это все, что вы можете добавить в качестве забавного маленького упражнения.

Кстати, эта статья была вдохновлена реальным питоном для веб- книги Майкла Хермана. В нем много интересных идей и примеров. Вы можете проверить это здесь .