前情提要
前一篇文章中,我想明白了:使用 書(shū)名
搜索,得到的豆瓣返回結(jié)果集的第一個(gè)元素其實(shí)就是目標(biāo)書(shū)的信息仓坞。于是,在這一篇文章中腰吟,我將調(diào)用豆瓣 API 得到借書(shū)記錄中所有的書(shū)的讀書(shū)標(biāo)簽无埃,并生成相應(yīng)的詞云。
使用豆瓣 API 根據(jù)書(shū)名搜索書(shū)
搜索豆瓣 API,找到開(kāi)發(fā)者文檔嫉称。豆瓣的 API 看著很清晰侦镇,一下子就找到我需要的接口∨觳海看了一下虽缕,還可以限制返回?cái)?shù)量,于是理所當(dāng)然的蒲稳,將 count
限制為 1
就獲取第一條數(shù)據(jù)了氮趋。
使用 Postman 驗(yàn)證想法
在 Postman
中輸入搜索 API,加上查詢條件江耀,發(fā)送請(qǐng)求剩胁,查看結(jié)果集。OK祥国,正是我想要的昵观。
查看 tags
對(duì)象,很好舌稀,不但有標(biāo)簽啊犬,還有類(lèi)似權(quán)重之類(lèi)的 count
,可能未來(lái)還有更多的用途壁查,目前先不管它觉至。
Javascript 代碼實(shí)現(xiàn)獲取所有 tag
寫(xiě)了段簡(jiǎn)單的 JS 代碼,驗(yàn)證是否得到與 Postman
一致的結(jié)果睡腿。結(jié)果语御,出錯(cuò)了……
URL 編碼問(wèn)題
挺奇怪的,使用的 URL 跟 Postman 的是一樣的席怪,為什么會(huì)出錯(cuò)呢应闯?
看了看錯(cuò)誤信息,status_code 是 400挂捻,請(qǐng)求有問(wèn)題碉纺?
這個(gè)請(qǐng)求里面只有 URL,那就是 URL 有問(wèn)題刻撒。想了想惜辑,難道是因?yàn)?URL 中的中文嗎?
試著把書(shū)名改為只有英文的 Java疫赎,嘿,成功了碎节。
那么問(wèn)題就在于中文編碼了捧搞。
解決
網(wǎng)絡(luò)上搜索了一下,發(fā)現(xiàn)使用 Javascript 發(fā)網(wǎng)絡(luò)請(qǐng)求,需要使用 encodeURI
將中文編碼之后才可以正常請(qǐng)求胎撇。
第一次遇到 URL 編碼問(wèn)題是大二介粘,后面就陸陸續(xù)續(xù)遇到同樣的問(wèn)題⊥硎鳎可是到現(xiàn)在我也沒(méi)有徹底搞明白這個(gè)問(wèn)題姻采。為什么 URL 不能有中文呢?以后可能還會(huì)遇到同樣的問(wèn)題爵憎,得找個(gè)機(jī)會(huì)徹底搞明白慨亲。當(dāng)然,這是后話宝鼓,且按下不表刑棵。
簡(jiǎn)單地寫(xiě)完了獲取所有 tag 的代碼,截取了部分書(shū)名愚铡,作為測(cè)試數(shù)據(jù)蛉签。執(zhí)行 node getTags.js
,成功獲得數(shù)據(jù)沥寥。
速度限制
出錯(cuò)
將測(cè)試數(shù)據(jù)改為完整的書(shū)名列表碍舍,結(jié)果出現(xiàn)了錯(cuò)誤。
很費(fèi)解邑雅,看了一會(huì)兒之后片橡,猜測(cè)是因?yàn)檎?qǐng)求數(shù)量太多了,豆瓣把我的請(qǐng)求給禁掉了蒂阱。
在 Postman
重新請(qǐng)求驗(yàn)證想法锻全,發(fā)現(xiàn)如我所料。
{
"msg": "rate_limit_exceeded2: 43.243.12.21",
"code": 112,
"request": "GET /v2/book/search"
}
查了 豆瓣的 API 文檔录煤,發(fā)現(xiàn)鳄厌,請(qǐng)求確實(shí)是有限制的。
我得到的錯(cuò)誤信息是這個(gè)
解決方法
讀書(shū)記錄中的書(shū)名有兩百多個(gè)妈踊,一次性發(fā)請(qǐng)求的話了嚎,肯定會(huì)超出限制。想了個(gè)方法廊营,就是在每個(gè)請(qǐng)求前隨機(jī)等待若干時(shí)間歪泳,這樣或許可以避免豆瓣的請(qǐng)求限制。
最后的代碼如下
const agent = require('superagent');
const async = require('async');
const bookTitleList = require('./book_title_list');
function sleep(milliseconds) {
let start = new Date().getTime();
for (let i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
function random_sleep(second) {
sleep(Math.floor((Math.random() * second) + 1) * 1000)
}
function requestTags(bookTitle, done) {
random_sleep(20);
agent.get(encodeURI(`https://api.douban.com/v2/book/search?q="${bookTitle}"&count=1`))
.end((err, res) => {
if (err) {
console.log(err);
} else {
const tag = res.body.books[0].tags;
done(null, tag);
}
}
);
}
async.map(bookTitleList, requestTags, (err, tags) => {
tags.forEach(tag => {
console.log(tag);
})
});
我還沒(méi)有驗(yàn)證它是否可以正常工作露筒,因?yàn)橄拗茣r(shí)間還沒(méi)過(guò)呢……