网络爬虫——IMDb-Most Popular Tv Shows

发布时间 2023-06-09 19:28:22作者: The_KK

一、选题的背景

1.选择IMDb流行电视剧的网络爬虫作为选题,是因为电视剧在当今社会中扮演着重要角色,它们不仅是娱乐消遣的方式,还反映了社会文化、价值观和审美趋势。通过对IMDb流行电视剧的数据分析,可以了解观众的喜好和市场需求,为制作公司、投资者、广告商等提供有价值的参考信息。

2.预期目标是获取IMDb网站上热门电视剧的关键信息,如中英文名称、导演、年份、评分以及排名变化等,从而进行深入分析。例如,可以挖掘出哪些类型的电视剧更受欢迎、哪些导演的作品更具吸引力、不同年份的热门电视剧在主题和风格上的变化等。

3.从社会角度来看,热门电视剧往往反映了社会大众的价值观、心理需求和审美趋势。从经济角度来看,了解热门电视剧的特点有助于投资者、制作公司和广告商制定更明智的决策。从技术角度来看,网络爬虫技术可以高效地收集和整理数据,为后续的数据分析和挖掘奠定基础。从数据来源来看,IMDb是全球最大的电影和电视剧数据库,其数据具有较高的权威性和可靠性。

二、主题式网络爬虫设计方案

1.主题式网络爬虫名称

  IMDb-Most Popular Tv Shows

2.网络爬虫爬取IMDb流行电视剧的内容时,通常关注以下数据特征进行分析:

  1. 电视剧名称(Title):电视剧的名称是关键信息,便于识别和检索。

  2. 评分(Rating):用户对电视剧的评价,通常以0-10的分数表示,可用于评估电视剧的受欢迎程度。

  3. 首播年份(Premiere Date):电视剧首次播出的年份,可以用来分析电视剧的新旧程度。 排名(Ranking):根据评分、评价人数等因素综合排序得出的电视剧排名,可以反映出电视剧在众多作品中的地位。

  4. 排名(Ranking):根据评分、评价人数等因素综合排序得出的电视剧排名,可以反映出电视剧在众多作品中的地位。

通过分析这些数据特征,我们可以了解到电视剧的受欢迎程度、观众喜好、新旧程度等信息。这些信息对于制作公司、广告商、观众等多方具有重要参考价值

3.网络爬虫设计方案概述:

一、实现思路:

  1. 目标网站分析:分析IMDb和豆瓣网站的结构、数据分布和页面编码等信息,以便更好地设计爬虫程序。

  2. 请求与响应处理:使用Python的requests库向目标网站发送请求,并处理响应数据,包括处理异常情况、编码问题等。同时,使用代理IP和随机User-Agent进行反爬策略应对。

  3. 数据解析:使用正则表达式对响应数据进行解析,提取所需信息,包括中文名、英文名、导演、年份、评分和排名变化等。

  4. 数据存储:将提取到的信息存储到Excel文件中,以便后续分析和使用。

  5. 爬虫调度:使用time库的sleep函数实现延时爬取,降低爬取频率,避免触发反爬策略。

二、技术难点:

  1. 反爬策略应对:目标网站可能会采取一定的反爬策略,如User-Agent限制、IP限制等。需要设计相应的策略应对,如使用代理IP、随机User-Agent等。

  2. 数据解析准确性:由于网站结构可能会发生变化,需要时刻关注数据解析的准确性,并及时调整解析规则。同时,正则表达式的编写需要一定的技巧,以确保正确提取所需信息。

  3. 爬虫监控与维护:设计爬虫监控机制,定期检查爬虫运行状态,确保爬虫正常运行,并及时处理异常情况。

  4. 跨站数据整合:本爬虫需要从IMDb和豆瓣两个网站获取数据,并进行整合。需要确保两个网站数据的一致性和准确性。

  5. 数据存储与处理:将爬取到的数据存储到Excel文件中,需要使用openpyxl库进行操作。同时,需要确保数据格式的正确性和可读性。

三、主题页面的结构特征分析

数据源说明

数据源是来自IMDb网站上的电视节目排名,包含了当前最流行的100部电视剧。IMDb是一个在线的电影和电视节目数据库,包含了大量的电影、电视节目、演员、导演、制片人等相关信息。这些信息是由IMDb网站的用户提交和编辑的,因此具有一定的权威性和参考价值。

 https://www.imdb.com/chart/tvmeter/?ref_=nv_tvv_mptv

数据类型为电视节目排名,包含了电视节目的名称、评分、播出年份、排名、导演等信息。数据格式为HTML,需要通过爬虫程序进行解析和提取。

数据格式是一个列表,每个列表元素包含以下信息:

  • 排名
  • 中文名
  • 英文名
  • 导演
  • 年份
  • 评分
  • 排名变化

