之前我写过一个 Python 脚本,它通过豆瓣开放的 API 获取书籍信息,然后根据贝叶斯平均算出每一本书的综合评分,再根据此评分制作一个排行榜。这一次,我用 JavaScript 重写了这个脚本,这样,我可以把这个脚本挂到网上,服务大家。网址:https://alexddhuang.github.io/douban-books-rank/

使用 JavaScript 访问豆瓣 API,第一个要解决的就是跨域访问的问题。使用 XMLHttpRequest 是不行的,但是我们可以用 JSONP

function jsonp(url) {
    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url + "&callback=callback";
    document.body.append(script)
}

这个函数借用了 <script> 元素去访问某个 url,服务器返回的 JSON 会传递给一个名叫 callback 的全局函数。

具体的代码可以在这里找到:douban-books-rank


2019-09-07 更新:更快!更强!

我发现之前的代码做了一些很愚蠢的事情,使得搜索速度不是特别快。豆瓣的搜索 API 是这样的:

https://api.douban.com/v2/book/search?q=[q]&start=[start]

其中 q 是需要搜索的文本,start 为每页数据的书籍条目的起始下标。如果不传入 start 参数,则该 API 返回第一页数据。每页数据都有字段 count,表示该页的书籍条目数量,字段 start 表示该页书籍条目的起始下标,字段 total 表示书籍条目总数。

我之前是用递归的方式获取所有条目的:

function callback(page) {
    const count = page['count'];
    const start = page['start'];
    const total = page['total'];

    // Parse data ...

    if (start + count >= total) {
        // Finish, show results ...
        return;
    }

    jsonp(url + `&start=${start + count}`);
}

这样写的缺点是:你必须等待上一页的数据解析完成后,再去访问下一页数据,从而造成了阻塞。

避免阻塞的一个方式就是,在解析数据之前先迭代遍历每一页数据:

let readbooks = 0;

function callback(page) {
    const count = page['count'];
    const start = page['start'];
    const total = page['total'];

    if (start == 0) {
        readbooks = 0;

        for (let s = start + count; s < total; s += count) {
            jsonp(url + `&start=${s}`);
        }
    }

    // Parse data ...

    readbooks += count;
    
    if (readbooks >= total) {
        // Finish, show results ...
    }
}