爬取豆瓣电影Top250进行数据清洗并存储到MongoDB
目录
- 一、数据爬取
- 准备
- 网页结构分析
- 完成爬取
- 导出数据
- 二、数据清洗
- 数据预处理
- 数据可视化
- 三、数据存储
- 验证
- 分析总结:
一、数据爬取
准备
1.在进行数据爬取之前我们需要用到两个库:
- requests
- lxml
安装:
lxml解析库:
pip install lxml
HTTP 请求库:
pip install requests

这里是在AnacondaPowerShell里安装好了
网页结构分析
- 导入模块
# 导入模块
import requests
from lxml import etree
- 存储目标url和headers请求头
打开豆瓣电影top250网址:https://movie.douban.com/top250?start=0&filter=

按键盘F12或鼠标右键检查打开网页开发者工具

headers 作用是设置合适的请求头信息来模拟真实浏览器行为,避过一些简单的反爬虫信息
# 目标url
url = 'https://movie.douban.com/top250?start=0&filter='
# headers
headers = {
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
}
注意: headers要用字典形式存储
- 对目标网址发送请求并获取返回结果的页面解析
# 发送请求
html = requests.get('https://movie.douban.com/top250?start=1&filter=', headers=headers)
# 将网页内容实例化
tree = etree.HTML(x.text)
- 使用xpath语法选择指定html标签

通过审查元素可以看到的标签是整块信息的父级元素
# 用xpath表达式查询所有 class属性为 info 的div元素
info = tree.xpath('//div[@class="info"]')
通过print(info) 打印我们发现他返回的结果是一个列表的形式
- 通过循环遍历的方式将info里面的每个子类的查找出来
for item in info:
# 电影名称
name = item.xpath('./div[@class="hd"]/a/span/text()')[0]
# 导演
director = item.xpath('./div[@class="bd"]/p/text()')[0]
# 演员
act = item.xpath('./div[@class="bd"]/p/text()')
print(name)
print(director)
print(act)
break
item.xpath('./div[@class="hd"]/a/span/text()') 查找info下的div=hd的元素里子标签a下的span标签


此处打印出来的结果仍是一个列表形式,可以用下标的形式选取第一个元素即电影名称,但可以看到导演与演员信息是在同一个p标签里的。我们用split字符串方法将导演与演员信息分割开来
- 获取电影名称、导演、主演、上映年份、国籍、类型、评分、评价人数
for item in info:
# 电影名称
name = item.xpath('./div[@class="hd"]/a/span/text()')[0]
# 导演
director = item.xpath('./div[@class="bd"]/p/text()')[0].split('导演:')[1].split('主演:')[0]
# 主演
act = item.xpath('./div[@class="bd"]/p/text()')[0].split('导演:')[1].split('主演:')[1]
# 上映年份
year = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[0]
# 国籍
nationality = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[1]
# 类型
genre = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[2]
# 评分
score = item.xpath('./div[@class="bd"]/div/span[2]/text()')[0]
# 评价人数
num_score = item.xpath('./div[@class="bd"]/div/span[4]/text()')[0]
# print(name)
# print(director)
print(year)
print(nationality)
print(genre)
print(act)
print(score)
print(num_score)

以上我们打算把整页面的所以电影信息获取下来,但出现了一个列表超出索引长度的报错
IndexError: list index out of range
通过查看网页结构发现有时候会没有主演信息

我们给获取主演的代码添加异常处理代码块try语句
# 主演
try:
act = item.xpath('./div[@class="bd"]/p/text()')[0].split('导演:')[1].split('主演:')[1]
# IndexError捕获当列表或序列超出索引范围时,抛出err异常
except IndexError as err:
print('无主演信息')
至此我们获取到了第一页的电影排行数据,那如何获取到剩下的24页呢?
通过翻页对比url地址发现他的页数与地址中的start有关,(当前页 -1) 25*



可以使用遍历10次总共的250部电影数据
完成爬取
# 导入模块
import requests
from lxml import etree
import pandas as pd
# 定义一个列表存储排行榜信息
movies_top250 = []
# 遍历10次,从第1页开始,到第10页结束
for i in range(1, 11):
# 目标url
url = f'https://movie.douban.com/top250?start={(i - 1) * 25}&filter='
# 添加浏览器请求头信息,避过简单的反爬措施
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
}
# 发送请求信息,将返回内容存储到html变量
html = requests.get(url, headers=headers)
# 将html解析为etree._Element对象
tree = etree.HTML(html.text)
# 用xpath表达式查询所有 class属性为 info 的div元素
divs = tree.xpath('//div[@class="info"]')
# 爬取操作
for item in divs:
# 定义一个字典存取电影字段与对应的数值
dic = {}
# 电影名称
name = item.xpath('./div[@class="hd"]/a/span/text()')[0]
# 存放电影名称
dic['电影名称'] = name
# 导演
director = item.xpath('./div[@class="bd"]/p/text()')[0].split('导演:')[1].split('主演:')[0]
dic['导演'] = director
# 主演
try:
act = item.xpath('./div[@class="bd"]/p/text()')[0].split('导演:')[1].split('主演:')[1]
dic['主演'] = act
# IndexError捕获当列表或序列超出索引范围时,抛出err异常
except IndexError as err:
print('无主演信息')
# 上映年份
year = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[0]
dic['上映年份'] = year
# 国籍
nationality = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[1]
dic['国籍'] = nationality
# 类型
genre = item.xpath('./div[@class="bd"]/p/text()')[1].split('/')[2]
dic['类型'] = genre
# 评分
score = item.xpath('./div[@class="bd"]/div/span[2]/text()')[0]
dic['评分'] = director
# 评价人数
num_score = item.xpath('./div[@class="bd"]/div/span[4]/text()')[0]
dic['评价人数'] = num_score
# 最后把 dic 字典数据添加到电影列表movies_top250
movies_top250.append(dic)
导出数据
# 将 movies_top250 转换成表格型数据结果
df = pd.DataFrame(movies_top250)
# 将表格导出到当前目录下,豆瓣电影Top250.xlsx
df.to_excel('豆瓣电影Top250.xlsx', index=False) # index=False 不保存行索引
运行无报错

