豆瓣电影TOP250爬虫获取和数据可视化

前期准备:python、Anaconda(用到的是安装完成后的Spyder)、Jupyter Notebook(主要用于数据可视化,因为画图比较方便)

需要用到的库:pandas、numpy、matplotlib、requests、BeautifulSoup、re、time、collections

爬取网站:https://movie.douban.com/top250

OK,Let's go.

首先分析目标网站的URL

第二页的URL是这样的:

https://movie.douban.com/top250?start=25&filter=

第三页的URL是这样的:

https://movie.douban.com/top250?start=50&filter=

可以发现每一页的URL除了''start=''后面的数字之外,都是一样的,数字的变化规律是每一页增加25,并且从0开始到225结束,一共10页。

接下来就可以解析URL得到HTML页面了,用到的库是requests和BeautifulSoup,并设置爬取的间隔时间。

for i in range(0,250,25):

print(i)

headers={

'Accept':'application/json, text/javascript, */*; q=0.01',

'Accept-Encoding':'gzip, deflate, br',

'Accept-Language':'zh-CN,zh;q=0.8',

'Connection':'keep-alive',

'Referer':'http://www.baidu.com/link?url=_andhfsjjjKRgEWkj7i9cFmYYGsisrnm2A-TN3XZDQXxvGsM9k9ZZSnikW2Yds4s&wd=&eqid=c3435a7d00006bd600000003582bfd1f'

}

url='https://movie.douban.com/top250?start='+str(i)+'&filter='

html=requests.get(url=url,headers=headers).text

time.sleep(0.5)

douban=BeautifulSoup(html,'html.parser')

可以看到我在获取HTML之前构造了一个头部文件headers,为的是模拟浏览器访问网页,降低封禁风险。

接下来就要从获取的页面信息中提取出想要的信息,并保存在列表中。这一段的心路历程可以写一本书了......

首先获取电影的排名和评分。这两项的获取比较容易,因为它们的节点中杂乱的数据较少。只需要使用find_all()函数找到对应的节点和子节点,直接获取就好了,并将获取到的数据存入对应的列表中。

rank=douban.find_all('em',attrs={'class':''})

for i in rank:

r=i.string

ranknum.append(r)

score=douban.find_all('span',attrs={'class':'rating_num'})

for i in score:

s=i.string

filmscore.append(s)

接下来获取电影名,别名,评价人数和一句话短评。这些数据的节点大致相同,子节点不同,数据较整齐。选择定位大的节点,再一步步往下定位小节点的方法,并配合正则表达式筛选出需要的正确字段。

movie_list=douban.find('ol',attrs={'class':'grid_view'})

for movie in movie_list.find_all('li'):

上述代码找到所有的

标签,以下所有的数据都可以从该标签中获取。

#电影名

name=movie.find('span',attrs={'class':'title'}).get_text()

filmname.append(name)

#别名

other=movie.find('span',attrs={'class':'other'}).get_text()

o=re.findall(r'\xa0/\xa0(.*?)\s+\/',other)

'''获取的数据为列表类型,需要转化为字符串才能判断如果为空字符串'''

if ''.join(o).strip() == '' :

othername.append('无')

else:

othername.append(''.join(o))

#评价人数

comment=movie.find('div',attrs={'class':'star'})

num=re.findall(r'\d+',str(comment))[-1]#匹配一个或多个数字

commentnum.append(num)

#短评

quote=movie.find('span',attrs={'class':'inq'})

if quote is None:#如果为空

quoteword.append('无短评')

else:

q=quote.get_text()

quoteword.append(q)

上述代码需要注意的就是当正则表达式匹配不到字段的时候,如果不给其赋值,会导致最后的汇总时出现各个列表长度不一样的错误,所以需要判断是否为空。

下面获取导演、演员、上映时间、上映地区和影片类型,这里是难点所在。因为所有的信息都保存在一个标签当中,我们需要用正则表达式将标签进行合理的拆分再得到对应的信息。并且最好将得到的网页源代码转化为文本文件,因为源码中的空格表示符 会给正则表达式的匹配带来麻烦。可以选择先转化一个页面,看看其空格的处理方式,会给正则表达式的书写带来方便。

info=movie.find('div',attrs={'class':'bd'})#获取所有信息

#导演

director=re.findall(r'\n\n\s+(.*?)\xa0\xa0\xa0',info.text)

if director is None:

director_list.append('无')

else:

director_list.append(''.join(director))

#转化为字符串保存,避免出现多维数组

#上映时间

show_time=re.findall(r'\n\s+(\d+)\xa0/\xa0',info.text)

if show_time is None:

showtime.append('无')

else:

showtime.append(''.join(show_time))

#演员

actors=re.findall(r'\xa0\xa0\xa0(.*?)\n\s+',info.text)

if actors is None:

actors_list.append('无')

