Python数据可视化:2018年电影分析

有态度地学习

双11已经过去,双12即将来临,离2018年的结束也就2个月不到,还记得年初立下的flag吗?

完成了多少?相信很多人和我一样,抱头痛哭...

本次利用猫眼电影,实现对2018年的电影大数据进行分析。

/ 01 / 网页分析

01 标签

通过点击猫眼电影已经归类好的标签,得到网址信息。

02 索引页

打开开发人员工具,获取索引页里电影的链接以及评分信息。

索引页一共有30多页,但是有电影评分的只有10页。

本次只对有电影评分的数据进行获取。

03 详情页

对详情页的信息进行获取。

主要是名称,类型,国家,时长,上映时间,评分,评分人数,累计票房。

/ 02 / 反爬破解

通过开发人员工具发现,猫眼针对评分,评分人数,累计票房的数据,施加了文字反爬。

通过查看网页源码,发现只要刷新页面,三处文字编码就会改变,无法直接匹配信息。

所以需要下载文字文件,对其进行双匹配。

from fontTools.ttLib import TTFont

#font = TTFont('base.woff')
#font.saveXML('base.xml')
font = TTFont('maoyan.woff')
font.saveXML('maoyan.xml')

将woff格式转换为xml格式,以便在Pycharm中查看详细信息。

利用下面这个网站,打开woff文件。

url: http://fontstore.baidu.com/static/editor/index.html

可以得到下面数字部分信息(上下两块)。

在Pycharm中查看xml格式文件(左右两块),你就会发现有对应信息。

通过上图你就可以将数字6对上号了,其他数字一样的。

def get_numbers(u):
    """
    对猫眼的文字反爬进行破解
    """
    cmp = re.compile(",\n           url\('(//.*.woff)'\) format\('woff'\)")
    rst = cmp.findall(u)
    ttf = requests.get("http:" + rst[0], stream=True)
    with open("maoyan.woff", "wb") as pdf:
        for chunk in ttf.iter_content(chunk_size=1024):
            if chunk:
                pdf.write(chunk)
    base_font = TTFont('base.woff')
    maoyanFont = TTFont('maoyan.woff')
    maoyan_unicode_list = maoyanFont['cmap'].tables[0].ttFont.getGlyphOrder()
    maoyan_num_list = []
    base_num_list = ['.', '3', '0', '8', '9', '4', '1', '5', '2', '7', '6']
    base_unicode_list = ['x', 'uniF561', 'uniE6E1', 'uniF125', 'uniF83F', 'uniE9E2', 'uniEEA6', 'uniEEC2', 'uniED38', 'uniE538', 'uniF8E7']
    for i in range(1, 12):
        maoyan_glyph = maoyanFont['glyf'][maoyan_unicode_list[i]]
        for j in range(11):
            base_glyph = base_font['glyf'][base_unicode_list[j]]
            if maoyan_glyph == base_glyph:
                maoyan_num_list.append(base_num_list[j])
                break
    maoyan_unicode_list[1] = 'uni0078'
    utf8List = [eval(r"'\u" + uni[3:] + "'").encode("utf-8") for uni in maoyan_unicode_list[1:]]
    utf8last = []
    for i in range(len(utf8List)):
        utf8List[i] = str(utf8List[i], encoding='utf-8')
        utf8last.append(utf8List[i])
    return (maoyan_num_list ,utf8last)

/ 03 / 数据获取

01 构造请求头

head = """
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host:maoyan.com
Upgrade-Insecure-Requests:1
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
"""

def str_to_dict(header):
    """
    构造请求头,可以在不同函数里构造不同的请求头
    """
    header_dict = {}
    header = header.split('\n')
    for h in header:
        h = h.strip()
        if h:
            k, v = h.split(':', 1)
            header_dict[k] = v.strip()
    return header_dict

因为索引页和详情页请求头不一样,这里为了简便,构造了一个函数。

02 获取电影详情页链接

def get_url():
    """
    获取电影详情页链接
    """
    for i in range(0, 300, 30):
        time.sleep(10)
        url = 'http://maoyan.com/films?showType=3&yearId=13&sortId=3&offset=' + str(i)
        host = """Referer:http://maoyan.com/films?showType=3&yearId=13&sortId=3&offset=0
        """
        header = head + host
        headers = str_to_dict(header)
        response = requests.get(url=url, headers=headers)
        html = response.text
        soup = BeautifulSoup(html, 'html.parser')
        data_1 = soup.find_all('div', {'class': 'channel-detail movie-item-title'})
        data_2 = soup.find_all('div', {'class': 'channel-detail channel-detail-orange'})
        num = 0
        for item in data_1:
            num += 1
            time.sleep(10)
            url_1 = item.select('a')[0]['href']
            if data_2[num-1].get_text() != '暂无评分':
                url = 'http://maoyan.com' + url_1
                for message in get_message(url):
                    print(message)
                    to_mysql(message)
                print(url)
                print('---------------^^^Film_Message^^^-----------------')
            else:
                print('The Work Is Done')
                break