在python文件目录下可以看到我们已经成功导出数据,打开查看数据并无缺少


注:数据导出成Excel表格形式需要用到pandas模块
二、数据清洗
数据清洗部分此处用Jupyter编写,打开Jupyter
- 导入库并对pyplo设置合适字体
# 导入库
import pandas as pd
import matplotlib.pyplot as plt
from pymongo import MongoClient
# 设置pyplot的rc参数
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置字体
如果运行报错

附几个库安装命令:
1.pandas安装
pip install pandas
2.matolotlib安装
pip install matolotlib
3.pymongo安装
pip install pymongo

安装完成
注:安装完成后需重启一遍内核
数据预处理
1.读取数据
# 读取数据
data = pd.read_excel('./豆瓣电影Top250.xlsx')
# 查看头3行
data.head(3)
查看头3行发现, 上映年份和 类型列有爬取不干净的无意义字符存在

查看数据原文件发现大量数据前后存在空格

2.处理前后空格和换行符
# 替换 上映年份 列和 类型 列的换行符\n
data['上映年份'] = data['上映年份'].str.replace('\n', '') # 用str.replace()方法查找并替换为空
data['类型'] = data['类型'].str.replace('\n', '')
# 删除 导演,主演,上映年份,国籍,类型 列的前后空格
data['导演'] = data['导演'].str.strip() # 用str.strip()方法删除前后空格
data['主演'] = data['主演'].str.strip()
data['上映年份'] = data['上映年份'].str.strip()
data['国籍'] = data['国籍'].str.strip()
data['类型'] = data['类型'].str.strip()
3.查找并替换缺失值
查看缺失值数量并求和
# 查看缺失值数量
data.isnull().sum()

以上可以看到主演列总共有12项缺失值
填充缺失值为"无主演信息"
# 填充缺失值
data['主演'] = data['主演'].fillna("无主演信息")
# 查看填充情况
data[data['主演'] == '无主演信息']

成功替换缺失值
4.导出处理完后的正常数据
# 导出清洗过后的数据
data.to_excel('./豆瓣电影Top250_2.xlsx',index=False)

数据可视化
1.查看上映年份列
data['上映年份'].values

发现有几个年份不是纯数字
2.对上映年份列进行数值处理
# 把年份列转出int类型,并修改异常值1982(中国大陆)为常规年份
for i in data['上映年份'].index:
data['上映年份'][i] = int(data['上映年份'][i].split('(')[0]) # 遍历整列数据进行替换,用str.split()方法对括号(切割成列表取索引0第一个。对每条数据转成int型
3.将上映年份分组聚合
# 将上映年份分组
bins = [1900,1990,2000,2010,2024] # 按'1900-1990','1991-2000','2001-2010','2011-2024' 切片
labels = ['1900-1990','1991-2000','2001-2010','2011-2024'] # 按 labels 作字段名
data_1 = data['上映年份'].groupby(pd.cut(data['上映年份'],bins = bins,labels=labels)).count() # 对切片数据分组并对分组结果进行 计次

4.画折线图
# 基于分组年份与电影上映数量画折线图
plt.figure(figsize=(12,6),dpi=980)
# 标题
plt.title('历年高分电影上映趋势图')
# x轴标签
plt.xlabel('上映年份')
# y轴标签
plt.ylabel('电影数量')
# 添加数据标注
for i in range(len(data_1)):
plt.text(data_1.index[i],data_1[i] * 1.01,data_1[i],c='green')
plt.plot(data_1, marker='o',c='red')
# 保存图表到当前目录下
plt.savefig('1.png')
# 显示图表
plt.show()

三、数据存储
# 创建client客户端,连接到MongoDB,使用本地连接,默认端口27017
client = MongoClient('localhost',27017)
# 连接到或创建一个 movies 数据库
db = client['movies']
# 连接到或创建一个 top250 集合
collection = db['top250']
# 向集合 top250 插入数据
docs = data.to_dict(orient='records') # orient='records' 值是包含每行数据的字典,字典的键是列名。
collection.insert_many(docs)

存储完成
验证
打开MongoDB
查看数据库
show dbs

切换数据库
use movies
查看集合
show collections

查看集合内容头5行
db.top250.find().pretty().limit(5)

可以看到已经成功写入到MongoDB数据库了
分析总结:
读下图得知,从90年到2010年前的高质量电影上榜率一直是水平上升的,直到2010年后到至今开始直线下滑。

参考
更多推荐
所有评论(0)