楼主: sssyunsheng
4145 7

[程序分享] R与豆瓣2000-2015年数据抓取数据整理部分 [推广有奖]

  • 2关注
  • 47粉丝

已卖:107份资源

博士生

52%

还不是VIP/贵宾

-

威望
0
论坛币
3 个
通用积分
4.4708
学术水平
47 点
热心指数
49 点
信用等级
43 点
经验
5127 点
帖子
201
精华
0
在线时间
306 小时
注册时间
2012-2-21
最后登录
2025-9-22

楼主
sssyunsheng 在职认证  发表于 2015-6-10 20:49:16 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

我们这个系列共分为三个部分:数据抓取,数据整理,数据分析应用,前面有一篇帖子已经讲述了数据抓取部分(需要的话可以点我发的帖子或者关注我们微信,帖子里代码有点小错误,应景更改了),这篇文章我们主要讲抓取过来的数据怎么整理,然后我们会发出多篇用抓取过来的数据写就的文章,请大家轻喷。

我们的目的简单,从数据电影属性中分离出上映电影的国家、时间、类型、导演和演员。

首先我们要把上一步抓取的数据读进来:

  1. #filmData <-read.csv("E:/自媒体/filmData.csv", header = T, sep =",", stringsAsFactors = F)
  2. #filmData <-filmData[, -1]
复制代码

以上两步可能和你存储的方式有关,filmData是抓取数据存放的csv格式的文件,如果你写入硬盘然后读尽来,会发现多了一列row.names叫做x,这里将他删除。如果你没有写入硬盘则无需删除操作。我们的电影都存放在filmAttri列。

  1. sentence <-as.vector(filmData$filmAttri)#将电影属性提出来转化为向量,准备下一步分解
  2. temp1 <-strsplit(sentence, "/")#将属性按照/分开
  3. filmAttribute <-as.data.frame(cbind(rep(filmData[,1], unlist(lapply(temp1, length))),
  4.                                     rep(filmData[,2], unlist(lapply(temp1, length))),
  5.                                      unlist(temp1)),stringsAsFactors = F)#将属性与电影名称组合成一个dataframe
  6. colnames(filmAttribute)<- c("id", "name", "attri")
复制代码

首先我们将属性列提取出来放到sentence里,观察一下发现这些属性是由“/”分开的,那么我们就用strsplit将其分开,然后我们就获得了一个和电影的部数一样长的list,list的每个vector包含一部电影的所有属性,要将电影的名称和属性对上,那么vector有多长,就要重复多少次相应的电影名称,然后我们又新加了一个电影id列,每一个id号对应一部电影,这样做是方便下面操作。

因为有些影片没有国家,有些影片没有时间,所以我们需要第一步删选出国家和时间均不空缺的记录,这就是一个如何实现多次筛选的问题:

  1. #如何实现多次筛选
  2. x <-as.character(c(2000:2015))#筛出年份
  3. time <-filmAttribute[filmAttribute$attri %in% x, ]#筛出年份
  4. filmAttribute <-filmAttribute[filmAttribute$id %in% time$id,]#去除没有年份的记录,这种筛选是按行名称提取的
复制代码

以上我们就完成了第一次筛选,我们把缺少年份的电影全部删除了。

  1. rownames(filmAttribute)<- seq(1:length(filmAttribute[,1]))#因为删除行后行名称并没有重新标注,我们这里给它重新标注
复制代码

上面我们删除了数据框filmAttribute中缺乏年份的电影,同时我们也要把原始数据框filmData里的也给删除了,保持两个数据的电影一致。

  1. filmData <-filmData[filmData$id %in% time$id,]#去除没有年份的记录
  2. rowname <-seq(1:length(filmAttribute[,1]))#因为后面的数据我是以行编码为依据提取的,为了防止乱掉创造了新列rowname,相当于属性的id
  3. filmAttribute <-data.frame(rowname, filmAttribute)
  4. x <-as.character(c(2000:2015))#筛出年份
  5. time <- filmAttribute[filmAttribute$attri%in% x, ]#筛出年份
复制代码

