使用 Org-mode 管理网络书签

发布于 2024-11-14 15:08:25 字数 8448 浏览 9 评论 0

安息吧!del.icio.us

若你对我之前所使用的方案不感兴趣的话,可以跳过此章。

我使用 delicious 来存储,管理,访问网络书签已经有几年的历史了. delicious 的 Firefox 插件实在太好用了,用它来存储,贴标签,加注释,管理书签实在很方便。

不幸的是,delicious 先是被卖给了 yahoo,随后又被卖给了 AVOS. 他们决定对 n 原本完美的社交化书签网络服务 (social bookmark web service ) 作出一些改变。我喜爱的 firefox 插件也在某一天停止工作了。我一点也不喜欢信的 delicious,它完全毁了我使用多年的完美工作流程。

因此,我开始寻找管理书签的替代方法。同时,我也开始尝试使用 Org-mode 来完成尽可能多的工作。这也是为什么 我想使用 Org-mode 来管理书签的原因,虽然这样以来就丢失像为书签和标签导出 RSS-feeds 这一类的社交化书签方面的功能 (social bookmark features)。再说,云服务已经毁了我的数字化生活一次了。我想这次还是独立一些比较好。

我的需求

在我说明实现的具体细节之前,我先描述一下我想象中理想的工作流程吧。我上面提到过的,我希望使用 Org-mode 来解决我的需求,为此我可以付出缺失任何社交化功能(例如 feeds) 的代价。

我需要在 Android 手机,桌面浏览器以及 GNU/Emacs 上都能保存书签。并且,我还需要能够为这些书签打标签以及添加注释。借助 Org-mode,我完全可以导航,搜索,过滤,打开以及修正这些已存储的书签。

实现方案

我有一个名为 notes.org 的文件,用于收集各种片段,知识,想法,指南之类的东西. 在文件最后,我创建了一个新的主标题,名为"Bookmarks". 在该主标题下存放的是我收集的网络书签,其格式与普通的 Org-mode 事项一样:

,** [What do people use to get stuff done?]( http://usesthis.com/interviews/) :pim:diy:hardware:software:
  :PROPERTIES:
  :CREATED: [2014-08-09 Sat 10:41]
  :END:

该书签带给了我关于硬件、软件、工作环境,有远见的想法等主题的源源不断的灵感!该 URL 是我从 heise 论坛中偶尔发现的。

参见 我的博客

台式电脑上捕获

在我的台式电脑上捕获书签很简单,我是用的每台电脑上运行有 GNU/Emacs 及 Org-mode,我使用以下捕获模板来创建书签:

  (setq org-capture-templates
    '(
      ;; many more capture templates
      ("b" "Bookmark" entry (file+headline "~/share/all/org-mode/notes.org" "Bookmarks")
        "* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n" :empty-lines 1)
      ;; many more capture templates
      )
    )

我曾经试验过 org-protocol 但是失败了. org-protocol 允许从外部软件(例如你的网络浏览器) 中直接捕获书签到 Org-mode 中。

虽然实验 org-protocol 失败了,但是我并不觉得现在方案就会很繁杂,我只要准照下面几个步骤就能添加一个书签了:首先,在浏览器中按下 C-l 跳转到地址栏并选中当前页面的 URL。然后我按下 C-c 拷贝 URL 到粘贴版,切换到 Emacs/Org-mode (通常位于左边的虚拟桌面中) 并按下捕获书签的快捷键: C-c c b。最后,我通过手工或者通过函数 my-url-linkify (函数定义在下面) 来插入 URL 及其描述即可。

在 Android 手机上捕获

大多数的书签都是我在手机上阅读 Atom/RSS feeder 时保存下来的. 幸运的是,我可以很容易地将 URL 分享給 MobileOrg,MobileOrg 可以与我的桌面环境的 Org-mode 相互同步,按照惯例,我需要在 URL 前面加上 Bookmark ​ 说明,这样的话,我就能将其他事项与要保存的书签 URL 区分开了。

同步之后,MobileOrg 中的书签会在 inbox.org 中产生类似下面的一条记录:

