一个小爬虫:获取Kindle的图书排行榜

时间:2022-05-04
本文章向大家介绍一个小爬虫:获取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" 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>
    </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" >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" 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("nnnnnnn~ ","",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("nnnnnnn~ ","",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)
  # 求作者
  )}#################主程序部分############################3mainfunction = function(URL){
  data = rbind(
            getAmazonBy1(URL[1]),            getAmazonBy1(URL[2]),            getAmazonBy1(URL[3]),            getAmazonBy1(URL[4]),            getAmazonBy1(URL[5]))
  data = cbind(data,1:100) }################运行部分###############################3URL = 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"))