Python实搞:导出浏览器书签并发送至邮箱

一、为什么要搞

工作中,会在工作电脑上的Chrome、Edge、Firefox浏览器中保存大量的书签。那么问题来了,比如Chrome浏览器并未登录账号,书签只保存在本地,当不在公司时就比较难搞了,拿不到链接还搞个毛线啊。

所以在平常工作都会抽空将Chrome、Edge浏览器中的书签导出,再导入到Firefox的书签中(Firefox浏览器登录了账号)。可这不就是重复性的工作吗?

二、准备如何搞

原本有个方案是使用Python的selenium库操作浏览器实现书签自动导出导入,可是这个方案会存在以下的问题:

1.各浏览器版本会不定时更新,Python脚本中的浏览器driver驱动也需要相应地更新

2.selenium操作浏览器需要对页面的各种元素定位并发送相应的指令,要对三种浏览器分别调试,费时费力

3.操作浏览器时,很可能会有一些随机弹窗出现,这使得不确定性提高,从而降低脚本的稳定性

于是乎,方案变更为:使用Python自动导出浏览器的书签并发送至邮箱

那么先来做好准备工作:

1.找到各浏览器的书签保存位置

Chrome和Edge的书签保存在本地的Bookmarks中,是个JSON文件

Python实搞:导出浏览器书签并发送至邮箱_Python

我本机的Chrome和Edge书签路径如下:

bookmarks_path_chrome = r'C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default'
bookmarks_path_edge = r'C:\Users\Administrator\AppData\Local\Microsoft\Edge\User Data\Default'

Firefox的书签保存在places.sqlite中,是个sqlite数据库

Python实搞:导出浏览器书签并发送至邮箱_自动化_02

我本机的Firefox书签路径如下:

bookmarks_path_firefox = r'C:\Users\Administrator\AppData\Roaming\Mozilla\Firefox\Profiles\iw3bxbjx.default-1659145063858'

2.准备要使用到的Python第三方库

导出的书签文件保存为excel文件,使用tablib库

发送邮件使用zmail库

pip install tablib
pip install zmail

3.开启邮箱的POP3/SMTP服务并获取授权码

设置后才能正常在授权设备上登录邮箱

我的163邮箱设置如下图所示,不同邮箱设置界面会有差异

Python实搞:导出浏览器书签并发送至邮箱_浏览器书签_03

三、说搞咱就搞

Edge、Chrome书签导出

Chrome和Edge的书签存储方式类似,只需要读取Bookmarks文件即可

下面以Edge浏览器为例:

import os.path


class EdgeChromeExtract:
    """
    导出Edge和Chrome浏览器的书签
    """

    def __init__(self, bookmarks_path):
        self.file_path = bookmarks_path + os.sep + 'Bookmarks'

    def read(self):
        """
        读取书签文件
        """
        if os.path.exists(self.file_path):
            with open(self.file_path, 'r+', encoding='utf-8') as f:
                return f.read()


if __name__ == "__main__":
    bookmarks_path_edge = r'C:\Users\Administrator\AppData\Local\Microsoft\Edge\User Data\Default'
    bm_e = EdgeChromeExtract(bookmarks_path_edge)
    print(bm_e.read())

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_浏览器书签_04

倒是读取成功了,会发现书签名name中包含了“+x,”等不可打印字符,不可忍受,得处理一下

在EdgeChromeExtract类中添加一个to_json方法:

def to_json(self):
    def gen():
        for s in self.read():
            if s.isprintable():
                yield s

    return json.loads(''.join(gen()))
print(bm_e.to_json())

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_Python_05

简化一下,在read方法中操作一手可得到相同结果

def read(self):
    """
    读取书签文件
    """
    if os.path.exists(self.file_path):
        with open(self.file_path, 'r+', encoding='utf-8') as f:
            # return f.read()
            return json.loads(''.join(s for s in f.read() if s.isprintable()))

接下来就是解析json,提取出书签名称和链接

def extract(self):
    """
    导出书签名称和地址
    """
    json_data = self.read()
    results_list = []
    for t in ['bookmark_bar', 'other']:
        for i in json_data['roots'][t]['children']:
            if 'children' in i.keys():  # 包含children
                for j in i['children']:
                    results_list.append({'name': j['name'], 'url': j['url']})
            else:  # 不包含children
                results_list.append({'name': i['name'], 'url': i['url']})
    return results_list
if __name__ == "__main__":
    bookmarks_path_edge = r'C:\Users\Administrator\AppData\Local\Microsoft\Edge\User Data\Default'
    bm_e = EdgeChromeExtract(bookmarks_path_edge)
    print(bm_e.extract())

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_Python_06

为了适配Chrome书签的JSON格式,添加了一个递归处理JSON的方法

def recursion_json(self, data):
    """
    递归处理json提取书签名和地址
    """
    results = []
    for i in data:
        if 'name' in i and 'url' in i:  # 若name和url键均存在则提取
            results.append({'name': i['name'], 'url': i['url']})
        if 'children' in i:  # 如果children键存在则递归
            results.extend(self.recursion_json(i['children']))  # 使用extend函数扩展结果列表
    return results

在extract方法中调用

def extract(self):
    """
    导出书签名称和地址
    """
    json_data = self.read()
    results_list = []
    for t in ['bookmark_bar', 'other']:
        # for i in json_data['roots'][t]['children']:
        #     if 'children' in i.keys():  # 包含children
        #         for j in i['children']:
        #             results_list.append({'name': j['name'], 'url': j['url']})
        #     else:  # 不包含children
        #         results_list.append({'name': i['name'], 'url': i['url']})
        results_list.extend(self.recursion_json(json_data['roots'][t]['children']))
    return results_list