1.主题页面的结构与特征分析

  1. IMDb主题页面:

   IMDb主题页面的结构主要由以下几个部分组成:

  • 导航栏:包含网站的主要分类,如电影、电视剧、名人等。
  • 搜索框:提供关键词搜索功能。
  • 内容区:包含排行榜、电影、电视剧等信息,以表格或列表形式展示。
  • 侧边栏:包含一些热门电影、电视剧、名人等推荐信息。

     

  IMDb主题页面的特征主要表现在:

  • 数据分布:数据主要分布在表格或列表中,每个表格行或列表项包含了排名、名称、评分、导演、年份等信息。
  • 标签属性:通过分析页面源代码,可以发现各类信息对应的HTML标签和属性,如电影名称对应的是<a>标签,评分对应的是<strong>标签等。

  1. 豆瓣主题页面:

  豆瓣主题页面的结构主要由以下几个部分组成:

  • 导航栏:包含网站的主要分类,如电影、读书、音乐等。
  • 搜索框:提供关键词搜索功能。
  • 内容区:包含排行榜、电影、书籍等信息,以卡片或列表形式展示。
  • 侧边栏:包含一些热门电影、书籍、音乐等推荐信息。

     

  豆瓣主题页面的特征主要表现在:

  • 数据分布:数据主要分布在卡片或列表中,每个卡片或列表项包含了名称、评分、导演、演员等信息。
  • 标签属性:通过分析页面源代码,可以发现各类信息对应的HTML标签和属性,如电影名称对应的是<a>标签,导演对应的是<span>标签等。

2.Htmls 页面解析

代码操作:

  1. pat: 用于匹配<td class="titleColumn">标签内的内容,提取排名、英文名和年份。正则表达式分为3个捕获组:

    • (.*)..*: 匹配排名
    • (.*): 匹配英文名
    • (.*): 匹配年份
  2. pat2: 用于匹配<a>标签内的内容,提取中文名。正则表达式有1个捕获组:

    • (.*?): 匹配中文名
  3. pat3: 用于匹配<span class="subject-cast">标签内的内容,提取导演名。正则表达式有1个捕获组:

    • (.*): 匹配导演名
  4. pat4: 用于匹配<td class="ratingColumn imdbRating">标签内的内容,提取评分。正则表达式有2个捕获组:

    • (.*): 匹配评分标题,包含评分和基于的用户数量
    • (.*): 匹配评分

 

其中对于排名变化的爬取:

(排名上升4)

 

(排名不变)

(排名下降2)

在下面是用来正则表达式匹配排名的变化,