,* NEXT Bookmark [Homepage of Karl Voit]( http://Karl-Voit.at )
[2014-08-10 Sun 16:24]   

通过 Elisp 函数 my-save-bookmark , 我能将 inbox.org 中的书签快速迁移到对应的 note.org 中对应的标题下,而且还能自动触发打标签的动作,总结来说,我只需要在书签上按一个快捷键,然后输入标签就行了。

下面这个 Elisp 函数是我刚开始学习 Elisp 时写得,因此若觉得还有改进的空间,请发我补丁:

;; ######################################################
;; smart moving bookmark headings from inbox to notes.org
;; see id:2014-03-09-inbox-to-bookmarks
(defun my-save-bookmark()
  "removes NEXT/Bookmark, (NOT YET: FIXXME: retrieves title),
move time-stamp to CREATED, re-file to bookmarks, invoke Org-mode tagging process"
  (interactive)
  (save-excursion
    ;; get myself to the beginning of the current heading:
    ;;(outline-previous-visible-heading 1)  ;; jump to previous heading
    ;;(outline-next-visible-heading 1)      ;; jumps to beginning of the current (interesting) heading
    (beginning-of-line)                   ;; jump to beginning of line
    (let ((mybegin (point)))              ;; mark beginning of line as start point
      (outline-next-visible-heading 1)    ;; jumps to EOF if it is the last entry
      (save-restriction
        (narrow-to-region mybegin (point))  ;; ignore everything outside of region
        ;; search/replace unwanted keywords at the beginning:
        (goto-char (point-min))
        (while (search-forward "* NEXT Bookmark " nil t) (replace-match "* " nil t))
        (goto-char (point-min))
        (while (search-forward "* NEXT " nil t) (replace-match "* " nil t))
        (goto-char (point-min))
        (while (search-forward "* Bookmark " nil t) (replace-match "* " nil t))
        (goto-char (point-min))
        (while (search-forward "//m.heise.de" nil t) (replace-match "//heise.de" nil t));; remove mobile heise URL
        (goto-char (point-min))
        (while (search-forward "/from/atom10?wt_mc=rss.ho.beitrag.atom" nil t);; remove heise RSS tags
          (replace-match "" nil t)
          )
        (goto-char (point-min))
        ;; insert second asterisk (modify to second level heading)
        (insert "*")
        ;; move time-stamp to properties-drawer:
        (search-forward-regexp "^\\[20")  ;; jump to second line (with time-stamp) via search
        (beginning-of-line)
        (insert ":PROPERTIES:\n:CREATED:  ")
        (end-of-line)
        (newline)
        (insert ":END:\n")
        ;; move region to end of notes.org
        (kill-region mybegin (point)) ;; kill region to kill-ring
        (switch-to-buffer "notes.org")
        (end-of-buffer)
        (newline)
        (yank)
        ;; add tags
        (outline-previous-visible-heading 1)  ;; jump to heading
        (org-set-tags-command)
        )
      )
    )
  )

若我存储 URL 时没有输入描述,则结果可能像下面这样:

,* NEXT Bookmark http://Karl-Voit.at 
[2014-08-10 Sun 16:24]

为了給链接加上对应网页上的标题,我在网上找到一段代码并改造了它以使之符合我的需求:

;; ######################################################
;; replaces URL with Org-mode link including description
;; see id:2014-03-09-inbox-to-bookmarks
(defun my-www-get-page-title (url)
  "retrieve title of web page.
from: http://www.opensubscriber.com/message/help-gnu-emacs @gnu.org/14332449.html"
  (let ((title))
    (with-current-buffer (url-retrieve-synchronously url)
      (goto-char (point-min))
      (re-search-forward "" nil t 1)
      (setq title (match-string 1))
      (goto-char (point-min))
      (re-search-forward "charset=\\([-0-9a-zA-Z]*\\)" nil t 1)
      (decode-coding-string title (intern (match-string 1)))))
  )

(defun my-url-linkify ()
  "Make URL at cursor point into an Org-mode link.
If there's a text selection, use the text selection as input.

Example: http://example.com/xyz.htm 
becomes
\[\[ http://example.com/xyz.htm \]\[Source example.com\]\]

Adapted code from: http://ergoemacs.org/emacs/elisp_html-linkify.html"
  (interactive)
  (let (resultLinkStr bds p1 p2 domainName)
    ;; get the boundary of URL or text selection
    (if (region-active-p)
        (setq bds (cons (region-beginning) (region-end)) )
      (setq bds (bounds-of-thing-at-point 'url))
      )
    ;; set URL
    (setq p1 (car bds))
    (setq p2 (cdr bds))
    (let (
          (url (buffer-substring-no-properties p1 p2))
          )
      ;; retrieve title
      (let ((title (my-www-get-page-title url)))
        (message (concat "title is: " title))
        ;;(setq url (replace-regexp-in-string "&" "&" url))
        (let ((resultLinkStr (concat "[" title "](" url ")")))
          ;; delete url and insert the link
          (delete-region p1 p2)
          (insert resultLinkStr)
          )
        )
      )
    )
  )

很不幸,这段代码并不时常有效. 大多数时候我会得到一个报错信息 save-current-buffer: Invalid coding system: UTF-8 ,我也看不懂这个错误是什么意思. 若你知道如何修复这个问题,请给我留言。

注意:想看我当前版本的 Elis 函数,请参见: https://github.com/novoid/dot-emacs

未来的计划: (再次)社交化

目前我对该方案还蛮满意的。

未来,我想通过博客软件 lazyblorg 来将选中的书签整合进我的博客中去,而所需要的步骤应该仅仅是为书签添加 blog 标签。

(像书签)这样简短的条目应该能够自动被加上 smallbookmark 标签,我会将这些条目发布成一个 Atom feed,这样你也能看到这些书签了,通过这种方式,我将又一次具有社交分享功能,只不过这一次该功能完全由我自己掌控。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

半寸时光

暂无简介

0 文章
0 评论
25 人气
更多

推荐作者

lixs

文章 0 评论 0

敷衍 

文章 0 评论 0

盗梦空间

文章 0 评论 0

tian

文章 0 评论 0

13375331123

文章 0 评论 0

你对谁都笑

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文