一個小爬蟲:獲取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> </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"))