03 获取电影详情页信息

def get_message(url):
    """
    获取电影详情页里的信息
    """
    time.sleep(10)
    data = {}
    host = """refer: http://maoyan.com/news
    """
    header = head + host
    headers = str_to_dict(header)
    response = requests.get(url=url, headers=headers)
    u = response.text
    # 破解猫眼文字反爬
    (mao_num_list, utf8last) = get_numbers(u)
    # 获取电影信息
    soup = BeautifulSoup(u, "html.parser")
    mw = soup.find_all('span', {'class': 'stonefont'})
    score = soup.find_all('span', {'class': 'score-num'})
    unit = soup.find_all('span', {'class': 'unit'})
    ell = soup.find_all('li', {'class': 'ellipsis'})
    name = soup.find_all('h3', {'class': 'name'})
    # 返回电影信息
    data["name"] = name[0].get_text()
    data["type"] = ell[0].get_text()
    data["country"] = ell[1].get_text().split('/')[0].strip().replace('\n', '')
    data["length"] = ell[1].get_text().split('/')[1].strip().replace('\n', '')
    data["released"] = ell[2].get_text()[:10]
    # 因为会出现没有票房的电影,所以这里需要判断
    if unit:
        bom = ['分', score[0].get_text().replace('.', '').replace('万', ''), unit[0].get_text()]
        for i in range(len(mw)):
            moviewish = mw[i].get_text().encode('utf-8')
            moviewish = str(moviewish, encoding='utf-8')
            # 通过比对获取反爬文字信息
            for j in range(len(utf8last)):
                moviewish = moviewish.replace(utf8last[j], maoyan_num_list[j])
            if i == 0:
                data["score"] = moviewish + bom[i]
            elif i == 1:
                if '万' in moviewish:
                    data["people"] = int(float(moviewish.replace('万', '')) * 10000)
                else:
                    data["people"] = int(float(moviewish))
            else:
                if '万' == bom[i]:
                    data["box_office"] = int(float(moviewish) * 10000)
                else:
                    data["box_office"] = int(float(moviewish) * 100000000)
    else:
        bom = ['分', score[0].get_text().replace('.', '').replace('万', ''), 0]
        for i in range(len(mw)):
            moviewish = mw[i].get_text().encode('utf-8')
            moviewish = str(moviewish, encoding='utf-8')
            for j in range(len(utf8last)):
                moviewish = moviewish.replace(utf8last[j], maoyan_num_list[j])
            if i == 0:
                data["score"] = moviewish + bom[i]
            else:
                if '万' in moviewish:
                    data["people"] = int(float(moviewish.replace('万', '')) * 10000)
                else:
                    data["people"] = int(float(moviewish))
        data["box_office"] = bom[2]
    yield data

/ 04 / 数据存储

01 创建数据库及表格

db = pymysql.connect(host='127.0.0.1', user='root', password='774110919', port=3306)
cursor = db.cursor()
cursor.execute("CREATE DATABASE maoyan DEFAULT CHARACTER SET utf8mb4")
db.close()
db = pymysql.connect(host='127.0.0.1', user='root', password='774110919', port=3306, db='maoyan')
cursor = db.cursor()
sql = 'CREATE TABLE IF NOT EXISTS films (name VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, country VARCHAR(255) NOT NULL, length VARCHAR(255) NOT NULL, released VARCHAR(255) NOT NULL, score VARCHAR(255) NOT NULL, people INT NOT NULL, box_office BIGINT NOT NULL, PRIMARY KEY (name))'
cursor.execute(sql)
db.close()

其中票房收入数据类型为BIGINT(19位数),最大为18446744073709551615。

INT(10位数),最大为2147483647,达不到36亿(3600000000)。

02 数据存储

def to_mysql(data):
    """
    信息写入mysql
    """
    table = 'films'
    keys = ', '.join(data.keys())
    values = ', '.join(['%s'] * len(data))
    db = pymysql.connect(host='localhost', user='root', password='774110919', port=3306, db='maoyan')
    cursor = db.cursor()
    sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)
    try:
        if cursor.execute(sql, tuple(data.values())):
            print("Successful")
            db.commit()
    except:
        print('Failed')
        db.rollback()
    db.close()

最后成功存储数据

