1. 程式人生 > 其它 >一個小爬蟲:獲取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" 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"))