起因

恰逢诺兰导演的新片《敦刻尔克》即将在中国上映,作为诺兰导演的铁粉,印象中他的很多部电影都进入了IMDB TOP250的榜单,但是具体是多少部呢?他是不是IMDB TOP250 中作品最多的导演呢?哪些演员在这些电影中出镜最多呢?在这些问题的启发下,我准备写一个简单的爬虫脚本来获取我想要的数据。

分析

首先需要对工作的流程进行一个简单的分析。我们的目标是获取以下的数据:

  • IMDB TOP250 中导演根据作品数量的排名
  • IMDB TOP250 中演员根据作品数量的排名

要得到以上的数据,我们需要的原始数据包括:

  • IMDB TOP250 的电影数据: 名称,评分
  • 电影导演
  • 电影演员

页面HTML分析

让我们先来看一下数据的来源,IMDB TOP250的网页。


可以看到在页面HTML文件中,我们可以得到的数据有电影的评分,电影的名字,电影的年份。但是导演和演员的数据呢?可以发现在页面上点击电影的名字,可以到达电影的详情页,而这个link也在HTML文件中。
我们接着观察电影的详情页。在HTML中我们可以获取到导演的信息


同时在Cast 的表中还可以获取到主要演员的信息


这样一来我们需要的数据就都有了。

数据库设计

要实现这种类型数据的排名和统计,关系型数据库更加合适。在这里,我的设计是用5个不同的表来记录不同的数据。同时我使用的是开源的MySQL数据库。

  1. 创建一个 imdb_movie schema
  2. 创建表 top_250_movies 用于存储电影的信息:电影名称 name, 电影的发行年份 year, 电影的评分 rate.
    这里还有一个电影的ID, 这个值如何来生成呢?是自动增加呢还是用一个其他的值?在前面的HTML文件中,我观察到电影的链接中有一个tt0111161的部分,所以我猜测0111161就是这部电影在IMDB中的UUID,所以我决定用这个值作为这个表的id值。
CREATE TABLE `top_250_movies` (
`id` int(11) NOT NULL,
`name` varchar(45) NOT NULL,
`year` int(11) DEFAULT NULL,
`rate` float NOT NULL,
PRIMARY KEY (`id`)
)
  1. 创建表 actorsdirectors来保存演员和导演的信息。
    这个表的结构很简单,就是演员的id 和演员的 name. 而演员/导演的ID和前面的电影ID的思路类似,通过演员详情页链接中的ID来设置。
CREATE TABLE `actors` (`id` int(11) NOT NULL,`name` varchar(45) DEFAULT NULL,PRIMARY KEY (`id`)
)
CREATE TABLE `directors` (`id` int(11) NOT NULL,`name` varchar(45) NOT NULL,PRIMARY KEY (`id`)
)
  1. 创建表 cast_in_movie来保存演员出演电影的信息。
    由于一个演员可以参演多部电影,而一个电影也有很多的演员,所以这里我会创建一个cast_id来标示每一个出演的关系,这个表中的每一行数据记录了一个演员参演了一部电影。同时是分别使用actor_idmovie_id为Foreign Key与actorstop_250_movies关联。
