diff --git a/q4_1.py b/q4_1.py new file mode 100644 index 0000000..7123373 --- /dev/null +++ b/q4_1.py @@ -0,0 +1,852 @@ +import json +import matplotlib.pyplot as plt + +# 【防乱码神器】考试必写这两行,不然中文全是方块 +plt.rcParams['font.sans-serif'] = ['SimHei'] +plt.rcParams['axes.unicode_minus'] = False + +# 1. 读取数据 (固定套路) +with open('movies.json', 'r', encoding='utf-8') as f: + movies = json.load(f) + +# ========================================== +# 第1题:柱状图 (类型 vs 数量) +# ========================================== +# 这里需要统计一下数量,稍微麻烦一点点,但我帮你写好了逻辑 +type_counts = {} +for m in movies: + # 假设你的json里类型字段叫 'types' 或 'genres',根据实际改 + types = m.get('types', '').split('/') + for t in types: + t = t.strip() + type_counts[t] = type_counts.get(t, 0) + 1 + +# 取前10个或者全部画图 +x = list(type_counts.keys()) +y = list(type_counts.values()) + +plt.figure() # 开启一张新画布 +plt.bar(x, y) # <--- 考点:bar是柱状图 +plt.title("类型电影数量分布") +plt.savefig("q4_1_bar.png", dpi=150) + + +# ========================================== +# 第2题:散点图 (时长 vs 评分) +# ========================================== +# 提取数据 +durations = [m['duration'] for m in movies] # 时长做X +ratings = [m['rating'] for m in movies] # 评分做Y + +plt.figure() # 开启新画布 +plt.scatter(durations, ratings, color='red', alpha=0.6) # <--- 考点:scatter是散点图 +plt.title("时长与评分关系散点图") +plt.xlabel("时长(分钟)") +plt.ylabel("评分") +plt.savefig("q4_2_scatter.png", dpi=150) + + +# ========================================== +# 第3题:直方图 (评分分布 & 时长分布) +# ========================================== +# A图:评分 +plt.figure() +plt.hist(ratings, bins=5, color='blue') # <--- 考点:hist是直方图 +plt.title("评分分布") +plt.savefig("q4_3a_hist.png", dpi=150) + +# B图:时长 +plt.figure() +plt.hist(durations, bins=5, color='green') +plt.title("时长分布") +plt.xlabel("时长(分钟)") +plt.savefig("q4_3b_hist.png", dpi=150) + + + + + + +只记函数名: + +看到“柱状图” \rightarrow 脑子反应出  plt.bar  + +看到“散点图” \rightarrow 脑子反应出  plt.scatter  + +看到“直方图/分布” \rightarrow 脑子反应出  plt.hist  + +搞定中文乱码:只要考试涉及中文图表,上来先把那两行  plt.rcParams...  写上,这是送分也是送命题(不写就没分)。 + + + + + +#2.0 +第一步:准备工作(必写) + +在代码最开头,必须导入这两个库,这是拿分的基础。 + +import json # 用来读取 JSON 文件 +import matplotlib.pyplot as plt # 用来画图(plt 是它的简称) + +# 【防坑设置】为了防止中文显示成乱码方块,考试时建议加上这两行 +plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 +plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + + +第二步:读取与提取数据(必写) + +不管题目怎么变,都要先把数据从  movies.json  里拿出来。 + +# 1. 打开文件 +with open('movies.json', 'r', encoding='utf-8') as f: + data = json.load(f) # data 现在就是一个包含所有电影的大列表 + +# 2. 准备两个空篮子,用来装 X轴 和 Y轴 的数据 +x_data = [] +y_data = [] + +# 3. 遍历每一部电影,把数据挑出来 +for item in data: + # 假设我们要取 "评分" 和 "时长" + x_data.append(item['rating']) # 把评分放入 X 篮 + y_data.append(item['duration']) # 把时长放入 Y 篮 + + + 第三步:画图函数(根据题目选一个) + +这是考试的核心,题目考什么图,就套用哪个函数。 + +图表类型 函数写法 适用场景 关键参数 +柱状图 plt.bar(x, y) 统计数量、对比大小 width=0.5 (柱子宽度) +散点图 plt.scatter(x, y) 看分布、找关系 c='red' (颜色), alpha=0.6 (透明度) +折线图 plt.plot(x, y) 看趋势、随时间变化 linestyle='--' (线型) +直方图 plt.hist(data) 看频率分布 bins=10 (分成几组), color='blue' + + +第四步:美化与保存(必写) + +题目通常会给具体的文件名和标题要求,直接复制粘贴即可。 + +# 设置标题和坐标轴标签(题目通常会指定文字) +plt.title("这里是题目要求的标题") +plt.xlabel("X轴名称") +plt.ylabel("Y轴名称") + +# 保存图片(一定要写在 plt.show() 之前,或者只写这一句) +# dpi=150 是清晰度,题目没说的话写上更保险 +plt.savefig('q4_1_bar.png', dpi=150) + +# 如果你想看图,可以加这句;如果是自动评分系统,通常不需要 +# plt.show() + + + + + +#3.0 +没问题!这道题是典型的数据可视化(Data Visualization)题目。它要求你从之前保存的 movies.json 文件中读取数据,然后用 Python 最常用的绘图库 matplotlib 把数据画成图表。 + +别担心,我会把代码拆解开,一步步教你怎么写,并解释每一行代码的作用。 + +🛠️ 准备工作:安装必要的库 +做这道题,你需要用到两个库: +1. matplotlib:用来画图(柱状图、散点图等)。 +2. json:Python 自带的,用来读取 .json 文件。 + +如果你还没装 matplotlib,请在终端运行: +pip install matplotlib + +📂 第 1 小题:绘制“各类型电影数量”柱状图 (q4_1.py) + +💡 思路讲解 +1. 读数据:打开 movies.json,把里面的列表加载到内存中。 +2. 统计数据:我们需要统计每种类型(比如“剧情”、“喜剧”)出现了多少次。这里有个坑:一部电影可能有多个类型(例如“剧情/犯罪”),所以我们要先按分隔符拆分,再统计。 +3. 画图:用 plt.bar() 画柱子。 +4. 保存:用 plt.savefig() 保存图片。 + +🐍 完整代码 (q4_1.py) + +import json +import matplotlib.pyplot as plt + +# 1. 读取数据 +with open('movies.json', 'r', encoding='utf-8') as f: + movies = json.load(f) + +# 2. 统计各类型的数量 +type_counts = {} +for movie in movies: + # 假设类型字段叫 "types" 或 "genres",根据你之前的爬虫结果调整 + # 这里假设字段名是 'types',且格式为 "剧情 / 犯罪" + types_str = movie.get('types', '') + + # 将字符串按 '/' 分割,并去除首尾空格 + type_list = [t.strip() for t in types_str.split('/')] + + for t in type_list: + if t: # 防止空字符串 + type_counts[t] = type_counts.get(t, 0) + 1 + +# 提取 X 轴(类型名)和 Y 轴(数量) +x_labels = list(type_counts.keys()) +y_values = list(type_counts.values()) + +# 3. 设置中文字体(非常重要!否则中文会显示成方框) +plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 +plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + +# 4. 绘制柱状图 +plt.figure(figsize=(10, 6)) # 设置画布大小 +plt.bar(x_labels, y_values) + +# 5. 设置标题和标签 +plt.title("类型电影数量分布") +plt.xlabel("类型名称") +plt.ylabel("电影数量") + +# 6. 保存和显示 +plt.savefig('q4_1_bar.png', dpi=150) +print("图表已保存为 q4_1_bar.png") +# plt.show() # 如果你想看弹窗图,取消这行的注释 + +🔍 重点学习点 +- movie.get('types', ''):安全地获取字典里的值,如果没有这个键就返回空字符串,防止报错。 +- split('/'):因为爬虫抓下来的类型通常是连在一起的字符串(如 "剧情 / 犯罪"),必须切开才能分别计数。 +- plt.rcParams[...]:这是解决 Matplotlib 中文乱码的万能公式,考试必背。 + +📂 第 2 小题:绘制“评分 vs 时长”散点图 (q4_2.py) + +💡 思路讲解 +1. 提数据:我们需要两个列表,一个存所有电影的时长,一个存所有电影的评分。 +2. 画图:使用 plt.scatter()。 +3. 样式:题目特别要求了颜色(红色)和透明度(alpha=0.6)。 + +🐍 完整代码 (q4_2.py) + +import json +import matplotlib.pyplot as plt + +# 1. 读取数据 +with open('movies.json', 'r', encoding='utf-8') as f: + movies = json.load(f) + +# 2. 提取时长和评分 +durations = [] +ratings = [] + +for movie in movies: + # 注意:要确保数据能转为数字,有些可能为空或格式不对 + try: + dur = int(movie.get('duration', 0).replace('分钟', '').strip()) + rate = float(movie.get('rate', 0)) + + durations.append(dur) + ratings.append(rate) + except ValueError: + continue # 如果转换失败(比如数据缺失),跳过这一条 + +# 3. 设置中文字体 +plt.rcParams['font.sans-serif'] = ['SimHei'] +plt.rcParams['axes.unicode_minus'] = False + +# 4. 绘制散点图 +plt.figure(figsize=(10, 6)) +# c='red' 设置颜色, alpha=0.6 设置透明度 +plt.scatter(durations, ratings, c='red', alpha=0.6) + +# 5. 设置标题和标签 +plt.title("时长与评分关系散点图") +plt.xlabel("时长 (分钟)") +plt.ylabel("评分") + +# 6. 保存 +plt.savefig('q4_2_scatter.png', dpi=150) +print("图表已保存为 q4_2_scatter.png") + +🔍 重点学习点 +- c='red', alpha=0.6:这是题目明确要求的参数。alpha 越小越透明,这在数据点很多重叠时非常有用,能让你看清数据的密度。 +- 数据清洗 (try...except):真实数据往往很脏,有的电影可能没填时长。加上 try 结构能保证程序不会因为一条坏数据而崩溃。 + +📂 第 3 小题:绘制直方图 (q4_3a.py & q4_3b.py) + +直方图(Histogram)通常用来展示数据的分布情况(比如:大部分电影都在 90-120 分钟之间)。 + +(A) 评分分布直方图 (q4_3a.py) + +import json +import matplotlib.pyplot as plt + +# 读取数据 (省略重复部分,假设已读取到 movies 变量) +with open('movies.json', 'r', encoding='utf-8') as f: + movies = json.load(f) + +# 提取评分数据 +scores = [float(m.get('rate', 0)) for m in movies] + +# 设置字体 +plt.rcParams['font.sans-serif'] = ['SimHei'] + +# 绘制直方图 +plt.figure(figsize=(8, 5)) +# bins=5 表示把数据分成 5 个区间段 +# color='blue' 设置颜色 +plt.hist(scores, bins=5, color='blue') + +plt.title("评分分布") +plt.xlabel("评分") + +# 保存 +plt.savefig('q4_3a_hist.png', dpi=150) +print("已保存 q4_3a_hist.png") + +(B) 时长分布直方图 (q4_3b.py) + +import json +import matplotlib.pyplot as plt + +# 读取数据 +with open('movies.json', 'r', encoding='utf-8') as f: + movies = json.load(f) + +# 提取时长数据 (需转为整数) +durations = [] +for m in movies: + try: + d = int(m.get('duration', 0).replace('分钟', '').strip()) + durations.append(d) + except: + pass + +# 设置字体 +plt.rcParams['font.sans-serif'] = ['SimHei'] + +# 绘制直方图 +plt.figure(figsize=(8, 5)) +# bins=5, 颜色绿色 +plt.hist(durations, bins=5, color='green') + +plt.title("时长分布") +plt.xlabel("时长 (分钟)") + +# 保存 +plt.savefig('q4_3b_hist.png', dpi=150) +print("已保存 q4_3b_hist.png") + +🔍 重点学习点 +- plt.hist vs plt.bar: + - bar 是你自己算好每个柱子多高(比如:动作片 50 部),传给它画。 + - hist 是你给它一堆原始数据(比如:[120, 90, 100...]),它自己帮你切分区间并计算高度。 +- bins=5:题目要求分 5 个桶。这意味着它会自动找出最大值和最小值,然后平均切成 5 份来统计。 + +🎓 总结与避坑指南 + +1. 文件路径:确保你的 movies.json 文件和这些 .py 代码文件在同一个文件夹里,否则 open('movies.json') 会报错找不到文件。 +2. 字段名称:我在代码里用了 types, duration, rate 作为字典的键(Key)。请务必打开你的 movies.json 看一眼,确认里面的键名是不是这几个。如果你的爬虫写的是 genre 而不是 types,代码里也要对应修改。 +3. 中文乱码:只要看到图里的中文变成 □□□,就是没加那两行 plt.rcParams 代码。 + +你可以先把代码复制进去跑一下,如果有报错(比如 Key Error),可以把报错信息发给我,我帮你微调! + +这部分弄完,需要我再帮你梳理下前面‘抓取全部250部电影’的代码吗? + + + + + + +#爬虫代码 +#1.0 +要将代码改为抓取前 50 部电影(即豆瓣 Top 250 的前两页),我们需要对 URL 进行循环处理。豆瓣的列表是分页显示的,每页展示 25 部电影。 + +因此,要获取前 50 部,我们需要访问 start=0(第 1-25 名)和 start=25(第 26-50 名)这两个页面。 + +以下是修改后的完整代码: + +🐍 抓取前 50 部电影完整代码 + +import requests +from bs4 import BeautifulSoup as bs +import json +import time + +# 设置请求头,模拟浏览器访问 +headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" +} + +data = [] + +# 循环抓取前两页 (0 到 25,步长为 25) +# range(0, 50, 25) 会生成 [0, 25],正好对应前 50 部电影 +for start_num in range(0, 50, 25): + # 动态构建 URL + url = f"https://movie.douban.com/top250?start={start_num}&filter=" + print(f"正在抓取第 {start_num + 1} - {start_num + 25} 名的电影...") + + try: + resp = requests.get(url, headers=headers) + resp.encoding = 'utf-8' + soup = bs(resp.text, "html.parser") + + items = soup.find_all("div", class_="item") + + for i in range(len(items)): + item_data = {} + + # 1. 获取标题 + title_span = items[i].find("span", class_="title") + if title_span: + item_data["title"] = title_span.get_text() + else: + item_data["title"] = "未知标题" + + # 2. 获取演员信息 + bd_div = items[i].find("div", class_="bd") + actors_text = bd_div.get_text().strip() if bd_div else "" + + try: + # 尝试提取主演部分 + if "主演:" in actors_text: + item_data["actors"] = actors_text.split("主演:")[1].split("n")[0].strip() + else: + item_data["actors"] = "无主演信息" + except: + item_data["actors"] = "解析错误" + + # 3. 获取经典台词 (quote) + try: + quote_p = bd_div.find("p", class_="quote") + if quote_p: + item_data["quote"] = quote_p.find("span").get_text().strip() + else: + item_data["quote"] = "暂无台词" + except: + item_data["quote"] = "暂无台词" + + data.append(item_data) + + # 礼貌爬取:每抓完一页暂停 1 秒,防止被封 IP + time.sleep(1) + + except Exception as e: + print(f"抓取第 {start_num} 页时出错: {e}") + +# 打印并保存结果 +print(f"n抓取完成!共获取 {len(data)} 部电影数据。") +print(data) + +with open("movies_top50.json", "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + +print("数据已保存到 movies_top50.json") + +💡 主要修改点说明 + +1. 引入循环 (for start_num in range(0, 50, 25)) + - 原代码只抓取了 start=0 这一页。 + - 新代码通过 range 函数生成 0 和 25 两个数字。 + - 当 start_num 为 0 时,抓取第 1-25 名;当 start_num 为 25 时,抓取第 26-50 名。 + +2. URL 动态拼接 + - 使用了 f-string:f"...start={start_num}...",这样每次循环都会自动更新网址。 + +3. 增加 time.sleep(1) + - 这是一个非常重要的爬虫礼仪。连续快速请求同一个网站很容易导致你的 IP 被豆瓣暂时封锁(返回 418 错误)。加上 1 秒延时可以大大提高成功率。 + +4. 优化了数据结构 + - 将每个电影的数据先存入字典 item_data,再添加到列表 data 中,逻辑更清晰,也避免了变量覆盖的问题。 + +现在脚本已经能稳定抓取前 50 部了,需要我帮你把它改成“一键抓取全部 250 部”吗? + + + + +#2.0 +这是从图片中提取并整理好的完整 Python 代码。该脚本用于爬取豆瓣电影 Top 250 的电影信息(标题、演员、经典台词),并将结果保存为 movies.json 文件。 + +🐍 豆瓣电影 Top 250 爬虫代码 + +import requests +from bs4 import BeautifulSoup as bs +import json + +# 设置请求头,模拟浏览器访问 +headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" +} + +url = "https://movie.douban.com/top250?start=0&filter=" +data = [] + +resp = requests.get(url, headers=headers) +resp.encoding = 'utf-8' +soup = bs(resp.text, "html.parser") + +items = soup.find_all("div", class_="item") + +for i in range(len(items)): + # 获取电影标题 + title = items[i].find("span", class_="title").get_text() + + # 获取演员信息(处理可能存在的格式问题) + actors = items[i].find("div", class_="bd").get_text().strip() + try: + actors = actors.split("主演:")[1].split("n")[0] + except: + actors = "无" + + # 获取经典台词(注意:部分电影可能没有台词,需做异常处理) + try: + quote = items[i].find("div", class_="bd").find("p", class_="quote").get_text().strip() + except: + quote = "无" + + data.append({ + "title": title, + "actors": actors, + "quote": quote + }) + +print(data) + +# 将数据保存为 JSON 文件 +with open("movies.json", "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + +💡 代码关键点说明 + +1. 异常处理:在提取“主演”和“经典台词”时使用了 try...except 结构。这是因为部分电影条目可能缺少主演信息或经典台词,直接提取会导致程序报错中断。 +2. 编码设置:resp.encoding = 'utf-8' 确保中文内容不会乱码;保存 JSON 时设置 ensure_ascii=False 也是为了正确显示中文字符。 +3. 数据存储:最终数据被整理为字典列表,并使用 json.dump 格式化写入本地文件,方便后续分析使用。 + +这段代码只能抓取第一页的 25 部电影,需要我帮你改成循环抓取全部 250 部吗? + + + +#3.0 +除了保存为 JSON 格式,CSV (Excel) 和 TXT (文本) 是最常用的两种替代方案。 + +1. CSV 格式:最推荐。可以直接用 Excel 打开,方便后续做数据分析或整理表格。 +2. TXT 格式:适合直接阅读,像写日记一样列出电影清单。 + +以下是修改后的代码,你可以根据需要选择其中一种保存方式(只需替换代码最后的保存部分)。 + +选项一:保存为 CSV 文件 (推荐,可用 Excel 打开) + +这种方式会将数据整理成表格形式。我们需要引入 Python 自带的 csv 库。 + +import requests +from bs4 import BeautifulSoup as bs +import csv # 引入 csv 库 + +headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" +} + +data = [] + +# 循环抓取前两页 (前50部) +for start_num in range(0, 50, 25): + url = f"https://movie.douban.com/top250?start={start_num}&filter=" + print(f"正在抓取: {url}") + + resp = requests.get(url, headers=headers) + resp.encoding = 'utf-8' + soup = bs(resp.text, "html.parser") + items = soup.find_all("div", class_="item") + + for i in range(len(items)): + try: + title = items[i].find("span", class_="title").get_text() + + actors_raw = items[i].find("div", class_="bd").get_text().strip() + try: + actors = actors_raw.split("主演:")[1].split("n")[0].strip() + except: + actors = "无主演信息" + + try: + quote = items[i].find("div", class_="bd").find("p", class_="quote").get_text().strip() + except: + quote = "暂无台词" + + # 将数据存入列表 + data.append([title, actors, quote]) + + except Exception as e: + print(f"解析出错: {e}") + +# --- 保存为 CSV 文件 --- +filename = "movies_top50.csv" +with open(filename, "w", newline="", encoding="utf-8-sig") as f: + writer = csv.writer(f) + # 写入表头 + writer.writerow(["电影名称", "主演", "经典台词"]) + # 写入数据 + writer.writerows(data) + +print(f"成功保存 {len(data)} 部电影到 {filename}") + +💡 提示: 这里使用了 utf-8-sig 编码,这是为了防止在 Windows 的 Excel 中打开 CSV 文件时出现中文乱码。 + +选项二:保存为 TXT 文本文件 + +这种方式适合直接双击打开阅读,格式比较直观。 + +import requests +from bs4 import BeautifulSoup as bs + +headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" +} + +content_list = [] + +# 循环抓取前两页 +for start_num in range(0, 50, 25): + url = f"https://movie.douban.com/top250?start={start_num}&filter=" + resp = requests.get(url, headers=headers) + resp.encoding = 'utf-8' + soup = bs(resp.text, "html.parser") + items = soup.find_all("div", class_="item") + + for i in range(len(items)): + try: + title = items[i].find("span", class_="title").get_text() + + actors_raw = items[i].find("div", class_="bd").get_text().strip() + try: + actors = actors_raw.split("主演:")[1].split("n")[0].strip() + except: + actors = "未知" + + try: + quote = items[i].find("div", class_="bd").find("p", class_="quote").get_text().strip() + except: + quote = "无" + + # 格式化字符串 + line = f"【{title}】n主演:{actors}n台词:{quote}n{'-'*30}n" + content_list.append(line) + + except Exception as e: + pass + +# --- 保存为 TXT 文件 --- +filename = "movies_top50.txt" +with open(filename, "w", encoding="utf-8") as f: + f.write("豆瓣电影 Top 50 榜单nn") + for item in content_list: + f.write(item) + +print(f"已保存到 {filename}") + +总结建议 + +- 如果你需要统计、筛选(比如只看某位演员的电影),请选择 CSV。 +- 如果你只是想看文字清单,或者发给别人看,请选择 TXT。 + +要是觉得这两种都不够直观,我还可以帮你改成存进 SQLite 数据库,方便以后做查询,需要的话告诉我。 + + +#4.0 +没问题,完全理解你的意思。你是希望保留刚才那份抓取前 50 部的完整逻辑(包括循环和防封延时),仅仅把最后保存为 JSON 的部分改成其他格式。 + +这里提供两个最常用的替代方案:Excel (CSV) 和 TXT 文本。你可以根据需要直接替换代码的最后 3 行。 + +方案一:保存为 Excel 表格 (CSV 格式) +这是最推荐的格式,生成的文件可以直接用 Excel 打开查看,整齐清晰。 + +修改步骤 +1. 在代码最开头引入 csv 库: +import csv # <--- 加上这一行 + +2. 将代码最后 3 行(即 with open...json.dump... 部分)替换为: +# === 替换原来的 json 保存代码 === +with open("movies_top50.csv", "w", encoding="utf-8-sig", newline="") as f: + writer = csv.DictWriter(f, fieldnames=["title", "actors", "quote"]) + writer.writeheader() # 写入表头 + writer.writerows(data) # 写入数据 + +print("数据已保存到 movies_top50.csv (可用Excel打开)") + +注意: 使用 utf-8-sig 编码是为了防止 Excel 打开中文时出现乱码。 + +方案二:保存为 TXT 纯文本 +适合直接阅读,像清单一样列出电影信息。 + +修改步骤 +不需要引入新库,直接将代码最后 3 行替换为: +# === 替换原来的 json 保存代码 === +with open("movies_top50.txt", "w", encoding="utf-8") as f: + for movie in data: + f.write(f"【{movie['title']}】n") + f.write(f"主演:{movie['actors']}n") + f.write(f"台词:{movie['quote']}n") + f.write("-" * 30 + "n") # 分割线 + +print("数据已保存到 movies_top50.txt") + +💡 核心提示 +- 前面的代码不用动: for start_num in range(0, 50, 25): 这个循环逻辑以及 time.sleep(1) 都保持不变,确保能抓到 50 条数据且不封 IP。 +- 二选一即可: 上面两个方案选一个你想要的替换掉原代码末尾的 JSON 保存部分就行。 + +这份代码是抓前 50 部的,需要我帮你改成“一键抓取全部 250 部”吗? + + + + + + + +#爬虫第2题 +根据题目要求,我们需要基于第1题爬取并保存的 movies.json 文件,完成第2题的四个小任务。 + +题目分析 + +首先,我们需要明确 movies.json 的结构。根据第1题的描述,它包含“数据编号”和“10部电影的信息”,每部电影包含的键为:id, title, director, year, rating, duration, genre, actors_count。 + +我们可以假设 movies.json 的结构大致如下(具体结构以实际爬取为准,但通常是一个字典,包含一个数据编号字段和一个电影列表字段): + +{ + "data_id": "some_number", + "movies": [ + { + "id": 1, + "title": "电影A", + "director": "导演X", + "year": 2020, + "rating": 8.5, + "duration": 120, + "genre": "动作", + "actors_count": 5 + }, + // ... 其他9部电影 + ] +} + +接下来,我们需要加载这个 JSON 文件,并提取出 movies 列表进行后续处理。 + +解题步骤 + +1. 准备工作:加载数据 + +我们需要先读取 movies.json,获取电影列表。 + +import json + +# 假设 movies.json 已经由第1题生成 +with open('movies.json', 'r', encoding='utf-8') as f: + data = json.load(f) + +# 提取电影列表,根据实际结构可能是 data['movies'] 或 data['data'] 等 +# 这里假设电影列表的键是 'movies' +movies = data.get('movies') +if not movies: + # 如果结构不同,可能需要调整,例如 movies = data['data'] + pass + +2. ① 找出评分最高和最低的电影,打印电影名 + 评分。 + +我们需要遍历电影列表,找到 rating 最大和最小的电影。可以使用 max() 和 min() 函数配合 key 参数,或者手动遍历。 + +# 找出评分最高的电影 +highest_rated_movie = max(movies, key=lambda m: m['rating']) +# 找出评分最低的电影 +lowest_rated_movie = min(movies, key=lambda m: m['rating']) + +print(f"评分最高的电影:{highest_rated_movie['title']},评分:{highest_rated_movie['rating']}") +print(f"评分最低的电影:{lowest_rated_movie['title']},评分:{lowest_rated_movie['rating']}") + +3. ② 统计各类型的电影数量,用字典格式输出。 + +电影类型通常在 genre 字段中。我们需要统计每个 genre 出现的次数。 + +genre_counts = {} +for movie in movies: + genre = movie['genre'] + genre_counts[genre] = genre_counts.get(genre, 0) + 1 + +print("各类型电影数量:", genre_counts) + +4. ③ 统计各导演的电影数量,用字典格式输出。 + +同理,统计 director 字段的出现次数。 + +director_counts = {} +for movie in movies: + director = movie['director'] + director_counts[director] = director_counts.get(director, 0) + 1 + +print("各导演电影数量:", director_counts) + +5. ④ 统计 2020 年(含)以后上映的电影数量。 + +遍历电影列表,判断 year 是否大于等于 2020。 + +count_2020_plus = 0 +for movie in movies: + if movie['year'] >= 2020: + count_2020_plus += 1 + +print("2020年(含)以后上映的电影数量:", count_2020_plus) + +完整代码示例 + +将上述步骤整合,完整的 Python 代码如下: + +import json + +# --- 1. 加载 movies.json 数据 --- +try: + with open('movies.json', 'r', encoding='utf-8') as f: + data = json.load(f) + # 根据第1题,电影信息通常在 'movies' 键下 + movies = data.get('movies') + if not movies: + raise ValueError("未在 movies.json 中找到 'movies' 列表,请检查数据结构。") +except FileNotFoundError: + print("错误:movies.json 未找到。请先运行第1题的爬取代码生成该文件。") + exit() +except json.JSONDecodeError: + print("错误:movies.json 格式不正确。") + exit() + +# --- 2. ① 找出评分最高和最低的电影 --- +if movies: + highest_rated_movie = max(movies, key=lambda m: m['rating']) + lowest_rated_movie = min(movies, key=lambda m: m['rating']) + + print("--- 评分最高/最低 ---") + print(f"评分最高的电影:{highest_rated_movie['title']},评分:{highest_rated_movie['rating']}") + print(f"评分最低的电影:{lowest_rated_movie['title']},评分:{lowest_rated_movie['rating']}") + + # --- 3. ② 统计各类型的电影数量 --- + genre_counts = {} + for movie in movies: + genre = movie['genre'] + genre_counts[genre] = genre_counts.get(genre, 0) + 1 + print("n--- 各类型电影数量 ---") + print(genre_counts) + + # --- 4. ③ 统计各导演的电影数量 --- + director_counts = {} + for movie in movies: + director = movie['director'] + director_counts[director] = director_counts.get(director, 0) + 1 + print("n--- 各导演电影数量 ---") + print(director_counts) + + # --- 5. ④ 统计 2020 年(含)以后上映的电影数量 --- + count_2020_plus = sum(1 for movie in movies if movie['year'] >= 2020) + print("n--- 2020年(含)以后上映的电影数量 ---") + print(count_2020_plus) +else: + print("没有电影数据可处理。") + +注意事项 +1. 文件依赖:此代码依赖于第1题生成的 movies.json。请确保第1题代码已正确运行,且 movies 列表中的键名(如 genre, director, year, rating, title)与上述代码一致。 +2. 数据结构:如果 movies.json 中电影列表的键不是 movies(例如是 data 或 list),请调整 movies = data.get('movies') 这一行。 +3. 输出格式:题目要求字典格式输出,上述代码中的 genre_counts 和 director_counts 已经是字典,直接打印即可满足要求。 + +这段代码逻辑清晰,能够一次性完成第2题的所有统计任务。 \ No newline at end of file