来试下导出Chrome的书签

if __name__ == "__main__":
    bookmarks_path_chrome = r'C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default'
    bm_c = EdgeChromeExtract(bookmarks_path_chrome).extract()
    print(bm_c)

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_浏览器书签_07

Firefox书签导出

先看看sqlite数据库的结构,书签的名称和链接分别放在moz_bookmarks和moz_places两个表中

Python实搞:导出浏览器书签并发送至邮箱_浏览器书签_08

Python实搞:导出浏览器书签并发送至邮箱_自动化_09

编写好查询SQL,来连接数据库获取查询结果

import os.path
import sqlite3
import traceback


class FirefoxExtract:
    """
    导出Firefox浏览器的书签
    """

    def __init__(self, bookmarks_path):
        self.file_path = bookmarks_path + os.sep + 'places.sqlite'

        self.conn = sqlite3.connect(self.file_path)  # 连接数据库
        self.cur = self.conn.cursor()  # 数据库游标

    def extract(self,
                sql="select b1.title,p.url from moz_bookmarks b1 left join moz_places p on b1.fk=p.id left join "
                    "moz_bookmarks b2 on b1.parent=b2.id where b1.type=1 and b2.type=2 and b2.title !='Mozilla "
                    "Firefox' order by b2.title,b1.position desc;"):
        try:
            self.cur.execute(sql)  # 执行查询sql
            results = self.cur.fetchall()  # 获取查询结果
            return results
        except Exception as err:
            info = f"出了点小问题!\n{repr(err)}\n{traceback.format_exc()}"
            print(info)
            self.conn.rollback()  # 回滚
        finally:
            self.conn.close()  # 关闭连接


if __name__ == "__main__":
    bookmarks_path_firefox = r'C:\Users\Administrator\AppData\Roaming\Mozilla\Firefox\Profiles\iw3bxbjx.default-1659145063858'
    bm_f = FirefoxExtract(bookmarks_path_firefox).extract()
    print(bm_f)

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_Python_10

啊!结果又包含了这种不可打印字符,还要处理一手

优化一下获取的查询结果

self.cur.execute(sql)  # 执行查询sql
results = self.cur.fetchall()  # 获取查询结果
# return results
results_list = [{'name': ''.join(j for j in i[0] if j[0].isprintable()), 'url': i[1]} for i in results]
return results_list

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_浏览器书签_11

书签保存为excel

使用tablib库,将导出的书签保存为bookmarks.xlsx

import tablib


def bookmarks_2_excel(bookmarks, file_name='bookmarks.xlsx'):
    """
    书签导出为excel
    """
    data = tablib.Dataset()  # 创建tablib数据集
    data.headers = ['名称', '地址']  # 表头
    for bm in bookmarks:  # 添加行数据
        data.append([bm['name'], bm['url']])
    with open(file_name, 'wb') as f:  # 导出为excel
        f.write(data.export('xlsx'))
if __name__ == "__main__":
    bookmarks_path_chrome = r'C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default'
    bookmarks_path_edge = r'C:\Users\Administrator\AppData\Local\Microsoft\Edge\User Data\Default'
    bookmarks_path_firefox = r'C:\Users\Administrator\AppData\Roaming\Mozilla\Firefox\Profiles\iw3bxbjx.default-1659145063858'
    bm_e = EdgeChromeExtract(bookmarks_path_edge).extract()
    bm_c = EdgeChromeExtract(bookmarks_path_chrome).extract()
    bm_f = FirefoxExtract(bookmarks_path_firefox).extract()
    bookmarks_list = bm_e + bm_c + bm_f
    bookmarks_2_excel(bookmarks_list)

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_自动化_12

发送书签excel邮件

通过zmail建立邮箱连接,构造邮件内容,发送邮件

import datetime
import os.path
import traceback
import zmail


def send_email(file_name='bookmarks.xlsx', sender='xxxx@163.com', receiver='xxxx@163.com'):
    """
    书签excel发送到邮箱
    """
    try:
        file_path = os.path.dirname(os.path.abspath(__file__)) + os.sep + file_name  # 书签文件路径
        rq = datetime.date.today()  # 今天的日期
        server = zmail.server(sender, 'L***************V')  # 发件人邮箱地址和授权码
        sign = '\n\n\n--\nDriver_Won\n1*********4\nHave a nice day (^_^)'  # 邮件签名
        mail_content = {
            'from': f'Driver_Won<{sender}>',  # 发件人及昵称
            'subject': f'书签汇总{rq}',  # 主题
            'content_text': '本邮件由系统自动发出,无需回复!' + sign,  # 文本内容
            'attachments': file_path  # 文件附件
        }
        server.send_mail(receiver, mail_content)  # 收件人地址和邮件内容
    except Exception as e:
        info = f"邮件发送失败!\n{repr(e)}\n{traceback.format_exc()}"
        print(info)
    else:
        print('邮件发送成功!')


if __name__ == "__main__":
    send_email()

运行结果:

Python实搞:导出浏览器书签并发送至邮箱_Python_13

四、搞完就收工

本机是windows电脑,将Python脚本添加到windows的计划任务中,定时自动执行

有效代替了手动导入导出书签的工作,随时随地手机上也能查看到保存在各浏览器上的书签了


上一篇:Qt生成coredump文件(支持arm和x86架构)


下一篇:MySQL主从复制