把豆瓣读书和电影收藏数据扒过来展示

handsome 主题带了一个叫豆瓣清单的自定义模板,可以把在豆瓣上标记的书籍和电影拉过来展示。我现在换了 Mirages 主题,有点想念这个功能,所以模仿着上线了两个页面。这里以读书展示页为例说说实现,电影页面除了扒数据的部分,其它完全类似。

示例页面:

读书 - 熊猫小A的博客 | 电影 - 熊猫小A的博客

后端

也就是如何把豆瓣上的数据扒回来。豆瓣 v2 接口中可以在不登录的情况下获取用户的书籍收藏信息,很方便;但是却没有电影收藏的接口,只能通过扒网页自己解析。获取电影收藏部分的代码我是从 handsome 主题抄的,所以就不贴了,只说一下书籍部分。获取书籍收藏的接口是:

https://api.douban.com/v2/book/user/[id]/collections?count=100

其中[id]是豆瓣 ID,count最多设置成 100,设置成更大时会被置为 100。从这个接口获取数据,并格式化保存到本地作为缓存。

updateBooks.php

<?php
$content=json_decode(file_get_contents('https://api.douban.com/v2/book/user/118077218/collections?count=100'));
$data=array();
foreach($content->collections as $value){
    if($value->status!='read') continue;  // 仅保存已读
    $item=array("img"=>str_replace("/view/subject/m/public/","/lpic/",$value->book->image), // 防止图片链接 403
    "title"=>$value->book->title,
    "rating"=>$value->book->rating->average,
    "author"=>$value->book->author[0],
    "link"=>$value->book->alt,
    "summary"=>$value->book->summary);
    array_push($data,$item);
}
$file=fopen("books.json","w");
fwrite($file,json_encode($data));
fclose($file);
?>

更新缓存的话,我是在服务器上设了一个定时任务来访问 updateBooks.php。

getBooks.php

<?php
header("Content-type: application/json");
header("Access-Control-Allow-Origin: *"); // 防止跨域问题
$content=json_decode(file_get_contents('books.json'));
$total=count($content);
$from=$_GET['from'];

//返回从 from 开始的 10 条记录
if($from<0 || $from>$total-1) echo json_encode(array());
else{
    $end=min($from+10,$total);
    $data=array();
    for ($index=$from; $index<$end; $index++) {
        array_push($data,$content[$index]);
    }
    echo json_encode($data);
}
?>

前端

HTML

<div class="douban-list book-list"></div>

<center style="font-size: 1.1em"><span id="loadMoreBooks" style="cursor:pointer" onclick="loadBooks();"><i id="loadMoreBooks-i" class="fa fa-circle-o-notch"></i> 加载更多</span></center> 

样式

