原文網(wǎng)址
https://bookfere.com/post/562.html
之前書伴曾寫過一篇文章《Calibre 使用教程之抓取 RSS 制成電子書》柳琢,介紹了利用 Calibre 的“抓取新聞”功能把網(wǎng)站的新聞源制期刊樣式電子書的方法装诡。不過軟件界面上也只提供了直接添加 RSS 地址的方法壮虫,也就是說網(wǎng)站必須有 RSS 供稿才行,否則就無法抓取思瘟。那對于不提供 RSS 的網(wǎng)站是否能夠抓取它上面的內(nèi)容制成電子書呢荸百?本文就來介紹一種進階技巧來解決這個問題。
在開始具體步驟之前滨攻,先簡單的描述一下工作流程:首先編寫一個 Calibre Recipe 腳本文件够话,根據(jù) Calibre 指定的規(guī)范定義具體的抓取行為,然后使用 Calibre 把此腳本轉(zhuǎn)化成 mobi 格式電子書文件铡买。
注意更鲁,本文的相關(guān)操作是在命令行中進行的,并且會牽涉到簡單的代碼編寫奇钞,為了讓更多沒有編程基礎(chǔ)的小伙伴能直接上手使用澡为,本文會盡可能詳細的解釋每一條代碼的作用,以便套用景埃。
一媒至、認識 Calibre Recipe 腳本
Recipe 這個單詞的含義為“食譜”、“處方”谷徙,顧名思義拒啰,它為 Calibre 定義了抓取新聞源這一動作的執(zhí)行細節(jié)。Calibre 也為 Recipe 腳本提供了一份詳盡的文檔“API documentation for recipes”完慧,對所能使用的參數(shù)或函數(shù)做了詳細的說明谋旦。如果你有編程基礎(chǔ),可能感覺直接查看它的源代碼會更清晰一些。
在抓取 RSS 制成電子書那篇文章中册着,我們只需要在 Calibre 軟件界面上拴孤,通過“添加自定義新聞源(Add or edit a custom news source)”菜單項調(diào)出操作面板,在里面添加 RSS 地址就完事兒了甲捏,剩下的抓取演熟、轉(zhuǎn)換工作就全部交給 Calibre 自動處理了。其實在這個過程的背后司顿,Calibre 也是根據(jù)你添加的 RSS 地址自動生成了一個 Recipe 腳本芒粹,并根據(jù)此腳本抓取內(nèi)容的〈罅铮可以點擊“添加自定義新聞來源”操作面板左下角的【切換到高級模式】(Switch to advanced mode)按鈕化漆,便可以看到如下所示代碼:
#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import unicode_literals, division, absolute_import, print_function
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1504764482(BasicNewsRecipe):
title = 'My news source'
oldest_article = 7
max_articles_per_feed = 100
auto_cleanup = True
feeds = [
('科學松鼠會', 'http://songshuhui.net/feed'),
('泛科學', 'http://pansci.tw/feed'),
]
從以上這個簡單的 Recipe 腳本中,我們可以看到此腳本繼承了 Calibre 提供的 BasicNewsRecipe 這個類猎提,并簡單的重寫了一下這個類的某些參數(shù)获三。由于 Calibre 可以自動處理標準的 RSS 結(jié)構(gòu)旁蔼,所以不需要我們額外修改就可以輕松抓取內(nèi)容锨苏。但是對于不提供 RSS 的網(wǎng)站內(nèi)容又該如何處理呢?
對于不提供 RSS 的網(wǎng)站棺聊,我們可以通過解析頁面內(nèi)容伞租,獲取一個數(shù)據(jù)結(jié)構(gòu)再進行轉(zhuǎn)換。Calibre Recipe 腳本提供的 parse_index()
方法就可以用來做這件事限佩。下面我們就來編寫一個簡單的 Recipe 腳本葵诈。
<small>提示:Calibre Recipe 腳本的 parse_index()
方法需要解析網(wǎng)站頁面的代碼結(jié)構(gòu)來提取數(shù)據(jù),但由于不同網(wǎng)站的代碼結(jié)構(gòu)也不相同祟同,從而處理邏輯也會有所差異作喘,所以抓取不同的網(wǎng)站內(nèi)容,可能就需要寫一個與之相對應(yīng)的 Recipe 腳本晕城。</small>
二泞坦、編寫 Calibre Recipe 腳本
下面以王垠的博客“當然我在扯淡”為例,編寫一個 Recipe 腳本砖顷,將整個博客內(nèi)容轉(zhuǎn)制成 mobi 格式的電子書贰锁。這個博客頁面結(jié)構(gòu)比較簡單,個人感覺比較適合上手滤蝠,初步了解一些基本的 Recipe 腳本寫法豌熄。
在開始編寫代碼之前我們先來分析一下這個博客的頁面結(jié)構(gòu):博客的首頁即是全部文章列表,列表中每一篇文章的標題被被類選擇器 li.list-group-item 包裹著物咳。這樣我們就可以提取出所有文章的標題和文章鏈接锣险,并據(jù)此循環(huán)處理每一篇文章內(nèi)容,組合成可供 Calibre 轉(zhuǎn)換的數(shù)據(jù)結(jié)構(gòu)。
下面是可用的 Recipe 腳本代碼芯肤,代碼中每一行都做了注釋夯接。看不懂可以看下面的詳細解釋纷妆。
#!/usr/bin/python
# encoding: utf-8
from calibre.web.feeds.recipes import BasicNewsRecipe # 引入 Recipe 基礎(chǔ)類
class Wang_Yin_Blog(BasicNewsRecipe): # 繼承 BasicNewsRecipe 類的新類名
#///////////////////
# 設(shè)置電子書元數(shù)據(jù)
#///////////////////
title = '當然我在扯淡' # 電子書名
description = u'王垠的博客' # 電子書簡介
#cover_url = '' # 電子書封面
#masthead_url = '' # 頁頭圖片
__author__ = '王垠' # 作者
language = 'zh' # 語言
encoding = 'utf-8' # 編碼
#///////////////////
# 抓取頁面內(nèi)容設(shè)置
#///////////////////
#keep_only_tags = [{ 'class': 'example' }] # 僅保留指定選擇器包含的內(nèi)容
no_stylesheets = True # 去除 CSS 樣式
remove_javascript = True # 去除 JavaScript 腳本
auto_cleanup = True # 自動清理 HTML 代碼
delay = 5 # 抓取頁面間隔秒數(shù)
max_articles_per_feed = 999 # 抓取文章數(shù)量
#///////////////////
# 頁面內(nèi)容解析方法
#///////////////////
def parse_index(self):
site = 'http://www.yinwang.org' # 頁面列表頁
soup = self.index_to_soup(site) # 解析列表頁返回 BeautifulSoup 對象
links = soup.findAll("li",{"class":"list-group-item title"}) # 獲取所有文章鏈接
articles = [] # 定義空文章資源數(shù)組
for link in links: # 循環(huán)處理所有文章鏈接
title = link.a.contents[0].strip() # 提取文章標題
url = site + link.a.get("href") # 提取文章鏈接
a = {'title': title , 'url':url} # 組合標題和鏈接
articles.append(a) # 累加到數(shù)組中
ans = [(self.title, articles)] # 組成最終的數(shù)據(jù)結(jié)構(gòu)
return ans # 返回可供 Calibre 轉(zhuǎn)換的數(shù)據(jù)結(jié)構(gòu)
首先引入 Calibre 提供的基礎(chǔ)類 BasicNewsRecipe
并創(chuàng)建一個繼承基礎(chǔ)類的新類 Wang_Yin_Blog
盔几。
接下來重寫一些可作為電子書的元數(shù)據(jù)的參數(shù)。如標題掩幢、簡介逊拍、作者、語言际邻、編碼之類芯丧。注意上面代碼中 cover_url
和 masthead_url
這兩個參數(shù)被注釋掉了,這樣 Calibre 會自動生成封面和期刊頭世曾。如果你想要自定義電子書封面和期刊頭缨恒,可以使用這兩個參數(shù)指定圖片的路徑。
然后還需要設(shè)置控制抓取頁面所需要的一些參數(shù)轮听。如去除電子書不需要的 CSS 樣式和 Javascript 腳本骗露,設(shè)定抓取頁面的時間間隔(避免對目標服務(wù)器造成負擔),設(shè)定抓取文章的數(shù)量(如果想要抓取所有文章設(shè)置一個足夠大的數(shù)值即可)等血巍。注意以上代碼中有一個 auto_cleanup
參數(shù)萧锉,它會用可讀性算法自動清理 HTML 標簽提取頁面中的有用內(nèi)容。如果頁面內(nèi)容比較復(fù)雜述寡,還可以使用 keep_only_tags
這個參數(shù)柿隙,指定僅提取頁面中某個標簽中的內(nèi)容,因為本例頁面內(nèi)容較簡單就注釋掉了鲫凶。
相關(guān)參數(shù)設(shè)置完畢后禀崖,就可以編寫處理頁面內(nèi)容的 parse_index()
方法了。在此方法中 Calibre 使用了內(nèi)置的 Python 模塊 BeautifulSoup螟炫。首先把首頁的文章列表解析成 BeautifulSoup 對象波附,然后提取出所有標題列表,循環(huán)處理這些列表后不恭,最終合并成一個完整的數(shù)據(jù)結(jié)構(gòu)交給 Calibre 轉(zhuǎn)換處理叶雹。
這樣一個簡單的 Recipe 腳本就寫完了,將其保存為 .recipe 文件備用换吧,本例保存為 wangyin.recipe折晦。接下來就可以把這個“小處方”轉(zhuǎn)換成 mobi 格式的電子書文件了。
<small>提示:當然有些網(wǎng)站的情況要復(fù)雜得多沾瓦,比如處理帶分頁的頁面满着、復(fù)雜內(nèi)容類型谦炒,還有多內(nèi)容來源的合并等,這些進階技巧限于篇幅暫不展開风喇。如果感興趣宁改,也可以翻一翻 Calibre 提供的 API 文檔“API documentation for recipes”自行研究一下。</small>
三魂莫、認識命令行工具 ebook-convert
有了寫好的 Recipe 腳本还蹲,接下來的工作就是將其轉(zhuǎn)化成 mobi 格式的電子書文件了。
在《Calibre 使用教程之批量獲取電子書元數(shù)據(jù)》這篇文章中耙考,我們認識了 Calibre 的一個命令行工具 ebook-meta
谜喊,它可以獲取電子書的元數(shù)據(jù)。現(xiàn)在要接觸到另外一個命令行工具 ebook-convert
倦始,此工具可以把某種格式轉(zhuǎn)換成另一種格式斗遏。比如想要把某個 epub 轉(zhuǎn)換成 mobi,只需要輸入以下命令即可:
ebook-convert BookName.epub BookName.mobi
當然想要使用 ebook-convert
命令需要預(yù)先在電腦里安裝 Calibre鞋邑。在 Windows 系統(tǒng)中诵次,一般安裝完成后即可直接在“命令提示符”中使用。對于 macOS 系統(tǒng)則需要設(shè)置一下環(huán)境變量枚碗,設(shè)置方法和 ebook-meta
一樣逾一,參考《Calibre 使用教程之批量獲取電子書元數(shù)據(jù)》這篇文章中的“準備 ebook-meta 工具”。
四视译、把 Recipe 腳本轉(zhuǎn)化為 mobi 文件
和轉(zhuǎn)換普通的電子書的格式一樣嬉荆,只需要輸入以下命令即可開始進行轉(zhuǎn)化。轉(zhuǎn)換所需要的時間和文章條目和網(wǎng)速相關(guān)酷含,如果你抓取的站點不幸被墻了,還需要使用網(wǎng)絡(luò)代理汪茧。
ebook-convert wangyin.recipe wangyin.mobi --output-profile kindle
注意上面的代碼中增加了一個參數(shù) --output-profile kindle
椅亚,這個參數(shù)的意思是根據(jù) Kindle 設(shè)備做適配,如果不添加這個參數(shù)舱污,轉(zhuǎn)換出來的電子書會有一個對 Kindle 來說多余的翻頁導航呀舔。
另外在轉(zhuǎn)換的過程中也會有意外情況,比如由于資源鏈接被墻扩灯,或由于網(wǎng)絡(luò)不穩(wěn)定導致頁面抓取失敗媚赖。本例中抓取的博客由于引用了兩張 Google 服務(wù)器上的圖片,不使用代理就會抓取失敗珠插。
以上命令執(zhí)行完畢后便可以得到最終的電子書文件 wangyin.mobi惧磺,拷貝或推送到 Kindle 即可閱讀。
<small>提示:如果你不想使用命令行工具捻撑,當然也可以使用 Calibre 界面上的“抓取新聞”功能來完成同樣的工作磨隘。你只需要把編寫好的 Recipe 代碼粘貼到新建的 Recipe 腳本中缤底,或者直接導入已有的 Recipe 腳本文件,然后和抓取 RSS 的操作一樣番捂,在“定期新聞下載”面板上選中“自定義腳本”个唧,點擊【立即下載】按鈕即可完成轉(zhuǎn)換。不過這種方法會始終帶有翻頁導航设预。</small>
五徙歼、現(xiàn)成的 Calibre Recipe 腳本
除了自己手動針對某個網(wǎng)站的內(nèi)容編寫 Recipe 腳本外,對于一些知名度較高的站點鳖枕,已經(jīng)有很多現(xiàn)成的 Recipe 腳本可用鲁沥,比如 Calibre 項目自身就提供了一個 Recipe 腳本庫(Calibre 的“抓取新聞”內(nèi)置的那些就是使用的這些 Recipe 腳本)。另外也有很多網(wǎng)友也分享了自己編寫的的 Recipe 腳本耕魄,你可以訪問 GitHub 搜索關(guān)鍵字“calibre recipe”來查找感興趣的腳本画恰。當然也歡迎你的分享。
以上就是利用 Recipe 腳本抓取不提供 RSS 的網(wǎng)站內(nèi)容并制成電子書的方法吸奴。以上內(nèi)容盡量兼顧沒有任何編程經(jīng)驗的小伙伴允扇,如果按照你的理解方式對那些地方不太明白,請留言则奥,確認有誤區(qū)后會按照你的意見進行更改考润。如果你發(fā)現(xiàn)本文存在錯誤,也歡迎留言指正读处。有更好的玩兒法糊治,也歡迎分享。
我自己的作品
以下是我成功抓取crazyguyonabike.com上一篇日志的recipe文件罚舱。改了幾個地方井辜,居然成功了。
#!/usr/bin/python
# encoding: utf-8
from calibre.web.feeds.recipes import BasicNewsRecipe # 引入 Recipe 基礎(chǔ)類
class Wang_Yin_Blog(BasicNewsRecipe): # 繼承 BasicNewsRecipe 類的新類名
#///////////////////
# 設(shè)置電子書元數(shù)據(jù)
#///////////////////
title = 'nz tips' # 電子書名
description = 'nz tips for crazy' # 電子書簡介
#cover_url = '' # 電子書封面
#masthead_url = '' # 頁頭圖片
__author__ = 'some' # 作者
language = 'zh' # 語言
encoding = 'utf-8' # 編碼
#///////////////////
# 抓取頁面內(nèi)容設(shè)置
#///////////////////
#keep_only_tags = [{ 'class': 'example' }] # 僅保留指定選擇器包含的內(nèi)容
no_stylesheets = True # 去除 CSS 樣式
remove_javascript = True # 去除 JavaScript 腳本
auto_cleanup = True # 自動清理 HTML 代碼
delay = 5 # 抓取頁面間隔秒數(shù)
max_articles_per_feed = 999 # 抓取文章數(shù)量
#///////////////////
# 頁面內(nèi)容解析方法
#///////////////////
def parse_index(self):
site = 'https://www.crazyguyonabike.com/doc/?o=1mr&doc_id=5873&v=6H' # 頁面列表頁
site2 = 'https://www.crazyguyonabike.com'
soup = self.index_to_soup(site) # 解析列表頁返回 BeautifulSoup 對象
links = soup.findAll("dd") # 獲取所有文章鏈接
articles = [] # 定義空文章資源數(shù)組
for link in links: # 循環(huán)處理所有文章鏈接
title = link.a.contents[0].strip() # 提取文章標題
url = site2 + link.a.get("href") # 提取文章鏈接
a = {'title': title , 'url':url} # 組合標題和鏈接
articles.append(a) # 累加到數(shù)組中
ans = [(self.title, articles)] # 組成最終的數(shù)據(jù)結(jié)構(gòu)
return ans # 返回可供 Calibre 轉(zhuǎn)換的數(shù)據(jù)結(jié)構(gòu)
```
只改了幾個地方管闷。
1是增加了一個site2的變量粥脚,因為日志的目錄頁不是首頁。
2是把需要查找的tag修改成頁頁上的dd包个。