不写R包的分析师不是好全栈

Kindle榜小爬虫

    R


好久没更新博客了,今天更新一下前段时间做的一个小爬虫:获取Kindle的图书排行榜.


话说单反穷三代,kindle富一生,这样目测我依旧前途未卜啊.本程序抓取在linux和Mac上是没什么问题的,不过windows会遇到编码问题,暂时没有心情来处理这个bug,就是这么任性~


目标在这里:

获取Amazon Kindle的排行榜网址

library(XML)
URL = paste0("http://www.amazon.cn/gp/bestsellers/digital-text/116169071/ref=sa_menu_kindle_l3_116169071#",1:5)

试着抓取第一个URL,也就是排名在1~20的图书….

## 我不是机器人,Amazon别封我IP~
Sys.sleep(runif(1,1,2))
doc<-htmlParse(URL[1],encoding="UTF-8")
rootNode<-xmlRoot(doc)

可以看到已经过把网页内的信息抓下了,不过书籍的信息不是类似表格的形式,(如果是表格,应该具有类似<table>的标签,可以直接用readHTMLTable来读取) 在这里我使用xpathSApply来读取标签内的信息:

先看下一个书籍的html源码:

<div class="zg_itemRow">
<div class="zg_item_compact">
<div class="zg_image zg_itemLeftDiv_compact">
<div class="zg_itemImage_compact">
<a href="http://www.amazon.cn/%E8%A7%A3%E5%BF%A7%E6%9D%82%E8%B4%A7%E5%BA%97-%E4%B8%9C%E9%87%8E%E5%9C%AD%E5%90%BE/dp/B00NOQNHP2/ref=zg_bs_116169071_1
">
<img src="http://ec4.images-amazon.com/images/I/51F6FK4cLGL._SL160_SL135_.jpg&quot; alt="解忧杂货店" title="解忧杂货店" onload="if (typeof uet == ‘function’) { uet(‘af’); }"/></a>
</div>
</div>
<div class="zg_itemRightDiv_compact">
<div class="zg_rankLine">
<span class="zg_rankNumber">1.</span>
<span class="zg_rankMeta"></span>
</div>
<div class="zg_title">
<a href="http://www.amazon.cn/%E8%A7%A3%E5%BF%A7%E6%9D%82%E8%B4%A7%E5%BA%97-%E4%B8%9C%E9%87%8E%E5%9C%AD%E5%90%BE/dp/B00NOQNHP2/ref=zg_bs_116169071_1">解忧杂货店</a&gt;
</div>
<div class="zg_byline">~ 东野圭吾 (作者), 李盈春 (译者)</div>
<div class="zg_reviews">
(<a href="http://www.amazon.cn/product-reviews/B00NOQNHP2/ref=zg_bs_116169071_cm_cr_acr_txt?ie=UTF8&showViewpoints=1&quot; >3,360</a>)</span…

看起来很乱是不是,我们主要需要从这里面找到我们需要的数据,并用一个Xpath来解读它,什么,不懂Xpath?其实没多大关系,给一个最简单的Xpath,然后照着修改就好:


给我个价格


比如我们想要里面的价格数据,先找到对应的标签:


<strong class="price">¥ 5.93</strong>

strong是代表该部分粗体,class是price,这个DOM标签对应的是"//strong[@class=’price’]"意思就是寻找strong的类,strong的class是price,在R里面获取下Price的数据:


givePrice = function(rootNode){
price<-xpathSApply(rootNode,"//strong[@class=’price’]",xmlValue)
price
## 收费付费混排,我只要付费的价格(循环补齐式选择)
(price = price[c(T,F)])
## 喂,你给我认真处理下数据,把¥去掉,再转数字
strsplit(price," ") -> price.c
unlist(price.c)[c(F,T)] -> price.c
as.numeric(price.c)[1:20] -> price
price
}
givePrice(rootNode)

##  [1] 12.00  0.79  9.99  2.99  0.99  2.99  2.00  2.00  0.10  3.99 16.99
## [12] 18.00 1.99 8.99 0.99 0.99 3.99 2.00 1.99 1.99



给我个评价


要抓取的内容为:


<a style="text-decoration:none" href="http://www.amazon.cn/product-reviews/B00NOQNHP2/ref=zg_bs_116169071_cm_cr_acr_img?ie=UTF8&showViewpoints=1&quot; name="reviewHistoPop_B00NOQNHP2_5288_star__" ><span class="swSprite s_star_4_5 " title="平均4.7 星" ><span>平均4.7 星</span></span>&nbsp;</a>

Xpath为:"//a[@style=’text-decoration:none’]/span"


giveRate = function(rootNode){
rate<-xpathSApply(rootNode,"//a[@style=’text-decoration:none’]/span",xmlValue)
rate[c(T,F,F,F)] -> rate
strsplit(rate,"平均") -> rate.c
unlist(rate.c)[c(F,T)] -> rate.c
strsplit(rate.c," 星") -> rate.c
unlist(rate.c) -> rate.c
as.numeric(rate.c) -> rate
rate
}
giveRate(rootNode)

##  [1] 4.2 4.4 4.3 4.4 4.3 4.5 4.4 4.3 4.5 4.2 4.5 4.2 4.6 4.6 4.4 4.6 4.4
## [18] 4.6 4.7 4.5

为什么要认真处理数据呢,因为评价在后期说不定需要进行比较并找到最高评分的书籍,所以在前期要做好数据的清理.




给我评价数


我在刷Amazon的时候,发现有书竟然能到5.0的评分(竟然是满分!)细看一下只有4个评分.所以获得评价数量应当也是其中的一环(哪怕最终用不到这个数据)


Xpath为:"//a[@style=’text-decoration:none’]/span"


giveNumber = function(rootNode){
number<-xpathSApply(rootNode,"//span[@class=’crAvgStars’]/a",xmlValue)
number[c(T,F)] -> number.c
sub(",","",number.c) ->number.c
as.numeric(number.c)
}
giveNumber(rootNode)

##  [1]   79  466 2137  706  540  533  141 1734  528  184   63  406  256  380
## [15] 326 547 257 464 218 311



给我书名


刷出书名:


giveNames = function(rootNode){
names <- xpathSApply(rootNode,"//div[@class=’zg_title’]/a",xmlValue)
names[c(T,F)]
}
giveNames(rootNode)

##  [1] "斯坦福极简经济学"
## [2] "话语操纵术2:不可思议的催眠式说服技巧 (话语操纵术系列)"
## [3] "解忧杂货店"
## [4] "最璀璨的银河——刘慈欣经典作品集"
## [5] "春日便当"
## [6] "你一定爱读的极简欧洲史(简约不简单的“最短”欧洲史,任志强、钱理群、钱文忠、公孙策联合推荐!)"
## [7] "狼图腾 (九头鸟长篇小说文库)"
## [8] "乖,摸摸头"
## [9] "浮生六记"
## [10] "张鸣说历史:重说中国国民性"
## [11] "血腥的盛唐大全集(珍藏版)(套装全7册)"
## [12] "从0到1:开启商业与未来的秘密(图文精编版) (奇点系列)"
## [13] "经典短篇小说101篇(英文原版) (西方经典英文读物) (English Edition)"
## [14] "白夜行"
## [15] "何以笙箫默"
## [16] "理想实习生"
## [17] "第一夜"
## [18] "从你的全世界路过"
## [19] "人生的智慧 (叔本华系列)"
## [20] "从零开始学炒股:新手入门、大智慧详解、买卖之道"



给我作者


再刷出作者:


giveAuthors = function(rootNode){
authors <- xpathSApply(rootNode,"//div[@class=’zg_byline’]",xmlValue)
authors[c(T,F)] -> authors
sub("\n\n\n\n\n\n\n~ ","",authors)
}
giveAuthors(rootNode)

##  [1] "(美)泰勒 (作者), 林隆全 (译者)"
## [2] "大卫•拜伦 (作者), 刘祥亚 (译者)"
## [3] "东野圭吾 (作者), 李盈春 (译者)"
## [4] "刘慈欣 (作者)"
## [5] "〔日〕吉井忍 (作者)"
## [6] "约翰•赫斯特 (作者), 席玉苹 (译者)"
## [7] "姜戎 (作者)"
## [8] "大冰 (作者)"
## [9] "(清)沈复著;朱奇志 校译 (作者)"
## [10] "张鸣 (作者)"
## [11] "王觉仁 (作者)"
## [12] "彼得·蒂尔 (作者), 布莱克·马斯特斯 (作者), 高玉芳 (译者)"
## [13] "(美)欧•亨利等 (作者)"
## [14] "东野圭吾 (Higashino Keigo) (作者), 刘姿君 (译者)"
## [15] "顾漫 (作者)"
## [16] "肖桐 (作者)"
## [17] "M.) (法) 李维 (Levy (作者), 李月敏 (译者)"
## [18] "张嘉佳 (作者)"
## [19] "叔本华(Arthur Schopenhauer) (作者), 韦启昌 (译者)"
## [20] "杨金 (作者)"



祭出主程序


我们之前刷出来了这几个函数:



  • givePrice

  • 求价格

  • giveRate

  • 求评价

  • giveNumber

  • 求评价数

  • giveNames

  • 求书名

  • giveAuthors

  • 求作者


组合到一起,合成获取某一个URL的主函数:


getAmazonBy1 = function(URL){
Sys.sleep(runif(1,1,2))
doc<-htmlParse(URL[1],encoding="UTF-8")
rootNode<-xmlRoot(doc)
data.frame(
Price = givePrice(rootNode),
# 求价格
Rate = giveRate(rootNode),
# 求评价
Number = giveNumber(rootNode),
# 求评价数
Name = giveNames(rootNode),
# 求书名
Author = giveAuthors(rootNode)
# 求作者
)
}

不过要注意的是,在win下刷出来的是UTF-8码,噗哈哈,我去用linux跑该脚本啦~




最后一步


多个网址,刷出各个排名并组合到一起:


URL = paste0("http://www.amazon.cn/gp/bestsellers/digital-text/116169071/ref=sa_menu_kindle_l3_116169071#",1:5)

mainfunction = function(URL){
data = rbind(
getAmazonBy1(URL[1]),
getAmazonBy1(URL[2]),
getAmazonBy1(URL[3]),
getAmazonBy1(URL[4]),
getAmazonBy1(URL[5]))
data = cbind(data,1:100)

}

mainfunction(URL)

保存好今天的数据,明天再来一次,就可以看看每天上升最快的本书了,然后呢,买来看啊….amazon数据是每小时更新的,我做此文的时候也刷了一次数据.




获取日上升最快的书籍


library(dplyr)

##
## Attaching package: ‘dplyr’
##
## The following object is masked from ‘package:stats’:
##
## filter
##
## The following objects are masked from ‘package:base’:
##
## intersect, setdiff, setequal, union

getImprove = function(t=0){
today = read.csv(paste0(Sys.Date()-t,".csv"))
yesterday = read.csv(paste0(Sys.Date()-1-t,".csv"))
yesterday = select(yesterday,Name,yes = X1.100)
final = left_join(today, yesterday, by = "Name") %>%
mutate(.,yes = ifelse(is.na(yes),100,yes)) %>%
mutate(.,improve = yes-X1.100) %>%
arrange(.,desc(improve))
head(final)
}

getImprove(t = 20)

##                                    Name  X Price Rate Number
## 1 世界上的另一个你 (最佳译文:53) 37 1.99 4.6 5
## 2 哈默手稿 29 3.99 4.3 170
## 3 说不尽的外交 51 6.99 4.5 114
## 4 神似祖先 (视点文丛) 45 3.99 4.5 55
## 5 孤独的人都要吃饱 49 2.99 4.2 17
## 6 潮汕味道 (潮汕文化丛书•岭南文化书系) 59 4.99 4.5 28
## Author
## 1 R.) , ( 美) 摩尔 (Moore,D.) ( 美) 霍尔 (Hall (作者), 李佳纯 (译者)
## 2 列奥纳多•达•芬奇 (Leonardo da Vinci) (作者), 李秦川 (译者)
## 3 李肇星 (作者)
## 4 郑也夫 (作者)
## 5 张佳玮 (作者)
## 6 张新民 (作者)
## X1.100 yes improve
## 1 37 100 63
## 2 29 78 49
## 3 51 100 49
## 4 45 89 44
## 5 49 93 44
## 6 59 100 41

最后程序:


library(XML)

givePrice = function(rootNode){
price<-xpathSApply(rootNode,"//strong[@class=’price’]",xmlValue)
price
## 收费付费混排,我只要付费的价格(循环补齐式选择)
(price = price[c(T,F)])
## 喂,你给我认真处理下数据,把¥去掉,再转数字
strsplit(price," ") -> price.c
unlist(price.c)[c(F,T)] -> price.c
as.numeric(price.c)[1:20] -> price
price
}

giveRate = function(rootNode){
rate<-xpathSApply(rootNode,"//a[@style=’text-decoration:none’]/span",xmlValue)
rate[c(T,F,F,F)] -> rate
strsplit(rate,"平均") -> rate.c
unlist(rate.c)[c(F,T)] -> rate.c
strsplit(rate.c," 星") -> rate.c
unlist(rate.c) -> rate.c
as.numeric(rate.c) -> rate
rate
}

giveNumber = function(rootNode){
number<-xpathSApply(rootNode,"//span[@class=’crAvgStars’]/a",xmlValue)
number[c(T,F)] -> number.c
sub(",","",number.c) ->number.c
as.numeric(number.c)
}

giveNames = function(rootNode){
names <- xpathSApply(rootNode,"//div[@class=’zg_title’]/a",xmlValue)
names[c(T,F)]
}

giveAuthors = function(rootNode){
authors <- xpathSApply(rootNode,"//div[@class=’zg_byline’]",xmlValue)
authors[c(T,F)] -> authors
sub("\n\n\n\n\n\n\n~ ","",authors)
}

getAmazonBy1 = function(URL){
Sys.sleep(runif(1,1,2))
doc<-htmlParse(URL[1],encoding="UTF-8")
rootNode<-xmlRoot(doc)
data.frame(
Price = givePrice(rootNode),
# 求价格
Rate = giveRate(rootNode),
# 求评价
Number = giveNumber(rootNode),
# 求评价数
Name = giveNames(rootNode),
# 求书名
Author = giveAuthors(rootNode)
# 求作者
)
}

#################主程序部分############################3

mainfunction = function(URL){
data = rbind(
getAmazonBy1(URL[1]),
getAmazonBy1(URL[2]),
getAmazonBy1(URL[3]),
getAmazonBy1(URL[4]),
getAmazonBy1(URL[5]))
data = cbind(data,1:100)

}

################运行部分###############################3
URL = paste0("http://www.amazon.cn/gp/bestsellers/digital-text/116169071/ref=zg_bs_116169071_pg_1?ie=UTF8&pg=",1:5)

data = mainfunction(URL)
write.csv(data, file = paste0(Sys.Date(),".csv"))

page PV:  ・  site PV:  ・  site UV: