R与《“大数据”评评2015年国产电影》豆瓣数据抓取
最近发了份电影的评论,发表了一些浅见,今天特地整理了一下文中电影数据的来源,给缺乏动手能力的童鞋参考参考。《“大数据”评评2015年国产电影》文运用到2000到2015年豆瓣共47000部电影数据。
先说说怎么抓取数据吧。
首先一起看看豆瓣这个网址http://www.douban.com/tag/2000/movie?start=0,有两个数字要注意一下,2000 指的是2000年的意思,0代表的是第0部电影。我们在爬取其他年份的数据时,除了这两个数据外,其他的部分都是固定不变的。
这里需要用到XML包和数据整理包plyr包,用readLines函数抓取网页源码:
library(XML)
library(plyr)
for(i in 1:100){
url1<-paste(\&\#39\;http://www.douban.com/tag/2000/movie?start=\&\#39\;,15*i,sep="")#记得修改年份
web1 <-readLines(url1,encoding="UTF-8")
web<-c(web,web1)
}
save(web, file ="webtotal.Rdata")
经亲测,发现当爬取的页面超过100页时,豆瓣会封IP。所以如果要爬取的数据量比较大的话,尽量每爬100页就换一次IP地址,实在懒得去换的话,可以分几次去爬。另外,如果不小心被封了IP也不用担心,第二天就会解封的。
把数据爬下来后已经成功了一半了(木有错,就是这么几行代码),然后我们要用正则表达式把我们感兴趣的内容抓出来(方法不分高低,使用就行嘿嘿),不知道什么是正则表达式的百度一下。
首先,我们要找出我们感兴趣的数据附近都有什么标志符。如电影名附近:西西里的美丽传说 Malèna ,除了电影名称外,其他如评分,评价人数,电影属性(电影属性包括导演、演员、国家、日期)等就在电影名称所在行的下面。需要注意的是,有些电影并没有评分数据,或者没有评论人数。如果先把电影名全部提取,再提取评分,我们会发现,数据错位了,无法将名称和评分绑定在一起,速7的评分可能就对上了大话天仙(还记得2014年的国产lan片吗)的评分。观察一下我们发现大约每部电影的信息共占有12行。因此,我们首先要把每一部电影相关的行全部抓取出来,放在list的同一个值内。
#load("D:/BF/Desktop/豆瓣电影/webtotal.Rdata")
temp1 <-grep(\&\#39\;class="title" target="_blank"\&\#39\;,webtotal)
film <- list()
for (i in (1:length(temp1))) {
film[i] <-list(webtotal[temp1[i]:(temp1[i] + 12)])
这样list值的编码就代表一部电影,而且电影相关的所有数据都在这个值内。
下面我们就可以逐个提取信息了。
首先是电影名称:
name <-webtotal[grep(\&\#39\;class="title"target="_blank">\&\#39\;,webtotal)]
#提取含有电影名称的行,一般电影都有名称,笑话
mname <- list()
for (i in (1:length(name))) {
y <- regexpr(\&\#39\;>.{1,200}<\&\#39\;, name[i],TRUE)#返回的结果为模式之前的字符数和模式的长度
z <- y + attr(y,"match.length")-1
mname[i] <- substr(name[i], y+1, z-1)#提取y,z之间的字符
}
name <-unlist(mname)
regexpr函数很有用,它返回的是模式之前有多少个字符和模式本身有多长,我们要的电影名称就在>十二公民<尖嘴符号之间,这样我们根据它返回的数字就可以用substr函数到电影名称所在行提起电影名称了,substr提取字符串两个参数之间的字符,最后我们将电影名称放在了name向量中。
其次提取电影评分:
pingfen <- list()
for (i in (1:length(film))) {
x <- unlist(film[i])
if (length(x[grep(\&\#39\;spanclass="rating_nums"\&\#39\;,x)]) == 0)
pingfen[i] <- ""
else pingfen[i] <- x[grep(\&\#39\;spanclass="rating_nums"\&\#39\;,x)]
}#提取含有评分的部分,并保证没有评分的电影该部分赋值为""
score <- list()
for (i in (1:length(pingfen))) {
y <- regexpr(\&\#39\;>.{1,10}<\&\#39\;,pingfen[i], TRUE)#返回的结果为模式之前的字符数和模式的长度
z <- y + attr(y,"match.length")-1
score[i] <- substr(pingfen[i], y+1, z-1)#提取y,z之间的字符
}#提取评分
score <-as.numeric(unlist(score))
filmData <-data.frame(name, score, stringsAsFactors = F)
并非每一部电影都有评分,有些电影由于评价人数很低或没有人评价,就没有评分,我们首先需要将每一部电影包含评分的那一行从film中提取出来,如果某部电影缺少这一行,我们就给他赋个个空值占位,这样我们就保证了电影名称和评分可以对应在一起。然后在提取评分,提取评分和提取名称用到的函数一样,不在赘述。
然后是提取评论人数,过程和方法同提取评分一样:
renshu <- list()
for (i in (1:length(film))) {
x <- unlist(film[i])
if (length(x[grep(\&\#39\;spanclass="rating_nums"\&\#39\;,x)]) == 0)
renshu[i] <- ""
else renshu[i] <- x[grep(\&\#39\;spanclass="rating_nums"\&\#39\;,x) + 1]
}#提取含有评价人数的行,并保证没有人数的电影该部分赋值为""
person <- list()
for (i in (1:length(renshu))) {
y <- regexpr(\&\#39\;>.{1,10}人\&\#39\;, renshu[i], TRUE)#返回的结果为模式之前的字符数和模式的长度
z <- y + attr(y,"match.length")-1
person[i] <- substr(renshu[i], y+1, z-1)#提取y,z之间的字符
}#提取评价人数
person <-as.numeric(unlist(person))
filmData <-data.frame(filmData, person, stringsAsFactors = F)
最后提取电影属性行:
filmAttri <- list()
for (i in (1:length(film))) {
x <- unlist(film[i])
if (length(x[grep(\&\#39\;\&\#39\;,x) + 1]) > 0)
filmAttri[i] <- x[grep(\&\#39\;\&\#39\;,x) + 1]
elsefilmAttri[i] <- ""
}#提取含有评分的部分,并保证没有评分的电影该部分赋值为""
filmAttri <-gsub(" ", "", filmAttri)#去除前面的空格
filmData <-data.frame(filmData, filmAttri, stringsAsFactors = F)
filmData <-filmData[!duplicated(filmData[,c(1,3)]),]
电影属性行独立一行,所以为我么省了很多麻烦,最后提取完成之后,要进行一次去重,因为同一部电影可能被豆瓣贴上不同年份的标签,但是不要只对电影名称去重,因为电影名称相同可能仍然是不同的电影,所以只有电影的属性也相同时才认为是重复。
到这里我们就提取了电影的所有数据,但是要做分析还是有点欠火候,我们需要处理电影属性,分离出年份、国家、类别、导演、演员等,处理方法我们将在另外一篇文章中解释,期盼高手指正。