.douban-list{
    width:100%;
    height:100%;
    display: flex;
    flex-flow: row wrap;
    justify-content: flex-start;
}
.douban-item {
    position: relative;
    margin-bottom: 15px!important;
    width: 22%;
    margin-right: 10px;
    display: flex;
    flex-flow: column nowrap;
    justify-content: flex-start;
    cursor: pointer;
    box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
    transition: all 0.3s;
    overflow: hidden;
}
.douban-item:hover{
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
@media screen and (max-width:768px){
    .douban-item{
        width: 30%;
    } 
}
@media screen and (max-width:400px){
    .douban-item{
        width: 45%;
    } 
}
.douban-thumb {
    width: 100%;
    padding-top: 141%;
    background-repeat: no-repeat!important;
    background-size: cover!important;
}
.douban-title {
    margin: 8px !important;
    font-size: 1.2em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap !important;
    text-align: center !important;
    color: #202020 !important;
    font-weight: 700 !important;
}
.douban-info{
    position: absolute;
    top:0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    transition: all 0.3s;
    background-color: white;
    color: black;
    font-size: 0.9em;
    line-height: 1.3em;
    transform: translateY(100%);
}
.douban-info > p{
    margin:1em!important;
    margin-top:1em!important;
    margin-bottom:0.3em!important;
}
.douban-info>.douban-info-basic{
    white-space: nowrap!important;
    overflow: hidden;
    text-overflow: ellipsis;
}
.douban-info-show > .douban-info{
    transform: translateY(0);
}

样式方面,由于书籍封面的长宽比相对比较固定,我自己发现 $\sqrt{2}$ 是一个不错的比例,因此问题主要是如何使 div 在缩放时保持长宽比。这里用到的原理是 padding-top 取值为百分比时,其最终的大小是根据 div 的宽度计算的,见上面 CSS 中 .douban-thumb 的写法。

JS(用到了 JQuery)

var curBooks=0;
function loadBooks(){
    if(window.location.href!="https://blog.imalan.cn/book/") return;
    $("#loadMoreBooks-i").addClass("fa-spin");
    $.getJSON("https://api.imalan.cn/Douban/getBooks.php?from="+String(curBooks),function(result){
        if(result.length<10){
            $("#loadMoreBooks").html("没有啦");
        }
        $.each(result,function(i,ite){
            var item=result[i];
            var html=`<div title="点击显示详情" id="douban-book-item-`+String(curBooks)+`" class="douban-item">
                        <div class="douban-thumb" style="background:url(`+item.img+`)"></div>
                <div class="douban-title">`+item.title+`</div>
                <div class="douban-info" title="点击关闭详情">
                    <p class="douban-info-basic">
                    书名:`+item.title+`<br>
                    评分:`+item.rating+`<br>
                    作者:`+item.author+`<br>
                    链接:<a target="_blank" href="`+item.link+`">豆瓣阅读</a><br>
                    简介:<br>
                    </p>
                    <p class="douban-info-summary">
                        `+item.summary+`
                    </p>
                </div>
            </div>`;
            $(".book-list").append(html);            
            curBooks++;
        });
        $("#loadMoreBooks-i").removeClass("fa-spin");
    });    
}

// 鼠标点击显示详情,点击其他区域关闭详情
$(document).click(function(e){
    var target=e.target;
    $(".douban-item").removeClass("douban-info-show");
    $(".douban-item").each(function(){
        if($(target).parent()[0]==$(this)[0] || $(target)==$(this)[0]){
            $(this).addClass("douban-info-show");
        }
    })
})

$(document).ready(function(){
    loadBooks();
});

一开始我始终不知道如何实现“点击其它区域关闭显示详情”这个功能,这个问题的本质是鉴别当前点击事件的对象具体是谁。后来在搜索引擎的帮助下发现 .click 事件有个 target 对象,那么只需要在点击事件中先把所有的详情显示都关闭了,然后遍历所有的 .douban-item ,若点击事件的 target 是当前遍历到的 item 或者是当前遍历到的 item 的子元素,那么就显示这个 item 的详情。由于一页中的 .douban-item 不是太多,所以性能不会差到哪里去。文字描述起来好绕……看上面的代码吧。

于是大功告成。

其它

有互联网上大量的资料支撑,想要自己做一点小玩具出来并不难。不过结合以前写追番清单那个插件的经历,以及最近微博、Twitter 等服务的接口权限相继收紧,我多少能闻到一些不妙的气息。豆瓣同理,以前是有获取用户电影收藏的接口的,现在却不能了(也许鉴权之后用高级接口可以)。豆瓣、微博、Twitter 这样的做法当然不难理解:虽然任何人都可以通过访问它们的网站来获取到用户的微博、tweet、收藏,但是重点是“去它们的网站”。这部分有价值的信息,这些公司慢慢都选择不再与整个互联网共享。

对此我感到非常遗憾。我非常尊敬豆瓣、Bangumi、IMDb 这些能在某个领域建立完整信息体系的网站,它们是整个互联网的财富;微博、Twitter 这类集中了大量 User Generated Content 的平台价值同样不可估量。作为一个普通的网民,虽然有些自私,我还是希望它们能保持开放的姿态。

Tags:none
上一篇
打赏
下一篇

添加新评论

已有 6 条评论

 柴可可 2 个月前 • |

啥时候能做成插件就好了。

 熊猫小A 2 个月前 • |
@柴可可

可以哦,有时间可以折腾一下 #(想一想)

 Lesun 2 个月前 • |

我开始也想搞个这个,但是技术不足,做不来(摊手)@(黑线)

 熊猫小A 2 个月前 • |
@Lesun

我觉得大佬应该只是没心思认真做而已@(勉强)

 熊猫小A 2 个月前 • |
@Lesun

大佬你的域名邮箱挂了?发给你的邮件被退回了。应该是解析的问题,你可以检查一下你的 MX 记录

 Lesun 2 个月前 • |
@熊猫小A

我说怎么没有收到邮件。。我去看一下