以上我们就筛出了年份列,数据框time里一共4列,rowname用来标记电影时间所在的行号,id指具体的电影帮助对应到电影,name指电影的名称,attri下全是电影上映的时间。下面我们要筛出国家,一般一部电影的第一行属性即为国家和地区,当然有些不是,我们暂且把第一行作为国家处理:

  1. country <-filmAttribute[!duplicated(filmAttribute[, 2]),]#因为影片属性的第一行即为国家,当然有可能不是,这里先假设第一行全部是国家,去重函数分函数根据id保证电影都是唯一的
复制代码

在R语言中,duplicated函数返回的是逻辑值TRUE和FALSE,如果某一行第一次出现,他被认为是独特的,duplicated返回的值是FALSE,如果不是第一次出现,则是重复的行,返回的值是TRUE,如果是对一个vector去重,则返回一个逻辑值vector,长度和vector一样。这里我们需要找到每一部电影的第一行,然后提起出来,那么我们加一个!非的操作就可以把每部电影的第一行数据,然后将这行的属性作为国家就行了。

然后我们需要提取电影的类别了,我们发现电影的类别一般在国家和上映时间所在行之间,于是我们之前加的列rowname有用了:

  1. temp1 <-country$rowname
  2. temp2 <-time$rowname
复制代码

大部分电影国家地区(第一行)和年份之间的部分为电影类型,但是那么多电影也许中间会什么都不隔,所以要先判断一下加以区分,在做这个之前我们要先把缺少国家和地区的电影删除,记得我们前面经过一次筛选,筛出的电影都是含有上映时间的,那么如果一部电影缺少国家,他的第一行肯定是时间,那么时间行和国家行的rowname肯定是相同的,他们的差值为零,我们找出这样的电影将其删除了,就保证了剩下的电影既包含国家又包含上映时间。

  1. x <- temp2 -temp1
  2. x <-temp1[which(x == 0)]
  3. x <-country[country$rowname %in% x,]
  4. x <-filmAttribute[filmAttribute$id %in% x$id, ]#经检查晒出来的x里确实是缺少国家地区的电影,这里予以删除
复制代码

在删除之前我们要看一下是不是这样的,结果我们发现x里面的电影确实没有国家这个属性,自然就要把他们删除掉了。

  1. filmAttribute <-filmAttribute[!(filmAttribute$id %in% x$id), ]
  2. filmAttribute <-filmAttribute[,-1]#删除缺少国家地区的记录,重新编号重新提取时间和地区
  3. rownames(filmAttribute)<- seq(1:length(filmAttribute[,1]))
  4. rowname <- seq(1:length(filmAttribute[,1]))
  5. filmAttribute <-data.frame(rowname, filmAttribute)
复制代码

这里你会发现既然删除了一些电影那么我们之前提取的时间也没用了,所以干脆重来一遍,当然如果不重来用匹配也行,于是我们删除了第一列的rowname然后给属性重新编制id列仍然叫做rowname。然后依次提取时间、国家、电影类型等。

#提取时间和国家

  1. x <-as.character(c(2000:2015))#筛出年份,我们的跨度是16年,如过你没那么多,就照着你抓取的年份做
  2. time <-filmAttribute[filmAttribute$attri %in% x, ]#筛出年份
  3. country <-filmAttribute[!duplicated(filmAttribute[, 2]),]#因为影片属性的第一行即为国家,当然有可能不是,这里先假设第一行全部是国家
复制代码
  1. filmData <-filmData[filmData$id %in% time$id,]#清除有年份没国家地区的记录
  2. temp1 <- country$rowname
  3. temp2 <-time$rowname
复制代码


再次检验是否存在缺少国家的记录,如果通过则进行提取影片类型的过程:

  1. x <- temp2 -temp1
  2. x <-temp1[which(x == 0)]
  3. x <-country[country$rowname %in% x,]
  4. x <-filmAttribute[filmAttribute$id %in% x$id, ]#经检验不存在
复制代码

下面我们要提取电影类型了,一般国家地区和年份之间的行即为影片类型,那么存在两种情况,如果国家地区和年份之间行号差值x小于2,则说明影片没有分类,如果大于等于则说明有分类:

  1. #提取电影类型
  2. x <- temp2 -temp1
  3. temp1 <-temp1[which(x > 1)]
  4. x <- x[which(x> 1)] - 1
  5. temp <- c()
  6. for (i in1:length(x)) {
  7.   temp2 <- temp1[i] + seq(1:x[i])
  8.   temp <- c(temp, temp2)
  9. }#生成含有影片类型的rowname,然后我们就知道那些rowname对应的是电影类型了,为什么减1,自己比划一下
  10. type <-filmAttribute[filmAttribute$rowname %in% temp, ]#提取影片类型
