对过程不感兴趣的小伙伴请直接戳这里看使用方法
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文件夹,打开一看,很有帮助。
很有帮助的图1
我们发现了什么?ReadingListArchives,保存“阅读列表”离线文件的目录,打开一看,目录下文件夹数量刚好和我的“阅读列表”里网页数量相同,然而目录名是一堆 UUID ,并不是 URL ,文件夹里的文件也只有网页的离线文件。
ReadingListArchives目录下内容
我想这些 UUID 一定是和 URL 有对应关系,于是又看了看 Safari 目录下的文件和文件夹,锁定这几个数据库文件和 plist 文件。首先用 SQLite 工具浏览几个数据库文件,还是没有帮助。再看 plist 文件。首先用Xcode打开Bookmarks.plist文件,展开Children,和那几个Item,发现了有用的东西:WebBookmarkTypeProxy、BookmarksBar、BookmarksMenu和com.apple.ReadingList。
Bookmarks.plist
展开com.apple.ReadingList所在的那级Item 3 -> Children,点开几个Item我发现了我想要的东西:“阅读列表”的 URL 和 标题。
URLString和title就是我们想要的
其实到这里已经解决最难的问题了,剩下的是导入到 Chrome 的书签里。本身 Chrome 提供了书签导入/导出功能,我们先导出一份书签看看。
导出的书签 html 文件
结构一目了然,先写个例子。
简单的🌰
然后导入测试一下。
测试结果
成功导入,完成了需求。
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 = '''
BookmarksBookmarks
'''
rowHeader = '
\n'
rowFooter = '
'
bookmarkFolderLine = '
Safari Web Bookmark
\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'
' + target[1] + u'' + '\n'f.write(str)
f.write(rowFooter)
f.write(rowFooter)
4. 改进
改进的地方有几个吧,比如全程自动导出导入,弄个小小的 GUI ,支持更多版本的 Mac / Safari / Chrome / Python 。小伙伴已经用上面的脚本完成导入,后续我会继续完善这个小工具,让它更简单更好用吧。
# EOF