大家好,我是北山啦。
@toc
利用Python对豆瓣电影Top250电影进行爬取,收集相关的信息,并且利用Python进行数据分析,获取'排名','电影名称','导演','上映年份','制作国家','类型','评分','评价分数','短评'等字段,探索相关的数据。
数据获取
翻页操作
代码语言:python代码运行次数:0复制#https://beishan.blog.csdn.net/article/details/112735850
第一页:https://movie.douban.com/top250
第二页:https://movie.douban.com/top250?start=25&filter=
第三页:https://movie.douban.com/top250?start=50&filter=
观察可知,我们只需要修改start参数即可
headers字段
headers中有很多字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫
通过headers中的User-Agent字段来
- 原理:默认情况下没有User-Agent,而是使用模块默认设置
- 解决方法:请求之前添加User-Agent即可;更好的方式是使用User-Agent池来解决(收集一堆User-Agent的方式,或者是随机生成User-Agent)
在这里我们只需要添加请求头即可
数据定位
这里我使用的是xpath
代码语言:python代码运行次数:0复制# -*- coding: utf-8 -*-
# @Author: Kun
import requests
from lxml import etree
import pandas as pd
df = []
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4343.0 Safari/537.36',
'Referer': 'https://movie.douban.com/top250'}
columns = ['排名','电影名称','导演','上映年份','制作国家','类型','评分','评价分数','短评']
def get_data(html):
xp = etree.HTML(html)
lis = xp.xpath('//*[@id="content"]/div/div[1]/ol/li')
for li in lis:
"""排名、标题、导演、演员、"""
ranks = li.xpath('div/div[1]/em/text()')
titles = li.xpath('div/div[2]/div[1]/a/span[1]/text()')
directors = li.xpath('div/div[2]/div[2]/p[1]/text()')[0].strip().replace("xa0xa0xa0","t").split("t")
infos = li.xpath('div/div[2]/div[2]/p[1]/text()')[1].strip().replace('xa0','').split('/')
dates,areas,genres = infos[0],infos[1],infos[2]
ratings = li.xpath('.//div[@class="star"]/span[2]/text()')[0]
scores = li.xpath('.//div[@class="star"]/span[4]/text()')[0][:-3]
quotes = li.xpath('.//p[@class="quote"]/span/text()')
for rank,title,director in zip(ranks,titles,directors):
if len(quotes) == 0:
quotes = None
else:
quotes = quotes[0]
df.append([rank,title,director,dates,areas,genres,ratings,scores,quotes])
d = pd.DataFrame(df,columns=columns)
d.to_excel('Top250.xlsx',index=False)
for i in range(0,251,25):
url = "https://movie.douban.com/top250?start={}&filter=".format(str(i))
res = requests.get(url,headers=headers)
html = res.text
get_data(html)
结果如下:
- 使用面向对象 线程
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 2 15:19:29 2021
@author: 北山啦
"""
import pandas as pd
import time
import requests
from lxml import etree
from queue import Queue
from threading import Thread, Lock
class Movie():
def __init__(self):
self.df = []
self.headers ={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4343.0 Safari/537.36',
'Referer': 'https://movie.douban.com/top250'}
self.columns = ['排名','电影名称','导演','上映年份','制作国家','类型','评分','评价分数','短评']
self.lock = Lock()
self.url_list = Queue()
def get_url(self):
url = 'https://movie.douban.com/top250?start={}&filter='
for i in range(0,250,25):
self.url_list.put(url.format(str(i)))
def get_html(self):
while True:
if not self.url_list.empty():
url = self.url_list.get()
resp = requests.get(url,headers=self.headers)
html = resp.text
self.xpath_parse(html)
else:
break
def xpath_parse(self,html):
xp = etree.HTML(html)
lis = xp.xpath('//*[@id="content"]/div/div[1]/ol/li')
for li in lis:
"""排名、标题、导演、演员、"""
ranks = li.xpath('div/div[1]/em/text()')
titles = li.xpath('div/div[2]/div[1]/a/span[1]/text()')
directors = li.xpath('div/div[2]/div[2]/p[1]/text()')[0].strip().replace("xa0xa0xa0","t").split("t")
infos = li.xpath('div/div[2]/div[2]/p[1]/text()')[1].strip().replace('xa0','').split('/')
dates,areas,genres = infos[0],infos[1],infos[2]
ratings = li.xpath('.//div[@class="star"]/span[2]/text()')[0]
scores = li.xpath('.//div[@class="star"]/span[4]/text()')[0][:-3]
quotes = li.xpath('.//p[@class="quote"]/span/text()')
for rank,title,director in zip(ranks,titles,directors):
if len(quotes) == 0:
quotes = None
else:
quotes = quotes[0]
self.df.append([rank,title,director,dates,areas,genres,ratings,scores,quotes])
d = pd.DataFrame(self.df,columns=self.columns)
d.to_excel('douban.xlsx',index=False)
def main(self):
start_time = time.time()
self.get_url()
th_list = []
for i in range(5):
th = Thread(target=self.get_html)
th.start()
th_list.append(th)
for th in th_list:
th.join()
end_time = time.time()
print(end_time-start_time)
if __name__ == '__main__':
spider = Movie()
spider.main()
数据分析
获取数据后,就可以对自己感兴趣的内容进行分析了
数据预处理
代码语言:python代码运行次数:0复制df = pd.read_excel("Top250.xlsx",index_col=False)
df.head()
- 上映年份格式不统一year = [] for i in df["上映年份"]: i = i[0:4] year.append(i) df["上映年份"] = year df["上映年份"].value_counts() x1 = list(df["上映年份"].value_counts().sort_index().index) y1 = list(df["上映年份"].value_counts().sort_index().values) y1 = [str(i) for i in y1]
上映年份分布
代码语言:python代码运行次数:0复制from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.faker import Faker
c1 = (
Bar()
.add_xaxis(x1)
.add_yaxis("影片数量", y1)
.set_global_opts(
title_opts=opts.TitleOpts(title="Top250年份分布"),
datazoom_opts=opts.DataZoomOpts(),
)
.render("1.html")
)
- 这里可以看出电影TOP250里,电影的上映年份,多分布于80年代以后。其中有好几年是在10部及以上的。
- 从年份的分布情况看,大部分高分电影都上映在 1987 年之后,并且随着时间逐渐增加,而近两年的高分电影的数量相对比较少。评分分布情况
plt.figure(figsize=(10,6))
plt.hist(list(df["评分"]),bins=8,facecolor="blue", edgecolor="black", alpha=0.7)
plt.show()
- 从上图分析,随着评分升高,排名也基本靠前,评分主要集中在 8.4~9.2 之间。同时可以通过 pandas 计算平均数,众数和相关系数,平均分为 8.83 分,众数为 8.7 分,而相关系数为 -0.6882,评分与排名强相关。
- 大多分布于「8.5」到「9.2」之间。最低「8.3」,最高「9.6」排名与评分分布情况
plt.figure(figsize=(10,5), dpi=100)
plt.scatter(df.index,df['评分'])
plt.show()
总的来说,排名越靠前,评价人数越多,并且分数也越高。
评论人数TOP10
代码语言:python代码运行次数:0复制c2 = (
Bar()
.add_xaxis(df1["电影名称"].to_list())
.add_yaxis("评论数", df1["评价分数"].to_list(),color=Faker.rand_color())
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(title_opts=opts.TitleOpts(title="电影评论Top10"))
.render("2.html")
)
让我们来看看人气最高的有哪些影片,你又看过几部呢?
导演排名
可以看到这些导演很