复制代码

我们得到了电影的上映时间,国家或地区,电影类型等,现在我们先把他们合并在一起了,这里牵涉到你是将数据进行long型数据框摆放还是进行wide型数据框摆放,因为ggplot一般使用long型数据,这里我们进行long型摆放。

  1. ###将time, country, type先合并成一个data.frame
  2. variable <-rep("上映时间", length(time[, 1]))
  3. time <-data.frame(time, variable, stringsAsFactors = F)
  4. variable <-rep("国家地区", length(country[, 1]))
  5. country <-data.frame(country, variable, stringsAsFactors = F)
  6. variable <-rep("类型", length(type[, 1]))
  7. type <-data.frame(type, variable, stringsAsFactors = F)
  8. filmtemp <-rbind(time, country, type)
复制代码

到这里我们已经得到电影的部分属性了,下面我们需要提取电影的导演和演员了,我们将我们已经提取的行删掉,就只剩下导演和演员了,一般出现的一个人名即为导演,其后是演员,,所以思路和提取国家时一样,去重得到的就是导演,我们按照这个思路往下走吧。

  1. #提取导演
  2. filmda <- filmAttribute[!(filmAttribute$rowname%in% filmtemp$rowname), ]#删除已经提取的行
  3. director <-filmda[!duplicated(filmda[, 2]),]
  4. actor <-filmda[duplicated(filmda[, 2]),]
  5. variable <-rep("导演", length(director[, 1]))
  6. director <-data.frame(director, variable, stringsAsFactors = F)
  7. #提取演员
  8. variable <-rep("主演", length(actor[, 1]))
  9. actor <-data.frame(actor, variable, stringsAsFactors = F)
  10. filmtemp <-rbind(filmtemp, director, actor)
  11. filmData <-filmData[filmData$id %in% filmtemp$id,]
  12. filmtemp <-filmtemp[, -1]
  13. filmfinal <-join(filmtemp, filmData)
  14. write.csv(filmfinal,"filmfinal.csv")
复制代码

到这里我们的豆瓣数据电影数据抓取基本告一段落,代码虽然笨虽然长但是理解起来不难。下面我们要规范一下属性的内容,经查看发现国家和地区有些是英文有些是繁体,那么统一一下,幸好地区并不多,我们都懂建一个对应词典叫diqudict就行,yingyu列是要替换的国家英文名、繁体名及其他别称,第二列位统一的称呼。Filmfinal第三列就是属性的列,我们将其重匹配上的不标准行找到,然后用标准的替换掉,这就是一个用其自身匹配,然后又替换掉自身的问题。

  1. setwd("C:/Users/Administrator/Documents")
  2. #将国家为英文的翻译成中文,同同时统一国家称呼和写法
  3. diqudict <-read.csv("diqudict.csv", header = T, sep = ",",stringsAsFactors = F)
  4. filmfinal1 <-filmfinal
  5. t <-match(filmfinal1$attri, diqudict$yingyu)
  6. filmfinal1[filmfinal1$attri%in% diqudict$yingyu, 3] <- diqudict[t[!is.na(t)], 2]#这一点很烦,就是将匹配上的内容替换掉,这里采用了一种没加辅助列的方法,不知是不是还有其他更为直捷的途径
复制代码

其实匹配从来都是一个让人蛋不定的问题,match返回的是x的元素在y中的位置,如果没有就为NA,如果多个就只返回第一个,返回的长度和x等长。经过一次match,这样我们去掉NA,就可以提取对应的字典里的统一的名称,然后将它放在filmfinal相应的位置。如果你看着代码不对,那就是论坛将% in %现实错了,在R里他是一个匹配函数,返回所有能够匹配上的x的编号。

然后有些国家同时出现了中文名称和英文名称,我们也要处理一下,方法就是先将这些记录提出来,然后将英文灭掉,再放回去。

  1. temp <-filmfinal1[which(filmfinal1$variable == "国家地区"),]#把国家地区记录单独提出来目的是去除同时出现了中英文的记录中的英文
  2. filmfinal1 <-filmfinal1[-which(filmfinal1$variable == "国家地区"),]
  3. sentence <-as.vector(temp$attri)
  4. sentence <-gsub("[a-zA-Z]", "", sentence)#去除英文字符
  5. attri <-sentence
  6. temp[,3] <-attri
  7. filmfinal1 <-rbind(filmfinal1, temp)#然后再和原来提取剩余的部分合并成原始文档
  8. #write.csv(filmfinal1,"filmfinal1.csv")#到这里还有极个别缺少国家和地区有毛病,写入硬盘手动添加和删除,然后再读入