/ 05 / 数据可视化

可视化源码就不放了,公众号回复电影即可获得。

01 电影票房TOP10

还剩一个多月,不知道榜单上会不会有新成员。最近「毒液」很火,蛮有希望。

02 电影评分TOP10

这里就得吐槽一下pyecharts,坐标转换后,坐标值名称太长就会被遮挡,还需改进呢~

03 电影人气TOP10

茫茫人海之中,相信一定也有大家的身影,我也是其中的一员!!!

04 每月电影上映数量

每月上映数好像没什么大差距,7月最少,难道是因为天气热?

05 每月电影票房

这里就看出春节档电影的威力了,金三银四、金九银十,各行各业的规律,电影行业也不例外。

上一张图我们知道7月份电影上新最少,票房反而是第二。

这里看了下数据,发现有「我不是药神」「西虹市首富」「邪不压正」「摩天营救」「狄仁杰之四大天王」几部大剧撑着。

06 各国家电影数量TOP10

原来中国电影这么高产的,可是豆瓣TOP250里又有多少中国电影呢?深思!!!

07 中外票房对比

2017年的年度票房是560亿,估计今年快要突破了。据说今年全年票房有望突破600亿。

08 电影名利双收TOP10

计算公式是,把某部电影的评分在所有电影评分中的排名与这部电影的票房在所有票房中的排名加起来,再除以电影总数。

除了「侏罗纪世界2」「无双」「捉妖记2」,我都看过啦!

09 电影叫座不叫好TOP10

计算公式是,把某部电影的票房排名减去某部电影的评分排名加起来,再除以电影总数。

可能是猫眼的用户比较仁慈吧,与豆瓣相比,普遍评分都比较高。我个人都不太敢相信这个结果。

不过有一个还是挺准的,「爱情公寓」。

10 电影类型分布

剧情电影永远引人深思。感觉今年的电影好多跟钱有关,比如「我不是药神」「西虹市首富」「一出好戏」「头号玩家」,贫穷限制了大家伙们。

原文发布于微信公众号 - 日常学python(daily_learn)

原文发表时间:2018-11-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android群英传

程序员英语口语等级考试

1614
来自专栏惨绿少年

计算机专用英语词汇1695个词汇表

特别感谢: 不愿意透露姓名的小虾同学提供的音标部分 1.单词说明:   command n. 命令,指令 [kə'mɑ:nd]   单词拼写 名词 单词含...

3240
来自专栏牛客网

美团java后台方向/2018春招/二面问题

2170
来自专栏Kirito的技术分享

中文文案排版指南

目录 空格 中英文之间需要增加空格 中文与数字之间需要增加空格 数字与单位之间需要增加空格 全角标点与其他字符之间不加空格 -ms-text-autospac...

4968
来自专栏点滴积累

JS前端三维地球渲染——中国各城市航空路线展示

前言 我还从来没有写过有关纯JS的文章(上次的矢量瓦片展示除外,相对较简单。),自己也学习过JS、CSS等前端知识,了解JQuery、React等框架,但是自己...

8636
来自专栏思影科技

《大话脑成像》系列之十——计算机存取MRI影像的那些事

昨天,小芳(隔壁村的)问笔者:为什么我输出不了超过256个大脑区域? 乍一看,非常绕口。 今天的内容如果直接回答,恐怕绝大多数人要崩溃了,让我一步步给大家讲解,...

3447
来自专栏Python与爬虫

[资源分享]计算机科学速成课

推荐 程序员的你一定要看,不是程序员的也可以看看,我已经安利刚中考完的我妹妹看了(培养程序媛...)

2413
来自专栏大数据挖掘DT机器学习

中文分词实践(基于R语言)

背景:分析用户在世界杯期间讨论最多的话题。 思路:把用户关于世界杯的帖子拉下来,然后做中文分词+词频统计,最后将统计结果简单做个标签云. 后续:中文分词是中文...

3756
来自专栏圆方圆学院精选

【戴嘉乐 IPFS】基于IPFS和GeoHash构建具有地理位置价值服务的DDApp(理论篇)

打造地理位置信息与区块链的关系对象模型,建立一套 人->位置->真实世界->传递信任->价值转移->位置->人 的生态模型,实现用区块链来索引真实世界的愿景。

1581
来自专栏智慧教育

比起WE大会“救命的AI”,这个AI已经悄悄进入人们的学习中

“未来人工智能要进一步发展的话,就需从脑科学得到启发,包括机器学习过程,怎么从脑启发的这个概念来设计新的计算模式,新的类似人脑的神经元结构的器件、芯片,甚至是机...

2954

扫码关注云+社区

领取腾讯云代金券