日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

用Java抓取RSS生成Mobi文件发送到Kindle

發(fā)布時間:2023/12/14 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用Java抓取RSS生成Mobi文件发送到Kindle 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近寫了個小工具,通過抓取RSS生成適合Kindle展示的Mobi格式的文件,并發(fā)送到Kindle 個人圖書館,也算是繼續(xù)“自動化”之旅。

代碼前前后后寫了個把月,趁著放假期間,決定把它搞定。使用方法什么的就不多說了,項目開源到Github上去了,上面有使用說明。

這篇文章簡單得聊聊項目本身以及總結(jié)的一些問題。

項目簡介

首先來看看項目的組織結(jié)構(gòu)圖:


大致分為三大部分:

  • core:核心部分,定義了RSS相關(guān)的基本數(shù)據(jù)抽象(base)以及所有流程的接口抽象(contract)
  • component:組件(可選),兩個組件,一個是對mobi文件進行strip處理,以很好得控制最終文件的大小,另一個是郵件發(fā)送
  • impl:對core以及component的實現(xiàn)。
其他幾個部分:util中定義了一些幫助方法,Service是程序的入口,也是各個流程接口方法的銜接與組合調(diào)用,resources(不包含在上圖中)定義了一大堆各種配置。用Java寫程序就是這樣,你懂的。

core


base包不必多說,一個RSS源大概可以分解為三個對象:一個Feed包含了N個Entry,每個Entry都包含了文本內(nèi)容(desc)以及若干個Image。 說說核心流程的抽象:(按照流程順序說明,不按照圖中接口順序)

AbstractConfigManager

是一個抽象類,維護了項目最核心的配置文件(resources/rtms.properties),并對其中的配置完整性進行檢查。

IFeedLinkProvider

要抓取RSS,必須要有RSS Source(RSS URL)。該接口作為Feed link 的 provider 定義了一個方法來獲取所有需要抓取的鏈接: /*** get parsing rss links** @return the String array of all links*/String[] getFeedLinks();

IRSSParser

獲取到feed的鏈接之后,就需要去一個個parse它們,該接口定義了parse方法: /*** parse feed with given urls** @param urls the URL Array* @return the BaseFeed Array*/BaseFeed[] parse(URL[] urls);
返回生成的Feed對象集合,由于有些RSS源不提供全文輸出(只是提供一個摘要,比如看這里),所以這時候就需要一個全文輸出處理,來從給定的URL獲取原文

IFullTxtOutput

該接口定義了輸出全文的協(xié)議: /*** output full text from a entrylink** @param entryLink the string of the entry link url* @return the full text*/String fullTextFrom(String entryLink);
在parse的過程中,難免會遇到<img />標(biāo)簽,我們在parse時候先對其src屬性進行處理,但圖片并不在此進行下載,所以需要將其先存起來,在需要下載的時候,在取出來下載,這時,我們需要一個“圖片運輸器“

IImgTransporter

該接口定義了圖片的保存與獲取協(xié)議: /*** push a img obj to the image store** @param img the instance of BaseImage*/public void push(BaseImage img);/*** get all image objs with the feed** @param feed the instance of BaseFeed* @return the map of all the images per feed*/public Map<String, BaseImage> getAllImageObjsWithFeed(BaseFeed feed);
由于我們認(rèn)為一個RSS Feed是一個基準(zhǔn)單位(一個mobi文件以一個個feed組合而成)所以圖片的獲取以feed為單位整取(當(dāng)然也可以粒度再細(xì)一點以feed里每個entry為基準(zhǔn))。
如果不需要額外的功能,我們已經(jīng)能夠生成mobi文件了:

IMobiGenerator

該接口定義了生成mobi需要實現(xiàn)的方法: /*** generate mobi file with a list of feeds** @param feeds the List of BaseFeed instances* @return return the generated mobi file path*/String generate(List<BaseFeed> feeds);
通過傳入一個BaseFeed對象的集合,最終輸出mobi文件的全路徑即可。 以上這些接口,定義了將RSS生成Mobi文件的最基本的流程,如果需要一些附加功能,可以有選擇的實現(xiàn)component package中的一些接口。

component

該package大概提供了三個擴展:

IEntryTransporter

