对过程不感兴趣的小伙伴请直接戳这里看使用方法
0. 剧情
某小伙伴平常用 Safari ,收藏了很多网页在“阅读列表”里,现在换成了 Chrome ,想要把“阅读列表”里的收藏作为书签导入到 Chrome 里,但是 Safari 没有提供这个功能,搜索了一番也没找的解决方案。所以作为程序猿,本着“自给自足丰衣足食”的精神,没有的功能咱就自己写。
1. 列需求
- 找到 Safari 的“阅读列表”的 URL 和标题
- 将 URL 和 标题导入到 Chrome
2. 探索一番
刚看到这个需求的时候,我搜索了一下“阅读列表”的作用:将网页内容保存到本地供离线使用。显然,找到保存网页离线数据的文件夹或者数据库就有可能找到 URL 列表,在/
目录下搜索包含safari
的文件夹,结果并没有什么帮助。
然后换个思路,能不能找到 Safari 的历史记录文件。用safari 历史记录 路径
做为关键字搜索,找到了这个:Safari 的历史记录文件保存在/Users/你的用户名/Library/Caches/Metadata/Safari/History
目录下,扫了一眼也没有什么帮助。但是发现在/Users/你的用户名/Library
目录下有个safari
文件夹,打开一看,很有帮助。
我们发现了什么?ReadingListArchives
,保存“阅读列表”离线文件的目录,打开一看,目录下文件夹数量刚好和我的“阅读列表”里网页数量相同,然而目录名是一堆 UUID ,并不是 URL ,文件夹里的文件也只有网页的离线文件。
我想这些 UUID 一定是和 URL 有对应关系,于是又看了看 Safari 目录下的文件和文件夹,锁定这几个数据库文件和 plist 文件。首先用 SQLite 工具浏览几个数据库文件,还是没有帮助。再看 plist 文件。首先用Xcode打开Bookmarks.plist
文件,展开Children
,和那几个Item
,发现了有用的东西:WebBookmarkTypeProxy
、BookmarksBar
、BookmarksMenu
和com.apple.ReadingList
。
展开com.apple.ReadingList
所在的那级Item 3
-> Children
,点开几个Item
我发现了我想要的东西:“阅读列表”的 URL 和 标题。
其实到这里已经解决最难的问题了,剩下的是导入到 Chrome 的书签里。本身 Chrome 提供了书签导入/导出功能,我们先导出一份书签看看。
结构一目了然,先写个例子。
然后导入测试一下。
成功导入,完成了需求。
3. 上代码
有了上面的探索,代码部分就简单多了。我本是 iOS 堆码猿,考虑到用 ObjectiveC 写还要发个工程压缩包给小伙伴,不如脚本来的轻快,于是我就用 Python3 来写。
我的环境:
- Mac 10.11.6
- Safari 10.0.1(11602.2.14.0.7)
- Chrome 57.0.2987.133 (64-bit)
- Python 3.6.1
用法:
- 把 plist 文件复制到一个文件夹
- 打开终端,cd 到那个文件夹
- 在终端输入
python3 safari2chrome.py
来运行脚本 - 脚本运行完会在当前目录生成个 html 文件,导入到 Chrome 齐活
代码如下:
from plistlib import *
# read plist file and get URLs and titles
with open('./Bookmarks.plist', 'rb') as fp:
plist = load(fp)
urlArray = plist['Children'][3]['Children']
marks = []
for dict in urlArray:
marks.append((dict['URLString'], dict['URIDictionary']['title']))
# create Chrome bookmark html file
fileHeader = '''<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
'''
rowHeader = '<DL><p>\n'
rowFooter = '</DL><p>'
bookmarkFolderLine = '<DT><H3 ADD_DATE="" LAST_MODIFIED="">Safari Web Bookmark</H3>\n'
f = open('chrome-bookmark.html', 'w')
f.write(fileHeader)
f.write(rowHeader)
f.write(bookmarkFolderLine)
f.write(rowHeader)
for target in marks:
str = u'<DT><A HREF="' + target[0] + u'" ADD_DATE="" ICON="">' + target[1] + u'</A>' + '\n'
f.write(str)
f.write(rowFooter)
f.write(rowFooter)
4. 改进
改进的地方有几个吧,比如全程自动导出导入,弄个小小的 GUI ,支持更多版本的 Mac / Safari / Chrome / Python 。小伙伴已经用上面的脚本完成导入,后续我会继续完善这个小工具,让它更简单更好用吧。
已经上传到 Github :https://github.com/GintokiS/Safari2Chrome