更新calibre-web豆瓣插件calibre-web-douban-api

最近豆瓣更新了反爬政策,連封面圖片也不允許直接訪問,越來越嚴(yán)格了,以前的calibre-web-douban-api插件不能正常獲取封面,因此只能想其他辦法來實(shí)現(xiàn)封面的獲取。

插件已更新到最新,本文是記錄插件獲取封面的技術(shù)實(shí)現(xiàn),使用參考:https://fugary.com/?p=238

源碼地址:https://github.com/fugary/calibre-web-douban-api/blob/main/src/NewDouban.py

如何實(shí)現(xiàn)

其實(shí)獲取封面只需要指定Refer=https://book.douban.com頭信息就可以了。

calibre-web是第三方開源服務(wù),在不能修改其源碼的情況下,要實(shí)現(xiàn)豆瓣封面需要處理兩個(gè)問題:

  1. 封面的預(yù)覽
  2. 封面下載

封面預(yù)覽

封面預(yù)覽實(shí)際是從瀏覽器根據(jù)豆瓣的封面url直接發(fā)起請求,沒有辦法增加頭信息(Chrome瀏覽器插件倒是可以實(shí)現(xiàn)),因此只能通過本地服務(wù)代理訪問豆瓣url。

代理地址

calibre-web是用flask開發(fā),可以考慮引入flask的藍(lán)圖對象,自己添加一個(gè)路由地址,在路由中轉(zhuǎn)發(fā)請求,參考代碼:

from cps.search_metadata import meta
from flask import request, Response

@meta.route("/metadata/douban_cover", methods=["GET"])
def proxy_douban_cover():
    cover_url = urllib.parse.unquote(request.args.get('cover'))
    res = requests.get(cover_url, headers=DEFAULT_HEADERS)
    return Response(res.content, mimetype=res.headers['Content-Type'])

這樣就實(shí)現(xiàn)了,通過本地的/metadata/douban_cover?cover=https://xxxx地址顯示封面了。

服務(wù)地址

有了本地代理服務(wù),然后就需要在返回封面數(shù)據(jù)的時(shí)候把這個(gè)代理服務(wù)替換原始的豆瓣封面地址。

這里有點(diǎn)麻煩,因?yàn)?code>flask的request對象是通過線程本地變量實(shí)現(xiàn),元數(shù)據(jù)服務(wù)又是通過線程池去獲取元數(shù)據(jù),線程池中是獲取不到request對象的,嘗試獲取將會報(bào)錯(cuò)。

{RuntimeError}RuntimeError('Working outside of request context.\n\nThis typically means that you attempted to use functionality that needed\nan active HTTP request. Consult the documentation on testing for\ninformation about how to avoid this problem.')

因此只有在元數(shù)據(jù)獲取之后、往外輸出之前來修改封面地址,這里通過繼承MetaRecord重寫魔法函數(shù)__getattribute__來實(shí)現(xiàn)封面url地址的替換,參考代碼:

@dataclasses.dataclass
class DoubanMetaRecord(MetaRecord):

    def __getattribute__(self, item):  # cover通過本地服務(wù)代理訪問
        if item == 'cover' and DOUBAN_PROXY_COVER:
            cover_url = super().__getattribute__(item)
            if cover_url:
                try:
                    host_url = DOUBAN_PROXY_COVER_HOST_URL
                    if not host_url and request.host_url:
                        host_url = request.host_url
                    if host_url and host_url not in cover_url:
                        self.cover = host_url + DOUBAN_PROXY_COVER_PATH + urllib.parse.quote(cover_url)
                except BaseException:
                    pass
        return super().__getattribute__(item)

這樣就實(shí)現(xiàn)了豆瓣封面預(yù)覽功能了。

如果獲取的服務(wù)器地址不正確,可以手動指定DOUBAN_PROXY_COVER_HOST_URL='http://nas_ip:8083/'

封面下載

這個(gè)本地代理地址不能實(shí)現(xiàn)封面下載,因?yàn)槿绻诒4娣饷娴?code>flask路由中再次訪問其他flask路由會卡死,感覺是因?yàn)闆]有開啟flask的多線程處理請求的原因,在不修改calibre-web的情況下,要實(shí)現(xiàn)封面下載,可以覆蓋calibre-webhelper.save_cover_from_url方法,用自己寫的方法去替換掉save_cover_from_url,在檢測到是douban的封面地址的情況下,用requests去下載封面數(shù)據(jù),代碼如下:

    @staticmethod
    def hack_helper_cover():
        save_cover = helper.save_cover_from_url
        def new_save_cover(url, book_path):
            if DOUBAN_COVER_DOMAIN in url:
                cover_url = url
                if DOUBAN_PROXY_COVER:
                    component = urllib.parse.urlparse(url)
                    query = urllib.parse.parse_qs(component.query)
                    cover_url = urllib.parse.unquote(query.get('cover')[0])
                res = requests.get(cover_url, headers=DEFAULT_HEADERS)
                return helper.save_cover(res, book_path)
            else:
                return save_cover(url, book_path)
        helper.save_cover_from_url = new_save_cover

這樣就實(shí)現(xiàn)了封面預(yù)覽和下載了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容