CREATE TABLE `cast_in_movie` (`cast_id` int(11) NOT NULL AUTO_INCREMENT,`actor_id` int(11) NOT NULL,`movie_id` int(11) NOT NULL,PRIMARY KEY (`cast_id`),KEY `actor_id_idx` (`actor_id`),KEY `movie_id_idx` (`movie_id`),CONSTRAINT `actor_id` FOREIGN KEY (`actor_id`) REFERENCES `actors` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,CONSTRAINT `movie_id` FOREIGN KEY (`movie_id`) REFERENCES `top_250_movies` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
  1. 用类似的思路创建表 direct_movie
CREATE TABLE `direct_movie` (`id` int(11) NOT NULL AUTO_INCREMENT,`director_id` int(11) NOT NULL,`movie_id` int(11) NOT NULL,PRIMARY KEY (`id`),KEY `director_id_idx` (`director_id`),KEY `movie_id_idx` (`movie_id`),CONSTRAINT `director_id` FOREIGN KEY (`director_id`) REFERENCES `directors` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)

脚本实现

在理清了工作流程之后,可以开始实现脚本了。

需要使用的扩展包
import re
import pymysql
import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
1. 解析IMDBTOP250 页面的HTML

代码中主要使用BeautifulSoup对HTML文件进行解析和搜索,获取需要的数据。
另外需要注意的是使用正则表达式来获取电影的ID
id_pattern = re.compile(r'(?<=tt)\d+(?=/?)')
获取的是tt开头 /结尾的字符串,但是不包含tt/,这部分数字就是我们想要的ID。
这个方法是一个生成器,每次的返回是一部电影的数据。

def get_top250_movies_list():url = "http://www.imdb.com/chart/top"try:response = requests.get(url)if response.status_code == 200:html = response.textsoup = BeautifulSoup(html, 'lxml')movies = soup.select('tbody tr')for movie in movies:poster = movie.select_one('.posterColumn')score = poster.select_one('span[name="ir"]')['data-value']movie_link = movie.select_one('.titleColumn').select_one('a')['href']year_str = movie.select_one('.titleColumn').select_one('span').get_text()year_pattern = re.compile('\d{4}')year = int(year_pattern.search(year_str).group())id_pattern = re.compile(r'(?<=tt)\d+(?=/?)')movie_id = int(id_pattern.search(movie_link).group())movie_name = movie.select_one('.titleColumn').select_one('a').stringyield {'movie_id': movie_id,'movie_name': movie_name,'year': year,'movie_link': movie_link,'movie_rate': float(score)}else:print("Error when request URL")except RequestException:print("Request Failed")return None
2. 将电影数据存入数据库
  1. 首先建立数据库连接
db = pymysql.connect("localhost","testuser01","111111","imdb_movie" )
cursor = db.cursor()
  1. 把电影数据存入数据库
    每次存入前需要检查这条数据是否已经存在,避免出错。
def store_movie_data_to_db(movie_data):print(movie_data)sel_sql =  "SELECT * FROM top_250_movies \WHERE id =  %d" % (movie_data['movie_id'])try:cursor.execute(sel_sql)result = cursor.fetchall()except:print("Failed to fetch data")if result.__len__() == 0:sql = "INSERT INTO top_250_movies \(id, name, year, rate) \VALUES ('%d', '%s', '%d', '%f')" % \(movie_data['movie_id'], movie_data['movie_name'], movie_data['year'], movie_data['movie_rate'])try:cursor.execute(sql)db.commit()print("movie data ADDED to DB table top_250_movies!")except:# 发生错误时回滚db.rollback()else:print("This movie ALREADY EXISTED!!!")
3. 获取电影详细信息

接着利用上面的得到的movie_data 来获取电影详情页的信息。包括导演信息和演员信息。

def get_movie_detail_data(movie_data):url = "http://www.imdb.com" + movie_data['movie_link']try:response = requests.get(url)if response.status_code == 200:soup = BeautifulSoup(response.text, 'lxml')# Parse Director's infodirector = soup.select_one('span[itemprop="director"]')person_link = director.select_one('a')['href']director_name = director.select_one('span[itemprop="name"]')id_pattern = re.compile(r'(?<=nm)\d+(?=/?)')person_id = int(id_pattern.search(person_link).group())movie_data['director_id'] = person_idmovie_data['director_name'] = director_name.stringstore_director_data_in_db(movie_data)#parse Cast's datacast = soup.select('table.cast_list tr[class!="castlist_label"]')for actor in get_cast_data(cast):store_actor_data_to_db(actor, movie_data)else:print("GET url of movie Do Not 200 OK!")except RequestException:print("Get Movie URL failed!")return None

获取演员信息的方法:

def get_cast_data(cast):for actor in cast:actor_data = actor.select_one('td[itemprop="actor"] a')person_link = actor_data['href']id_pattern = re.compile(r'(?<=nm)\d+(?=/)')person_id = int(id_pattern.search(person_link).group())actor_name = actor_data.get_text().strip()yield {'actor_id': person_id,'actor_name': actor_name}
4. 把导演信息存入数据库

这里需要在两个table中插入数据。首先在directors中插入导演的数据,同样检查记录是否已经存在。接着在 direct_movie插入数据,插入前也检查是否已经存在相同的数据。

def store_director_data_in_db(movie):sel_sql = "SELECT * FROM directors \WHERE id =  %d" % (movie['director_id'])try:# 执行sql语句cursor.execute(sel_sql)# 执行sql语句result = cursor.fetchall()except:print("Failed to fetch data")if result.__len__() == 0:sql = "INSERT INTO directors \(id, name) \VALUES ('%d', '%s')" % \(movie['director_id'], movie['director_name'])try:# 执行sql语句cursor.execute(sql)# 执行sql语句db.commit()print("Director data ADDED to DB table directors!", movie['director_name'] )except:# 发生错误时回滚db.rollback()else:print("This Director ALREADY EXISTED!!")sel_sql = "SELECT * FROM direct_movie \WHERE director_id =  %d AND movie_id = %d" % (movie['director_id'], movie['movie_id'])try:# 执行sql语句cursor.execute(sel_sql)# 执行sql语句result = cursor.fetchall()except:print("Failed to fetch data")if result.__len__() == 0:sql = "INSERT INTO direct_movie \(director_id, movie_id) \VALUES ('%d', '%d')" % \(movie['director_id'], movie['movie_id'])try:# 执行sql语句cursor.execute(sql)# 执行sql语句db.commit()print("Director direct movie data ADD to DB table direct_movie!")except:# 发生错误时回滚db.rollback()else:print("This Director direct movie ALREADY EXISTED!!!")
5. 把演员信息存入数据库

这里需要在两个table中插入数据。首先在actors中插入演员的数据,同样检查记录是否已经存在。接着在cast_in_movie插入数据,插入前也检查是否已经存在相同的数据。

def store_actor_data_to_db(actor, movie):sel_sql = "SELECT * FROM actors \WHERE id =  %d" % (actor['actor_id'])try:# 执行sql语句cursor.execute(sel_sql)# 执行sql语句result = cursor.fetchall()except:print("Failed to fetch data")if result.__len__() == 0:sql = "INSERT INTO actors \(id, name) \VALUES ('%d', '%s')" % \(actor['actor_id'], actor['actor_name'])try:# 执行sql语句cursor.execute(sql)# 执行sql语句db.commit()print("actor data ADDED to DB table actors!")except:# 发生错误时回滚db.rollback()else:print("This actor has been saved already")sel_sql = "SELECT * FROM cast_in_movie \WHERE actor_id =  %d AND movie_id = %d" % (actor['actor_id'], movie['movie_id'])try:# 执行sql语句cursor.execute(sel_sql)# 执行sql语句result = cursor.fetchall()except:print("Failed to fetch data")if result.__len__() == 0:sql = "INSERT INTO cast_in_movie \(actor_id, movie_id) \VALUES ('%d', '%d')" % \(actor['actor_id'], movie['movie_id'])try:# 执行sql语句cursor.execute(sql)# 执行sql语句db.commit()print("actor casted in movie data ADDED to DB table cast_in_movie!")except:# 发生错误时回滚db.rollback()else:print("This actor casted in movie data ALREADY EXISTED")
6. 完成代码

这里需要注意的是在操作完成或者出错的情况下都要关闭数据库连接。

def main():try:for movie in get_top250_movies_list():store_movie_data_to_db(movie)get_movie_detail_data(movie)finally:db.close()if __name__ == '__main__':main()

数据库查询分析

运行脚本完成数据获取之后,我们通过SQL语句来获取我们最终想要的数据

IMDB TOP250导演排名
SELECT dm.director_id, d.name, count(dm.id) as direct_count
FROM imdb_movie.direct_movie as dm
JOIN imdb_movie.directors as d ON d.id = dm.director_id
group by dm.director_id
order by direct_count desc
IMDB TOP250演员排名
SELECT cm.actor_id, a.name, count(cm.actor_id) as count_of_act
FROM imdb_movie.cast_in_movie as cm
JOIN imdb_movie.actors as a ON a.id = cm.actor_id
group by cm.actor_id
order by count_of_act desc

最终的答案是什么呢?各位同学可以自己来揭晓。

谁是史上最强-用爬虫分析IMDB TOP250电影数据相关推荐

  1. 终极!绝无仅有,史上最强python爬虫资料分享!

    这是一份终极秘籍! 这是一份终极秘籍! 这是一份终极秘籍! 坦白了,不装了.强大如我,竟语无伦次,完全想不到一个贴合的词汇去形容这份资料的强大.在我看来,人间最美的文字在它面前也终是亵渎. 很难想象, ...

  2. 史上最强GAN被谷歌超越!标注数据少用90%,造假效果却更逼真

    当前生成图像最逼真的BigGAN被超越了! 出手的,是谷歌大脑和苏黎世联邦理工学院.他们提出了新一代GAN:S³GAN. 它们生成的照片,都是真假难辨. 下面这两只蝴蝶,哪只更生动? 两张风景照片,哪 ...

  3. 股票分析报告怎么写?史上最强股票分析报告范文模板帮助你

    股票分析报告是投资者赚钱的第一步,也是最重要的一步,绝大多数盲目的闭眼玩家在买入股票之前,只是稍微粗略浏览下K线走势和基本公司资料就大手笔自信满满的跟风买入,实际等于摸黑下楼梯,掉坑里了也不知道,股票 ...

  4. 史上最强 iPhone 越狱工具开源:功能永久有效!

    微信搜 "GitHubDaily" 点关注 设为 "星标",每天带你逛 GitHub! 转发自量子位,作者栗子 这可能是史上最强的 iPhone 越狱工具了. ...

  5. 【经典干货】GitHub标星10万+,史上最强Google面试指南!

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 一位从1997年就入行的Web工程师,立志要成为Google软件工程师,3年前写下 ...

  6. 3分钟入门python_3分钟学完Python,直接从入门到精通「史上最强干货库」

    作为帅气小编,我已经把python一些模块的甩在这儿了qwq,只要你拿到这些干货,包你玩转python,直接冲向"大佬"的段位,如果已经学了C或者C++或者说如果你需要你的一段关键 ...

  7. 【整理】史上最强的娱乐大餐———九奔、汉澳、器普。。。。。。

    按: 本文内容来源于:http://shouji88.com/msg_list.cgi?bbs_id=000001                               http://shouj ...

  8. 苹果:两个 M1 Max 拼一块儿,“史上最强 PC 芯片”M1 Ultra 就出来了!

    苹果打造最强 PC 芯片教程:拿两个 M1 Max 拼一块,最强的 M1 Ultra 就诞生了! 整理 | 郑丽媛 出品 | CSDN(ID:CSDNnews) 今日凌晨,苹果春季发布会如期而至.由于 ...

  9. 史上最强春节档来袭!Python 解读哪部影片值得一看?

    作者 | 徐麟 责编 | 郭芮 随着春节脚步的临近,想必各位都已经开始规划幸福的春节长假该如何度过.阖家团聚,探亲访友,与好久没见的好基友一起谈天说地,怼天怼地,想想都是让人感到幸福. 除此之外,每年 ...

  10. 史上最强GAN:训练费10万起,现在免费体验,画风鬼畜又逼真

    夏乙 郭一璞 安妮 晓查 发自 亚龙湾  量子位 报道 | 公众号 QbitAI ?好消息!好消息! 9月底轰动业界的史上最强GAN,也就是最高动用512个TPU训练的BigGAN,Demo已经正式放 ...

最新文章

  1. AI与区块链的融合会给人类带来什么?
  2. HTML5学习笔记简明版(10):过时的元素和属性
  3. python怎么导入时间-python初步学习-import和datetime模块
  4. vs2005中文的,可是有180天的适用期,哪位高手能破了啊
  5. linux怎么命令设置网络连接,Linux网络操作命令
  6. COM笔记-CoCreateInstance
  7. Eclipse - CDT使用GDB调试C++的问题-无源文件命名(No source file named)
  8. mysql 配置多个数据库连接_总结MySQL修改最大连接数的两个方式
  9. idea创建springmvc项目
  10. oracle 循环继续,oracle – 是否可以继续从异常循环?
  11. 初学者一些常用的SQL语句(一)
  12. 2017-2018-20155336 《信息安全系统设计基础》第六周学习总结
  13. JAVA垃圾回收机制
  14. 金字塔 2020-12-29
  15. layui 表单 验证整数
  16. 网易交互设计师微专业 C1 揭开交互神秘面纱
  17. 计算机vfp知识要点,2017年职称计算机VisualFoxPro基础知识:表的基本操作
  18. C#发送邮件,包含发送附件
  19. 802.11 MAC 基础
  20. 三:JavaScript快速入门(脚本开发方向)

热门文章

  1. x星球出入站(蓝桥杯递归)
  2. html打字机特效,[JS插件]酷炫的打字机效果: Typed.js
  3. Miss okhttp
  4. Linux常用命令学习
  5. 电脑远程桌面连接怎么操作?
  6. 经验 | 在麻省理工人工智能实验室如何做研究?
  7. tomcat报错:No such device; No faulty members identified.
  8. ps2模拟器linux,PS2模拟器PCSX
  9. idea安装findBugs 报idea运行项目报错Cannot run program
  10. 火狐Firefox安装屏蔽广告插件ublock origin方法