创建将 Pocket 文章存储为 PDF 的服务






4.88/5 (10投票s)
一个小型项目,用于创建一个服务,该服务下载保存到 Pocket 以供日后阅读的文章,并将其转换为 PDF。
引言
我目前使用一个系统,通过在 Feedly 中浏览和阅读文章来管理我的新闻流,而那些我认为要为将来参考而保留的文章,我会将它们保存到我的 Pocket 账户。
将它们保存在 Pocket 中对于将链接转发给朋友等很有用,但在 Pocket 中积累了相当大的文章参考库后,很难轻松找到我正在寻找的文章,也很难一次性搜索我所有参考的资料。
为了解决这个问题,我构建了一个 Windows 服务,该服务使用 Pocket API 下载我所有已保存链接的列表,然后从该列表中,它会下载所有新条目并将其保存到我的本地计算机。这样,我最终会得到所有文章的可搜索 PDF,然后我可以使用我喜欢的引用程序来组织和引用它们。
使用代码
必备组件
在您开始使用代码之前,您需要创建自己的 Pocket 应用程序并为您的 Pocket 用户创建 access_token。由于这更像是一个未完成的项目,我只会指导您如何无需编码即可完成此操作。
以下是创建所需密钥的步骤:
- 创建 Pocket 应用程序。
- 创建您的 access_token。
这是一个简单的步骤。您只需访问 此页面 并输入所需信息即可。完成此操作后,您将获得自己的应用程序“consumer key”,稍后将使用它来授权您的用户使用新应用程序。
这需要多一点技巧。您可以将以下文本保存到 HTML 文件中,并按照其中的步骤获取您的 access_token。它在 Pocket 开发者网站上的 这里 有记录,基本上您是通过一些回调的技巧来完成这些步骤的。
<html><head></head><body>
1. Enter your consumer_key and hit "Submit" and you should get a response saying "code=XXXX"
<form action="http://getpocket.com/v3/oauth/request" method="post">Consumer_key<input type="text"
name="consumer_key" value =""><input type="hidden" name="redirect_uri"
value="fobar://test"><input type="submit" value="Get code"></form>
2. Go to the following link and accept the application authorization request.
<p>Go to https://getpocket.com/auth/authorize?request_token=<code from step 1>&redirect_uri=fobar://test</p>
3. Enter your consumer_key and the code from step 1> Push submit and you should get a response with "access_token=YYYYY"
<form action="http://getpocket.com/v3/oauth/authorize" method="post">Consumer_key<input
type="text" name="consumer_key" value =""> Code<input type="text"
name="code" value =""><input type="submit" value="Authorize"></form>
</body></html>
现在您已经获得了您的 access_token 和 consumer_key,它们将被用于服务以下载您的 Pocket 信息。
您还需要下载 iTextsharp 库,因为它在代码中被引用。
通用逻辑
这个项目的通用想法是定期获取保存到特定 Pocket 帐户的文章列表(URL),并将其与服务已下载文章的已存储列表进行比较。如果有任何新文章,它们将被下载并转换为 PDF 格式,然后保存到指定的目录。文章下载完成后,由服务下载的文章的存储列表将用新条目更新。
连接到 Pocket
如果您正确创建了密钥,获取您所有 Pocket 文章/URL 列表的任务将非常直接。您需要的所有信息都记录在 此页面 上,我的代码中的相关部分如下所示。
using (WebClient test = new WebClient())
{
System.Collections.Specialized.NameValueCollection reqparm =
new System.Collections.Specialized.NameValueCollection();
reqparm.Add("consumer_key", consumer_key);
reqparm.Add("access_token", access_token);
reqparm.Add("state", "all");
reqparm.Add("detailType", "complete");
byte[] responsebytes = test.UploadValues("https://getpocket.com/v3/get", "POST", reqparm);
string responsebody = Encoding.UTF8.GetString(responsebytes);
...
}
这段代码将检索一个 JSON 列表,其中包含有关给定 Pocket 帐户的信息,包括 Pocket 中保存的每个 URL 的信息,例如标签、添加日期等。
从这段 JSON 文本中,我使用正则表达式提取我需要的信息,而不是使用更复杂的 JSON 解析器。
MatchCollection matches = Regex.Matches(responsebody,
"\"given_title\":\"(.*?)\".*?\"resolved_title\
":\"(.*?)\".*?\"resolved_url\":\"(.*?)\"");
然后,我将每个 URL 添加到一个列表中,用于与已获取文章的已保存列表进行比较。顺便说一句,该列表保存在 XML 文件中,并使用 XmlSerializer
对象进行序列化/反序列化。
创建 PDF 的两种方法
现在,对于不在已下载文章列表中的每个 URL,将在指定目录中创建一个 PDF。
我最初使用 WKHTMLToPDF 来创建 PDF,但我发现生成的质量不如我之前使用的在线供应商 PDFCrowd,所以我编写了一个小程序来使用该站点。不过,这样做我相当确定我可能违反了用户协议,但由于我进行的转换量很少,我希望没问题。
使用 WKHTMLToPDF
这种方法非常直接。我只需启动一个新进程,并使用应用程序所需的命令行选项(请确保您已安装该程序,并且您的 app.config 指向正确的路径!),然后让它完成其余工作。之后,我只需检查程序的输出,看看它是否顺利完成。
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("\""+ wkhtmlToPDFExePath + "\"");
procStartInfo.Arguments = "--load-error-handling ignore --zoom 1.33 --javascript-delay 5000 \"" + url +
"\" \"" + tempFileName + "\"";
使用 PDFCrowd
使用 PDFCrowd 需要在 app.config 文件中填写您的帐户用户名和密码,然后模拟网络浏览会话来创建和下载 PDF。代码中有一些细节,您可以自己查看它是如何模拟会话的。
我应该提到的一点是使用 CookieAwareWebClient
类,它将使用 cookie 来匹配客户端的服务器会话,主要是为了登录状态。
添加 PDF 元数据
作为一个小小的补充,我使用 iTextsharp 来为 PDF 添加一些元数据,以便更容易导入文档管理系统。我最初也想为文件添加关键字 - 并将它们与 Pocket 标签同步 - 但由于我的 DMS 不支持该功能,所以我基本上没有实现它。不过,我确实在 PDF 元数据中添加了源 URL、作者(网页)和标题。
private static void AddMetadataToPDF(string filePath, string url,
string author, string title, string keywords)
{
string tempFileName = System.IO.Path.GetTempFileName();
PdfReader pdfReader = new PdfReader(filePath);
pdfReader.SelectPages("1-" + pdfReader.NumberOfPages);
using (PdfStamper stamper =
new PdfStamper(pdfReader, new FileStream(tempFileName, FileMode.Create)))
{
Dictionary<String, String> info = new Dictionary<string,string>();
info.Add("Keywords", keywords);
info.Add("Title", title);
info.Add("Author", author);
info.Add("OriginalUrl", url);
info.Add("CreationDate", "D:" +
DateTime.Now.ToString("yyyyMMddHHmmss") + "-01'01'");
stamper.MoreInfo = info;
stamper.Close();
}
pdfReader.Close();
File.Delete(filePath);
File.Move(tempFileName, filePath);
}
设置
app.config 文件包含 configurationFile
、logFile
、downloadFolder
、interval
、accessToken
、consumerKey
、createUsingPDFCrowd
、pdfCrowdUserName
、pdfCrowdPassword
、createUsingWKHTMLToPDF
、wkhtmlToPDFExePath
。这些都非常容易理解,但您需要正确设置它们,程序才能正常工作。
由 configurationFile
条目指向的 configuration.xml 文件也需要是具有以下内容的 xml 文件才能工作。顺便说一句,这将是保存已下载 URL 列表的文件。
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfDownloadedUrl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</ArrayOfDownloadedUrl>
错误处理
这更像是一个“概念验证”项目,而不是一个发布候选版本,所以错误处理充其量也很糟糕 :)
如果您想将其投入某种生产状态,您确实需要采取一些措施使其更具容错性。
考虑到这是一个没有用户界面且只能通过各种日志记录与用户/管理员通信的 Windows 服务,您确实需要使其故障安全。