else:

actors_list.append(''.join(actors))

#上映地区

place=re.findall('\xa0/\xa0(.*?)\xa0/\xa0',info.text)

if place is None:

showplace.append('无')

else:

showplace.append(''.join(place))

#影片类型

kind=re.findall(r'\xa0/\xa0.*?\xa0/\xa0(.*?)\n\s+',info.text)

if kind is None:

filmkind.append('无')

else:

filmkind.append(''.join(kind))

接下来就是整合数据。将所有得到的数组用DataFrame类型存储到一个变量中,再保存在CSV文件中。这里更倾向于将文件保存为CSV文件而非Excel文件,虽然都可以用excel打开。具体原因可以自行百度。

data=pd.DataFrame({'排名':ranknum,'电影名':filmname,'别名':othername,'评分':filmscore,

'评价人数':commentnum,'概述':quoteword,'导演':director_list,

'演员':actors_list,'上映时间':showtime,'上映地区':showplace,

'类型':filmkind},columns=['排名','电影名','评分','上映时间','上映地区',

'别名','概述','导演','演员','评价人数','类型'])

data.to_csv('豆瓣电影TOP250.csv')

有了数据,就可以对数据做一些可视化的处理。接下来的所有步骤都是用Jupyter Notebook完成的。因为相对于Spyder来说,Jupyter Notebook在数据可视化方面更加方便。

初始化。获取数据,删去无用的列,设置图像能够正常显示中文和正负号。

data=pd.read_csv(r'D:\pycode\豆瓣电影TOP250.csv',engine='python')

plt.rcParams['font.sans-serif'] = ['STXihei'] #用来正常显示中文标签

plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

del data['Unnamed: 0']

data.head()

不同评分的电影数量

a=data.groupby('评分')['评分'].count()

x1=np.arange(len(a.index))

y1=a.values

a.plot(kind='bar',x=a.index,y=a.values,colormap='summer',figsize=(12,4),alpha=0.6)

plt.xlabel('评分')

plt.ylabel('数量')

plt.title('不同评分电影数量')

plt.grid(True,linestyle='--',alpha=0.3)

for i,j in zip(x1,y1):

plt.text(i-0.1,j+0.2,'%.0f'%j,color='grey',alpha=0.7)

电影上映时间的分布。X轴:时间,Y轴:数量,散点的大小代表了该年上映电影的平均评分。

可以从图中看到1980年前的电影占据了少数部分,但评分明显高于1980年后的平均评分。电影的高产和高质量时期在1990-2000年之间。

b=data.groupby('上映时间')['上映时间'].count()

c=data.groupby('上映时间')['评分'].mean()

plt.scatter(x=b.index,y=b.values,c=c.values,cmap='Reds')

plt.xlabel('上映时间')

plt.ylabel('数量')

plt.title('电影上映时间分布')

plt.xticks(range(1930,2020,10))

plt.grid(True,linestyle='--',alpha=0.4)

各地区的TOP250上映数量。直接通过groupby函数分类计算各地区上映电影数。我们先来看一下绘图的效果。

可以发现,Y轴的数量特别多,使得整张图表看起来非常不整洁,原因是每一部电影的上映地区可能有很多个,但groupby将每一个多元素的数组当成一个整体进行了计算。依旧附上代码。

d=data.groupby('上映地区')['上映地区'].count()

d.plot(kind='barh',x=d.values,y=d.index,figsize=(12,12),cmap='summer',alpha=0.5)

plt.xlabel('数量')

plt.ylabel('上映地区')

plt.title('各地区上映')

plt.grid(True,linestyle='--',alpha=0.3)

对上图进行改进。方法是将原本的单个数组中的多个元素分割,使其一个数组中只包含一个元素。再通过计数得到各个地区上映TOP250电影的数量。

可以看到,美国占据了250部中的143部,超过了半数。英国法国德国日本和中国香港都有不错的表现。这张图可以从侧面反映出各个地区电影制片水平的高低,数量越多,水平越高。

place=data['上映地区']

place_list=[]

for i in place:

a=i.split(' ')

place_list.extend(a)

#place_list=array(place_list)

#place_list.flatten()

#place_list

from collections import *

d=defaultdict(int)

for i in place_list:

d[i]+=1

counts=pd.Series(d)

counts.plot(kind='barh',x=counts.values,y=counts.index,figsize=(12,12),cmap='summer',alpha=0.5)

plt.xlabel('数量')

plt.ylabel('上映地区')

plt.title('各地区上映TOP250电影数量')

plt.grid(True,linestyle='--',alpha=0.3)

a=counts.values

b=range(len(counts.index))

for i,j in zip(a,b):

plt.text(i+0.2,j-0.2,'%.0f'%i,color='grey')

以上就是全部。(提取密码:lr11)

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180512G0RQBD00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券