复制代码

到这里我们发现棒子的电影如果只有韩语名称,是无法正确识别的,我们需要将这部分电影删除:

  1. filmfinal1 <-read.csv("filmfinal1.csv", header = T, sep = ",",stringsAsFactors = F)
  2. filmfinal1 <-filmfinal1[-grep("^<.{6}>", filmfinal1$name), ]#删除个别几个只有韩语名称的电影,他们的特征是开头就是<
  3. filmfinal1[,2]<- gsub("<.{6}>", "", filmfinal1$name)#将名称中形如<U+0E1E>字符替换为空字符
  4. filmfinal1[,3]<- gsub("<.{6}>", "", filmfinal1$attri)#替换为""
  5. filmfinal1[,3]<- gsub("'", "", filmfinal1$attri)#替换为""
  6. filmfinal1 <-filmfinal1[-grep("[deleted]", filmfinal1$attri), ]
复制代码

到这里我们只需要把名称为””的行删除就可以了,但是这里你碰到了麻烦,因为你查不出这个值为””的行,无论是is.na,is.null,is.nan都无法查出,好吧我们用正则表达式来解决吧:

  1. #解决空字符,但是is.na查不出的问题

  2. filmfinal1 <-filmfinal1[grep("\\S", filmfinal1$attri), ]#查了很多方法is.na,is.null,is.nan均不能查出""即双引号产生的空值,例如以上几行产生的空值,这里用正则表达式可以把这类空值筛掉
复制代码

正则表达式\S表示匹配任意不是空白符的字符,那么我将非空白符的行提取出来就行了,自然就把那些””幽灵去掉了,如果你想找到这些幽灵,秩序在grep前面加个-号就行了,如果你用的是grepl就加!号。

最后我们将评分、人数空缺的全部填充为0。

  1. filmfinal1[is.na(filmfinal1[,5]),5] <- 0
  2. filmfinal1[is.na(filmfinal1[,6]),6] <- 0
  3. write.csv(filmfinal1,"filmfinal2.csv")#剩下的可以手动整理了
复制代码

这里我们的R抓取豆瓣数据并整理数据的文章就写完了,下面我们就要展现基于这些数据的有趣的分析文章了,比如我们要看《2015国产电影到底有多糟?》


关于大音如霜工作室

关注理性与文艺,用数据创作内容性的精致阅读。关注请加微信公众号:dayinrushuang或扫描下方二维码

qrcode_for_gh_89f96c48034b_258.jpg


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:数据整理 抓取数据 数据抓取 上映电影 Data 文章 国家

已有 3 人评分经验 论坛币 学术水平 热心指数 信用等级 收起 理由
日新少年 + 2 + 2 + 2 精彩帖子
Alfred_G + 5 + 3 + 3 + 3 奖励积极上传好的资料
fantuanxiaot + 10 + 2 + 2 + 2 精彩帖子

总评分: 经验 + 10  论坛币 + 5  学术水平 + 7  热心指数 + 7  信用等级 + 7   查看全部评分

本帖被以下文库推荐

沙发
亚米UM 发表于 2015-6-11 00:09:49 来自手机
sssyunsheng 发表于 2015-6-10 20:49
我们这个系列共分为三个部分:数据抓取,数据整理,数据分析应用,前面有一篇帖子已经讲述了数据抓取部分( ...
Mark

藤椅
少才 发表于 2015-6-11 11:02:47
感谢分享

板凳
wukecai1990 发表于 2015-6-11 12:13:38
好想学习学习

报纸
Alfred_G 学生认证  发表于 2015-6-11 19:08:20
很不错的资料

地板
likelihood_cox 发表于 2015-6-12 00:27:59
good sharing! thx!

7
Joskiy 发表于 2016-12-12 18:25:06
楼主太给力了

8
潜心求学@@ 发表于 2016-12-15 17:42:50 来自手机
强大

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-25 20:50