65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (10投票s)

2013 年 8 月 19 日

CPOL

5分钟阅读

viewsIcon

28117

downloadIcon

7

一个小型项目,用于创建一个服务,该服务下载保存到 Pocket 以供日后阅读的文章,并将其转换为 PDF。

引言

我目前使用一个系统,通过在 Feedly 中浏览和阅读文章来管理我的新闻流,而那些我认为要为将来参考而保留的文章,我会将它们保存到我的 Pocket 账户。

将它们保存在 Pocket 中对于将链接转发给朋友等很有用,但在 Pocket 中积累了相当大的文章参考库后,很难轻松找到我正在寻找的文章,也很难一次性搜索我所有参考的资料。

为了解决这个问题,我构建了一个 Windows 服务,该服务使用 Pocket API 下载我所有已保存链接的列表,然后从该列表中,它会下载所有新条目并将其保存到我的本地计算机。这样,我最终会得到所有文章的可搜索 PDF,然后我可以使用我喜欢的引用程序来组织和引用它们。 

使用代码

必备组件

在您开始使用代码之前,您需要创建自己的 Pocket 应用程序并为您的 Pocket 用户创建 access_token。由于这更像是一个未完成的项目,我只会指导您如何无需编码即可完成此操作。

以下是创建所需密钥的步骤: 

  1. 创建 Pocket 应用程序。
  2. 这是一个简单的步骤。您只需访问 此页面 并输入所需信息即可。完成此操作后,您将获得自己的应用程序“consumer key”,稍后将使用它来授权您的用户使用新应用程序。

  3. 创建您的 access_token。
  4. 这需要多一点技巧。您可以将以下文本保存到 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=&lt;code from step 1&gt;&redirect_uri=fobar://test</p>
3. Enter your consumer_key and the code from step 1&gt; 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 文件包含 configurationFilelogFiledownloadFolderintervalaccessTokenconsumerKeycreateUsingPDFCrowdpdfCrowdUserNamepdfCrowdPasswordcreateUsingWKHTMLToPDFwkhtmlToPDFExePath。这些都非常容易理解,但您需要正确设置它们,程序才能正常工作。

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 服务,您确实需要使其故障安全。 

© . All rights reserved.