上面在說到IfullTxt的時候,我們通過實現(xiàn)該接口,完成了全文輸出。出于效率與性能考慮,我們有必要將最近處理過的feed的一些entry“緩存”起來。這是因為,該項目有可能被構(gòu)建為daily task,而有些RSS 源并不是每日更新,即便更新過,如果不多,還是會抓取到一些舊的。這時我們會先去查看這些entry是否曾經(jīng)已被處理過,如果已經(jīng)處理過,那么就沒必要再去處理了(特別是全文輸出還是很耗時的),協(xié)議: /*** save a entry** @param entry the instance of BaseEntry*/void save(BaseEntry entry);/*** check is entry exists** @param entry the instance of BaseEntry* @return if exists return true otherwise return false*/boolean entryExists(BaseEntry entry);/*** get processed entry (the func for cache entry)** @param entry the instance of BaseEntry* @return return the processed entry*/BaseEntry getProcessedEntry(BaseEntry entry);/*** get a feed's all entry (processed)** @param feed the instance of BaseFeed* @return the map of the feed's entries (key is entry's link)*/Map<String, BaseEntry> getAllEntryPerFeed(BaseFeed feed);

IFileStripHandler

由于生成適合kindle的mobi文件,大致的手段是通過amazon提供的kindlegen命令行工具實現(xiàn),但通過他生成的文件非常大(大到幾十兆)。這樣非常不便于網(wǎng)絡(luò)傳輸(特別是郵件發(fā)送),在不影響閱讀的情況下,是有辦法大幅減小其大小的,所以,我們定義類該接口,供需要的擴展實現(xiàn): /*** do strip* @param originalFilePath the original file path* @param newFilePath the new path*/void doStrip(String originalFilePath, String newFilePath);
最后,如果有必要,可以將生成的文件發(fā)生到kindle接受的圖書館(一個認(rèn)可的郵件地址),這時我們需要抽象出,郵件發(fā)送的接口:

IMailSender

它定義了發(fā)送帶附件的郵件接口: /*** send mobi file from path* @param filePath the mobi file path*/void sendFrom(String filePath);

impl

該package默認(rèn)實現(xiàn)了core以及component里所有的接口(既然全都實現(xiàn)了,還抽出接口來干嘛?)。這里,我只是提供了默認(rèn)實現(xiàn),抽出接口的原因是為了保證流程的穩(wěn)定性,從而不必過份關(guān)注實現(xiàn)(下面你會看到實現(xiàn)的方式可能并不是唯一的),即使實現(xiàn)變了,也只是某個對象的內(nèi)部發(fā)生改變,不至于擴大影響。

抽象與實現(xiàn)

哪里可能存在變化呢?因為我本機裝了redis,所以我對圖片對象以及entry對象的存儲借助于redis,如果你不熟悉redis你可以更改其他的存儲方式。FeedLink我默認(rèn)存放在一個properties配置文件里,你可以存放在其他數(shù)據(jù)源內(nèi)。RSSParser采用的是ROM+jsoup的解析方式,FullTxt 以及 Strip mobi借助于python來實現(xiàn)。。。由于有很多開源庫可供選擇,所以,如果你對某個庫更熟悉,你可以更改它的實現(xiàn),而不必傷筋動骨。

庫與技術(shù)點總結(jié)

(1)mobi文件生成其實是可以基于html文件的,因而可以定義出固定的templete,填充內(nèi)容后直接生成即可,這里使用的是freemarker (2)解析Feed以及對html進行過濾處理使用的是ROM+jsoup (3)json處理采用gson (4)全文輸出借助于python的兩個強大的開源庫(feedparser / readability-lxml)。原理大概是解析dom,對某些元素的父元素計算特征值,可參考這篇文章?以及 這篇文章 (5)mobi文件生成,借助于amazon提供的 for mac平臺的kindlegen (6)郵件發(fā)送采用的是javamail (7)strip減小mobi文件大小也借助于python的實現(xiàn),通過刪除文件內(nèi)部大量無用的空白等 (8)圖片多線程下載,采用的是線程池,這邊還有待重新處理

TODO

(1)重新實現(xiàn)多線程圖片下載 (2)用node.js 起一個http server或者構(gòu)建一個linux定時任務(wù),做daily task
具體的使用方法以及后續(xù)更新請關(guān)注:https://github.com/yanghua/RssToMobiService

總結(jié)

以上是生活随笔為你收集整理的用Java抓取RSS生成Mobi文件发送到Kindle的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。