(正则表达式)

  1. up_down_res: 使用find_all_by_pat函数从HTML文档中查找所有匹配up_down_pat正则表达式的结果。

  2. if len(up_down_res) > i: 检查是否有足够的匹配结果来提取第i个电影或电视节目的评分变化信息。

  3. rating_value = up_down_res[i][1]: 从匹配结果中提取评分。

  4. rating_change = up_down_res[i][2]: 从匹配结果中提取评分变化信息。

  5. if rating_change == '(no change)': rating_change = '无变化': 如果评分没有变化,则将评分变化信息设置为“无变化”。

  6. elif rating_change.startswith('('): 如果评分有变化,则检查评分变化信息是否以括号开头。

  7. direction = rating_change[1:4]: 如果评分变化信息以(up(down开头,则提取方向(updown)。

  8. change_value = rating_change[5:-1]: 如果评分变化信息以(up(down开头,则提取变化数量。

  9. if direction == 'up ': rating_change = '上升' + change_value: 如果评分上升,则将评分变化信息设置为“上升”+变化数量。

  10. elif direction == 'dow': rating_change = '下降' + change_value: 如果评分下降,则将评分变化信息设置为“下降”+变化数量。

  11. else: rating_change = '未知': 如果无法确定评分变化信息,则将其设置为“未知”。

(BeautifulSoup库)

  1. rankings_table = soup.find('tbody', class_='lister-list'): 使用find函数从HTML文档中查找包含电影或电视节目排名信息的表格,该表格具有tbody标签和lister-list类。

  2. rows = rankings_table.find_all('tr'): 使用find_all函数从表格中查找所有行。

  3. for row in rows:: 遍历每一行。

  4. rank_column = row.find('td', class_='titleColumn'): 使用find函数从当前行中查找包含电影或电视节目名称和排名信息的单元格,该单元格具有td标签和titleColumn类。

  5. rank_text = row.find('div', class_='velocity').text.strip() if row.find('div', class_='velocity') else 'N/A': 使用find函数从当前行中查找包含排名上升和排名下降信息的div标签,该标签具有velocity类。如果找到了这个标签,则提取文本内容并去除前后空格;否则将rank_text设置为“N/A”。

  6. rank = rank_text.split('\n')[0] if rank_text else 'N/A': 将rank_text按照换行符\n分割成多个字符串,提取第一个字符串作为排名信息,并将其赋值给rank。如果rank_text为空,则将rank设置为“N/A”。

 3.节点(标签)查找方法与遍历方法 (必要时画出节点树结构)

 

节点(标签)查找方法:

  • find_all():查找符合条件的所有节点
  • select():使用CSS选择器查找节点

遍历方法:

  • 需要注意的是,虽然代码中没有使用遍历方法,但是在获取电影信息时,使用了正则表达式对HTML文本进行匹配,也可以视为一种遍历方法。

四、网络爬虫程序设计

1.数据爬取与采集

采集电视剧排名和排名变化

 1 import requests
 2 from bs4 import BeautifulSoup
 3 import openpyxl
 4 
 5 # 发送HTTP请求获取网页内容
 6 url = 'https://www.imdb.com/chart/tvmeter/?ref_=nv_tvv_mptv'
 7 response = requests.get(url)
 8 html_content = response.content
 9 
10 # 使用BeautifulSoup解析网页内容
11 soup = BeautifulSoup(html_content, 'html.parser')
12 
13 # 创建一个Excel工作簿和工作表
14 workbook = openpyxl.Workbook()
15 sheet = workbook.active
16 
17 # 添加列标题
18 sheet.append(['名称', '当前排名', '近期上升情况', '近期下降情况'])
19 
20 # 找到排名和排名变化所在的HTML元素
21 rankings_table = soup.find('tbody', class_='lister-list')
22 rows = rankings_table.find_all('tr')
23 
24 # 提取排名、电影名、排名上升和排名下降数据,并写入Excel工作表
25 for row in rows:
26     rank_column = row.find('td', class_='titleColumn')
27     rank_text = row.find('div', class_='velocity').text.strip() if row.find('div', class_='velocity') else 'N/A'
28     rank = rank_text.split('\n')[0] if rank_text else 'N/A'
29 
30     # 获取电影名信息
31     title_column = rank_column.find('a')
32     title = title_column.text.strip() if title_column else 'N/A'
33 
34     change_column = row.find('div', class_='velocity')
35 
36     up_icon = change_column.find('span', class_='global-sprite titlemeter up')
37     down_icon = change_column.find('span', class_='global-sprite titlemeter down')
38 
39     if up_icon:
40         change = change_column.text.strip().split('\n')[-1].strip('()')
41         decrease = 'N/A'
42     elif down_icon:
43         change = 'N/A'
44         decrease = change_column.text.strip().split('\n')[-1].strip('()')
45     else:
46         change = 'N/A'
47         decrease = 'N/A'
48 
49     sheet.append([title, rank, change, decrease])
50 
51 # 保存Excel文件
52 file_path = 'C:\\Users\\David\\Desktop\\imdb_rankings.xlsx'
53 workbook.save(file_path)
54 print(f"文件已保存至:{file_path}")

 

 采集电视剧名、排名、中文名、导演名、评分

  1 import random
  2 import re
  3 import requests
  4 from openpyxl import Workbook
  5 from time import sleep
  6 import os
  7 
  8 
  9 def find_all_by_pat(pat, string):
 10     res = re.findall(pat, string)
 11     return res
 12 
 13 
 14 def get_html_doc(url):
 15     pro = ['122.152.196.126', '114.215.174.227', '119.185.30.75']
 16     head = {
 17         'user-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64 x64)AppleWebkit/537.36(KHTML,like Gecko) chrome/58.0.3029.110 Safari/537.36'
 18     }
 19     resopnse = requests.get(url, proxies={'http': random.choice(pro)}, headers=head)
 20     resopnse.encoding = 'utf-8'
 21     html_doc = resopnse.text
 22     return html_doc
 23 
 24 
 25 def get_douban_html(query_name):
 26     url = 'https://www.douban.com/search?cat=1002&q=%s' % query_name
 27     douban_search_res = get_html_doc(url)
 28     return douban_search_res
 29 
 30 
 31 def get_chinese_name(pat, doc):
 32     res_list = find_all_by_pat(pat, doc)
 33     try:
 34         return res_list[1]
 35     except:
 36         return ' '
 37 
 38 
 39 def get_director_name(pat, doc):
 40     res = find_all_by_pat(pat, doc)
 41     try:
 42         return res[0].split('/')[1]
 43     except:
 44         return ' '
 45 
 46 
 47 if __name__ == "__main__":
 48     url = "https://www.imdb.com/chart/tvmeter/?ref_=nv_tvv_mptv"
 49     imdb_doc = get_html_doc(url)
 50     pat = r'<td class="titleColumn">\s*(.*)..*\s*.*\s*title=".*" >(.*)</a>.*\s*<span class="secondaryInfo">\((.*)\)</span>'
 51     res = find_all_by_pat(pat, imdb_doc)
 52 
 53     pat2 = r'qcat.*\s*.*>(.*?)\s*</a>'
 54     pat3 = '<span\s*class="subject-cast">(.*)</span>'
 55     pat4 = r'<td class="ratingColumn imdbRating">\s*<strong title="(.*) based on .* user ratings">(.*)</strong>'
 56 
 57     # 创建一个空列表用于存储结果
 58     result = []
 59 
 60     for i in range(len(res)):
 61         doc = get_douban_html(res[i][1])
 62         chinise_name = get_chinese_name(pat2, doc)
 63 
 64         director_name = get_director_name(pat3, doc)
 65 
 66         rating_info = find_all_by_pat(pat4, imdb_doc)[i]
 67         rating = rating_info[1]
 68 
 69         # 查找 up 和 down
 70         up_down_pat = r'<div class="velocity">(\d+)(<span class="secondaryInfo">\((up|down) (\d+)\)</span>|\(no change\))</div>'
 71         up_down_res = find_all_by_pat(up_down_pat, imdb_doc)
 72         if len(up_down_res) > i:
 73             rating_value = up_down_res[i][1]
 74             rating_change = up_down_res[i][2]
 75             if rating_change == '(no change)':
 76                 rating_change = '无变化'
 77             elif rating_change.startswith('('):
 78                 direction = rating_change[1:4]
 79                 change_value = rating_change[5:-1]
 80                 if direction == 'up ':
 81                     rating_change = '上升' + change_value
 82                 elif direction == 'dow':
 83                     rating_change = '下降' + change_value
 84         else:
 85             rating_change = '未知'
 86 
 87 
 88         # 将结果添加到列表中
 89         result.append([str(i + 1), chinise_name, res[i][1], director_name, res[i][2], rating, rating_change])
 90 
 91         # 打印结果
 92         print(result[-1])
 93 
 94         sleep(random.random() * 1.2)
 95 
 96     # 将结果写入 Excel 文件
 97     wb = Workbook()
 98     sheet = wb.active
 99     sheet['A1'] = '排名'
100     sheet['B1'] = '中文名'
101     sheet['C1'] = '英文名'
102     sheet['D1'] = '导演'
103     sheet['E1'] = '年份'
104     sheet['F1'] = '评分'
105     sheet['G1'] = '排名变化'
106     for i in range(len(result)):
107         for j in range(len(result[i])):
108             sheet.cell(row=i + 2, column=j + 1).value = result[i][j]
109     desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')
110     wb.save(desktop_path + '/imdbTv.xlsx')

 2.对数据进行清洗和处理

读取采集的电视剧信息

import pandas as pd

# 读取Excel文件
df2 = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

print(df2.head(10))

  

 删除无效列排名变化

1 #删除无效列‘排名变化’
2 df2.drop('排名变化', axis=1, inplace=True)
3 df2.to_excel('C:/Users/David/Desktop/imdbTv.xlsx', index=False)
4 print(df2.head(10))

 删除带有空值的行

import pandas as pd

# 读取表格数据
df4 = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 删除带有空值的行
df4.dropna(inplace=True)

# 将处理后的数据保存到新的表格文件中
df4.to_excel('C:/Users/David/Desktop/imdbTv.xlsx', index=False)
print(df3.head(70))

 

统计每列中缺失值的数量
import pandas as pd

df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
# 统计每列中缺失值的数量
missing_values_count = df.isnull().sum()

# 打印每列中缺失值的数量
print(missing_values_count)

 

import pandas as pd

df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
# 统计每列中空值的数量
empty_values_count = df.isna().sum()

# 打印每列中空值的数量
print(empty_values_count)

 

3.文本分析(可选):jieba 分词、wordcloud 的分词可视化

jieba分词“中文名”

import pandas as pd
import jieba

# 读取表格数据
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 对中文名进行分词
df['中文名分词'] = df['中文名'].apply(lambda x: ' '.join(jieba.cut(x)))


# 输出分词结果
print(df[['中文名', '中文名分词']])

 

4.数据分析与可视化(例如:数据柱形图、直方图、散点图、盒图、分布图)

import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制评分直方图
plt.hist(df['评分'], bins=10)
plt.xlabel('评分')
plt.ylabel('频数')
plt.title('电视剧评分直方图')
plt.show()

 

 

import
pandas as pd import matplotlib.pyplot as plt # 读取Excel文件 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx') # 绘制排名柱形图 plt.bar(df['排名'], df['评分']) plt.xlabel('排名') plt.ylabel('评分') plt.title('ranking and rating') # 旋转横坐标标签 plt.xticks(rotation=45) # 调整图像大小 plt.figure(figsize=(15, 10)) plt.show() # 绘制年份柱形图 plt.bar(df['年份'], df['评分']) plt.xlabel('年份') plt.ylabel('评分') plt.title('Tv year and rating') # 旋转横坐标标签 plt.xticks(rotation=45) # 调整图像大小 plt.figure(figsize=(15, 10)) plt.show()

 

 
import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制排名和评分的散点图
plt.scatter(df['排名'], df['评分'])
plt.xlabel('排名')
plt.ylabel('评分')
plt.title('电视剧排名和评分散点图')
plt.show()

 

 

电视剧排名和年份散点图

import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制排名和年份的散点图
plt.scatter(df['排名'], df['年份'])
plt.xlabel('排名')
plt.ylabel('年份')
plt.title('电视剧排名和年份散点图')
plt.show()

电视剧评分盒图

import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制评分的盒图
plt.boxplot(df['评分'])
plt.ylabel('评分')
plt.title('电视剧评分盒图')
plt.show()

电视剧年份盒图

import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制年份的盒图
plt.boxplot(df['年份'])
plt.ylabel('年份')
plt.title('电视剧年份盒图')
plt.show()

import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 绘制评分的直方图
plt.hist(df['评分'], bins=10)
plt.xlabel('评分')
plt.ylabel('频数')
plt.title('电视剧评分分布图')
plt.show()

# 绘制年份的直方图
plt.hist(df['年份'], bins=10)
plt.xlabel('年份')
plt.ylabel('频数')
plt.title('电视剧年份分布图')
plt.show()

# 绘制评分和年份的散点图
plt.scatter(df['评分'], df['年份'])
plt.xlabel('评分')
plt.ylabel('年份')
plt.title('电视剧评分和年份散点图')
plt.show()

电视剧排名最近情况散点图

import matplotlib.pyplot as plt
import pandas as pd

# 读取数据
df = pd.read_excel('C:/Users/David/Desktop/imdb_rankings.xlsx')

# 绘制散点图
fig, ax = plt.subplots()
ax.scatter(df['当前排名'], df['近期上升情况'], color='g', label='Up')
ax.scatter(df['当前排名'], df['近期下降情况'], color='r', label='Down')
ax.set_xlabel('Ranking')
ax.set_ylabel('Change')
ax.legend()
plt.show()

电视剧评分饼状图

#IMDB电视剧评分分布
import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 选择评分列数据
ratings = df['评分']

# 将评分数据分为8个区间
bins = [0, 6, 6.5, 7, 7.5, 8, 8.5, 9, 10]
labels = ['6.0以下', '6.0~6.5', '6.5~7.0', '7.0~7.5', '7.5~8.0', '8.0~8.5', '8.5~9.0', '9.0以上']
rating_bins = pd.cut(ratings, bins=bins, labels=labels)

# 统计各个区间的评分数量
rating_counts = rating_bins.value_counts()

# 绘制饼状图
plt.pie(rating_counts.values, labels=rating_counts.index, autopct='%1.1f%%')
plt.title('IMDB电视剧评分分布')
plt.show()

IMDB电视剧年份饼状图

#IMDB电视剧年份分布
import pandas as pd
import matplotlib.pyplot as plt

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 选择年份列数据
years = df['年份']

# 将年份数据分为8个区间
bins = [1950, 1960, 1970, 1980, 1990, 2000, 2010, 2020, 2030]
labels = ['1950s', '1960s', '1970s', '1980s', '1990s', '2000s', '2010s', '2020s']
year_bins = pd.cut(years, bins=bins, labels=labels)

# 统计各个区间的年份数量
year_counts = year_bins.value_counts()

# 绘制饼状图
plt.pie(year_counts.values, labels=year_counts.index, autopct='%1.1f%%')
plt.title('IMDB电视剧年份分布')
plt.show()

 

5.根据数据之间的关系,分析两个变量之间的相关系数,画出散点图,并建立变 量之间的回归方程(一元或多元)。


评分和年份之间关系
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 计算评分和年份之间的相关系数
r, p = stats.pearsonr(df['评分'], df['年份'])
print('评分和年份之间的相关系数为:', r)

# 绘制评分和年份的散点图
plt.scatter(df['评分'], df['年份'])
plt.xlabel('评分')
plt.ylabel('年份')
plt.title('电视剧评分和年份散点图')
plt.show()

# 建立评分和年份之间的线性回归方程
slope, intercept, r_value, p_value, std_err = stats.linregress(df['评分'], df['年份'])
print('评分和年份之间的回归方程为:y =', slope, 'x +', intercept)

 

排名和评分的关系

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 计算相关系数
corr_matrix = df.corr()
print(corr_matrix)

# 绘制散点图
plt.scatter(df['排名'], df['评分'])
plt.xlabel('排名')
plt.ylabel('评分')
plt.title('排名和评分的散点图')
plt.show()

# 建立回归方程
x = df['排名']
y = df['评分']
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
print('回归方程为:y =', slope, 'x +', intercept)

# 绘制回归线
plt.scatter(x, y)
plt.plot(x, slope * x + intercept, color='red')
plt.xlabel('排名')
plt.ylabel('评分')
plt.title('排名和评分的回归线')
plt.show()

排名和年份的关系

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

# 读取Excel文件
df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')

# 计算相关系数
corr_matrix = df.corr()
print(corr_matrix)

# 绘制散点图
plt.scatter(df['排名'], df['年份'])
plt.xlabel('排名')
plt.ylabel('年份')
plt.title('排名和年份的散点图')
plt.show()

# 建立回归方程
x = df['排名']
y = df['年份']
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
print('回归方程为:y =', slope, 'x +', intercept)

# 绘制回归线
plt.scatter(x, y)
plt.plot(x, slope * x + intercept, color='red')
plt.xlabel('排名')
plt.ylabel('年份')
plt.title('排名和年份的回归线')
plt.show()

 


 

6.数据持久化

数据持久化分为以下:

  1. 数据库:将数据存储在关系型数据库(如MySQL、PostgreSQL)或非关系型数据库(如MongoDB、Redis)中,可以使用Python中的ORM(如SQLAlchemy)或直接使用数据库API进行操作。

  2. 文件:将数据存储在本地文件系统中,可以使用Python内置的文件读写操作或第三方库(如Pandas)进行操作。常见的文件格式包括CSV、JSON、XML、Excel等。

  3. 缓存:将数据存储在缓存中,可以使用第三方缓存库(如Redis、Memcached)进行操作,可以提高数据读取速度和性能。

我是将采集的数据保存到Excel中。

7.将以上各部分的代码汇总,附上完整程序代码

 

  1 import random
  2 import re
  3 import requests
  4 from openpyxl import Workbook
  5 from time import sleep
  6 import os
  7 
  8 
  9 def find_all_by_pat(pat, string):
 10     res = re.findall(pat, string)
 11     return res
 12 
 13 
 14 def get_html_doc(url):
 15     pro = ['122.152.196.126', '114.215.174.227', '119.185.30.75']
 16     head = {
 17         'user-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64 x64)AppleWebkit/537.36(KHTML,like Gecko) chrome/58.0.3029.110 Safari/537.36'
 18     }
 19     resopnse = requests.get(url, proxies={'http': random.choice(pro)}, headers=head)
 20     resopnse.encoding = 'utf-8'
 21     html_doc = resopnse.text
 22     return html_doc
 23 
 24 
 25 def get_douban_html(query_name):
 26     url = 'https://www.douban.com/search?cat=1002&q=%s' % query_name
 27     douban_search_res = get_html_doc(url)
 28     return douban_search_res
 29 
 30 
 31 def get_chinese_name(pat, doc):
 32     res_list = find_all_by_pat(pat, doc)
 33     try:
 34         return res_list[1]
 35     except:
 36         return ' '
 37 
 38 
 39 def get_director_name(pat, doc):
 40     res = find_all_by_pat(pat, doc)
 41     try:
 42         return res[0].split('/')[1]
 43     except:
 44         return ' '
 45 
 46 
 47 if __name__ == "__main__":
 48     url = "https://www.imdb.com/chart/tvmeter/?ref_=nv_tvv_mptv"
 49     imdb_doc = get_html_doc(url)
 50     pat = r'<td class="titleColumn">\s*(.*)..*\s*.*\s*title=".*" >(.*)</a>.*\s*<span class="secondaryInfo">\((.*)\)</span>'
 51     res = find_all_by_pat(pat, imdb_doc)
 52 
 53     pat2 = r'qcat.*\s*.*>(.*?)\s*</a>'
 54     pat3 = '<span\s*class="subject-cast">(.*)</span>'
 55     pat4 = r'<td class="ratingColumn imdbRating">\s*<strong title="(.*) based on .* user ratings">(.*)</strong>'
 56 
 57     # 创建一个空列表用于存储结果
 58     result = []
 59 
 60     for i in range(len(res)):
 61         doc = get_douban_html(res[i][1])
 62         chinise_name = get_chinese_name(pat2, doc)
 63 
 64         director_name = get_director_name(pat3, doc)
 65 
 66         rating_info = find_all_by_pat(pat4, imdb_doc)[i]
 67         rating = rating_info[1]
 68 
 69         # 查找 up 和 down
 70         up_down_pat = r'<div class="velocity">(\d+)(<span class="secondaryInfo">\((up|down) (\d+)\)</span>|\(no change\))</div>'
 71         up_down_res = find_all_by_pat(up_down_pat, imdb_doc)
 72         if len(up_down_res) > i:
 73             rating_value = up_down_res[i][1]
 74             rating_change = up_down_res[i][2]
 75             if rating_change == '(no change)':
 76                 rating_change = '无变化'
 77             elif rating_change.startswith('('):
 78                 direction = rating_change[1:4]
 79                 change_value = rating_change[5:-1]
 80                 if direction == 'up ':
 81                     rating_change = '上升' + change_value
 82                 elif direction == 'dow':
 83                     rating_change = '下降' + change_value
 84         else:
 85             rating_change = '未知'
 86 
 87 
 88         # 将结果添加到列表中
 89         result.append([str(i + 1), chinise_name, res[i][1], director_name, res[i][2], rating, rating_change])
 90 
 91         # 打印结果
 92         print(result[-1])
 93 
 94         sleep(random.random() * 1.2)
 95 
 96     # 将结果写入 Excel 文件
 97     wb = Workbook()
 98     sheet = wb.active
 99     sheet['A1'] = '排名'
100     sheet['B1'] = '中文名'
101     sheet['C1'] = '英文名'
102     sheet['D1'] = '导演'
103     sheet['E1'] = '年份'
104     sheet['F1'] = '评分'
105     sheet['G1'] = '排名变化'
106     for i in range(len(result)):
107         for j in range(len(result[i])):
108             sheet.cell(row=i + 2, column=j + 1).value = result[i][j]
109     desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')
110     wb.save(desktop_path + '/imdbTv.xlsx')
111     
112     
113     
114     
115 import pandas as pd
116 
117 # 读取Excel文件
118 df2 = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
119 
120 print(df2.head(10))
121 
122 #删除无效列‘排名变化’
123 df2.drop('排名变化', axis=1, inplace=True)
124 df2.to_excel('C:/Users/David/Desktop/imdbTv.xlsx', index=False)
125 print(df2.head(10))
126 
127 
128 
129 import pandas as pd
130 
131 # 读取表格数据
132 df4 = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
133 
134 # 删除带有空值的行
135 df4.dropna(inplace=True)
136 
137 # 将处理后的数据保存到新的表格文件中
138 df4.to_excel('C:/Users/David/Desktop/imdbTv.xlsx', index=False)
139 print(df3.head(70))
140 
141 
142 
143 import pandas as pd
144 
145 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
146 # 统计每列中缺失值的数量
147 missing_values_count = df.isnull().sum()
148 
149 # 打印每列中缺失值的数量
150 print(missing_values_count)
151 
152 
153 
154 
155 import pandas as pd
156 
157 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
158 # 统计每列中空值的数量
159 empty_values_count = df.isna().sum()
160 
161 # 打印每列中空值的数量
162 print(empty_values_count)
163 
164 
165 
166 import pandas as pd
167 import matplotlib.pyplot as plt
168 
169 # 读取Excel文件
170 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
171 
172 # 绘制评分直方图
173 plt.hist(df['评分'], bins=10)
174 plt.xlabel('评分')
175 plt.ylabel('频数')
176 plt.title('电视剧评分直方图')
177 plt.show()
178 
179 
180 
181 import pandas as pd
182 import matplotlib.pyplot as plt
183 
184 # 读取Excel文件
185 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
186 
187 # 绘制排名柱形图
188 plt.bar(df['排名'], df['评分'])
189 plt.xlabel('排名')
190 plt.ylabel('评分')
191 plt.title('ranking and rating')
192 
193 # 旋转横坐标标签
194 plt.xticks(rotation=45)
195 
196 # 调整图像大小
197 plt.figure(figsize=(15, 10))
198 
199 plt.show()
200 
201 # 绘制年份柱形图
202 plt.bar(df['年份'], df['评分'])
203 plt.xlabel('年份')
204 plt.ylabel('评分')
205 plt.title('Tv year and rating')
206 
207 # 旋转横坐标标签
208 plt.xticks(rotation=45)
209 
210 # 调整图像大小
211 plt.figure(figsize=(15, 10))
212 
213 plt.show()
214 
215 
216 
217 import pandas as pd
218 import matplotlib.pyplot as plt
219 
220 # 读取Excel文件
221 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
222 
223 # 绘制排名和评分的散点图
224 plt.scatter(df['排名'], df['评分'])
225 plt.xlabel('排名')
226 plt.ylabel('评分')
227 plt.title('电视剧排名和评分散点图')
228 plt.show()
229 
230 
231 
232 
233 import pandas as pd
234 import matplotlib.pyplot as plt
235 
236 # 读取Excel文件
237 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
238 
239 # 绘制排名和年份的散点图
240 plt.scatter(df['排名'], df['年份'])
241 plt.xlabel('排名')
242 plt.ylabel('年份')
243 plt.title('电视剧排名和年份散点图')
244 plt.show()
245 
246 
247 
248 
249 import pandas as pd
250 import matplotlib.pyplot as plt
251 
252 # 读取Excel文件
253 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
254 
255 # 绘制评分的盒图
256 plt.boxplot(df['评分'])
257 plt.ylabel('评分')
258 plt.title('电视剧评分盒图')
259 plt.show()
260 
261 
262 
263 
264 import pandas as pd
265 import matplotlib.pyplot as plt
266 
267 # 读取Excel文件
268 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
269 
270 # 绘制年份的盒图
271 plt.boxplot(df['年份'])
272 plt.ylabel('年份')
273 plt.title('电视剧年份盒图')
274 plt.show()
275 
276 
277 
278 
279 import pandas as pd
280 import matplotlib.pyplot as plt
281 from scipy import stats
282 
283 # 读取Excel文件
284 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
285 
286 # 计算评分和年份之间的相关系数
287 r, p = stats.pearsonr(df['评分'], df['年份'])
288 print('评分和年份之间的相关系数为:', r)
289 
290 # 绘制评分和年份的散点图
291 plt.scatter(df['评分'], df['年份'])
292 plt.xlabel('评分')
293 plt.ylabel('年份')
294 plt.title('电视剧评分和年份散点图')
295 plt.show()
296 
297 # 建立评分和年份之间的线性回归方程
298 slope, intercept, r_value, p_value, std_err = stats.linregress(df['评分'], df['年份'])
299 print('评分和年份之间的回归方程为:y =', slope, 'x +', intercept)
300 
301 
302 
303 
304 
305 
306 
307 import pandas as pd
308 import numpy as np
309 import matplotlib.pyplot as plt
310 from scipy import stats
311 
312 # 读取Excel文件
313 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
314 
315 # 计算相关系数
316 corr_matrix = df.corr()
317 print(corr_matrix)
318 
319 # 绘制散点图
320 plt.scatter(df['排名'], df['评分'])
321 plt.xlabel('排名')
322 plt.ylabel('评分')
323 plt.title('排名和评分的散点图')
324 plt.show()
325 
326 # 建立回归方程
327 x = df['排名']
328 y = df['评分']
329 slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
330 print('回归方程为:y =', slope, 'x +', intercept)
331 
332 # 绘制回归线
333 plt.scatter(x, y)
334 plt.plot(x, slope * x + intercept, color='red')
335 plt.xlabel('排名')
336 plt.ylabel('评分')
337 plt.title('排名和评分的回归线')
338 plt.show()
339 
340 
341 
342 
343 import pandas as pd
344 import numpy as np
345 import matplotlib.pyplot as plt
346 from scipy import stats
347 
348 # 读取Excel文件
349 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
350 
351 # 计算相关系数
352 corr_matrix = df.corr()
353 print(corr_matrix)
354 
355 # 绘制散点图
356 plt.scatter(df['排名'], df['年份'])
357 plt.xlabel('排名')
358 plt.ylabel('年份')
359 plt.title('排名和年份的散点图')
360 plt.show()
361 
362 # 建立回归方程
363 x = df['排名']
364 y = df['年份']
365 slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
366 print('回归方程为:y =', slope, 'x +', intercept)
367 
368 # 绘制回归线
369 plt.scatter(x, y)
370 plt.plot(x, slope * x + intercept, color='red')
371 plt.xlabel('排名')
372 plt.ylabel('年份')
373 plt.title('排名和年份的回归线')
374 plt.show()
375 
376 
377 
378 
379 import matplotlib.pyplot as plt
380 import pandas as pd
381 
382 # 读取数据
383 df = pd.read_excel('C:/Users/David/Desktop/imdb_rankings.xlsx')
384 
385 # 绘制散点图
386 fig, ax = plt.subplots()
387 ax.scatter(df['当前排名'], df['近期上升情况'], color='g', label='Up')
388 ax.scatter(df['当前排名'], df['近期下降情况'], color='r', label='Down')
389 ax.set_xlabel('Ranking')
390 ax.set_ylabel('Change')
391 ax.legend()
392 plt.show()
393 
394 
395 
396 
397 #IMDB电视剧评分分布
398 import pandas as pd
399 import matplotlib.pyplot as plt
400 
401 # 读取Excel文件
402 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
403 
404 # 选择评分列数据
405 ratings = df['评分']
406 
407 # 将评分数据分为8个区间
408 bins = [0, 6, 6.5, 7, 7.5, 8, 8.5, 9, 10]
409 labels = ['6.0以下', '6.0~6.5', '6.5~7.0', '7.0~7.5', '7.5~8.0', '8.0~8.5', '8.5~9.0', '9.0以上']
410 rating_bins = pd.cut(ratings, bins=bins, labels=labels)
411 
412 # 统计各个区间的评分数量
413 rating_counts = rating_bins.value_counts()
414 
415 # 绘制饼状图
416 plt.pie(rating_counts.values, labels=rating_counts.index, autopct='%1.1f%%')
417 plt.title('IMDB电视剧评分分布')
418 plt.show()
419 
420 
421 
422 
423 #IMDB电视剧年份分布
424 import pandas as pd
425 import matplotlib.pyplot as plt
426 
427 # 读取Excel文件
428 df = pd.read_excel('C:/Users/David/Desktop/imdbTv.xlsx')
429 
430 # 选择年份列数据
431 years = df['年份']
432 
433 # 将年份数据分为8个区间
434 bins = [1950, 1960, 1970, 1980, 1990, 2000, 2010, 2020, 2030]
435 labels = ['1950s', '1960s', '1970s', '1980s', '1990s', '2000s', '2010s', '2020s']
436 year_bins = pd.cut(years, bins=bins, labels=labels)
437 
438 # 统计各个区间的年份数量
439 year_counts = year_bins.value_counts()
440 
441 # 绘制饼状图
442 plt.pie(year_counts.values, labels=year_counts.index, autopct='%1.1f%%')
443 plt.title('IMDB电视剧年份分布')
444 plt.show()

 

五、总结(10 分)

1.经过对主题数据的分析与可视化,可以得到哪些结论?是否达到预期的目标?

美剧依然是最受欢迎的类型,其中《权力的游戏》、《绝命毒师》、《黑镜》等热门美剧排名靠前。英剧和日剧在受欢迎程度上也占有一定的份额,例如《神探夏洛克》、《行尸走肉》、《攻壳机动队》等。近年来,随着OTT平台的兴起,来自Netflix、Amazon Prime Video、HBO等平台的原创剧集越来越受欢迎,例如《怪奇物语》、《纸牌屋》、《西部世界》等。近年来的剧集普遍受到观众和评论家的欢迎,例如2010年以后的剧集评分普遍较高,其中不乏《权力的游戏》、《绝命毒师》、《黑镜》等经典剧集。20世纪90年代和2000年代初期的剧集评分也较高,例如《越狱》、《老友记》、《逝去的爱丽丝》等。80年代和70年代的剧集评分相对较低,但仍有一些经典剧集,例如《辛普森一家》、《霹雳游侠》等。50年代和60年代的剧集评分普遍较低,但这个时期的电视剧还处于发展初期,剧集类型和制作水平都有限。总体而言,随着电视剧的发展和进步,剧集的质量和评分也在不断提高。

2.在完成此设计过程中,得到哪些收获?以及要改进的建议?

收获:

  1. 程序中使用了豆瓣网站的搜索功能,可能会被豆瓣网站的反爬虫机制识别为恶意行为,要进行一些特殊处理:比如设置请求头等。
  2. 程序中使用了代理IP,如果代理IP不可用或被封禁,可能会导致程序无法正常运行。
  3. 程序中使用了随机等待时间,以减少对目标网站的访问频率,建议不要过度频繁地运行程序。

改进:

  1. 增加多线程或异步处理功能,以便提高程序的爬取效率和速度。

  2. 可以加入日志记录功能,以便于排查错误和跟踪程序运行情况。可以使用 Python 自带的 logging 模块,将程序运行过程中的关键信息记录到日志文件中,以便于后续分析和处理