Eudic → Maimemo 自动同步工具:欧路词典 & 墨墨背单词

大家好,今天介绍一款实用工具。

长期以来,笔者的英文查词流程是:

查单词用欧路词典(Eudic),记忆单词用墨墨背单词(Maimemo)。

但这带来一个很现实的长期问题 --------- 欧路与墨墨之间并没有官方的词库同步功能。

桌面端每天查文献生词、收藏、加入词本等动作都在欧路里完成,可真正背词又是在移动端墨墨里进行,两个平台非常割裂。

为了让整个流程真正自动化、可托管,也为了避免笔者每隔一段时间就要"整理一次词库"这种重复劳动,遂开发本工具。

也希望这套方案能帮助到同样使用欧路 + 墨墨组合的朋友们,让单词管理真正做到"一次查词,多端同步"。

🧩 功能概述

本工具支持:

  1. 自动抓取欧路全部生词本
  2. 自动去重、过滤中文
  3. 写入本地文本文件备份
  4. 调用墨墨 API 更新指定云词本
  5. 推送同步结果通知
  6. 定时自动执行

🪜 功能配置

(1)获取欧路词典 API Token

获取授权:https://my.eudic.net/OpenAPI/Authorization

登录 → 设置 → API → 复制 NISxxxx 开头的 Token

(2)获取墨墨背单词 API Token

参考文档:https://open.maimemo.com/document

墨墨移动端 → 我的 → 更多设置 → 实验功能 → 开放 API → 复制 Token

(3)获取墨墨 "云词本 ID"

使用墨墨查询云词本 API 获取云词本 ID

注意不是网页 URL 显示的编号,需要手动从开发者工具或 API 查看实际 ID

很多人搞错这个步骤,此处必须使用真实 notepad ID

(4)创建飞书机器人 Webhook

飞书桌面端 → 新建群组 → 群设置 → 机器人 → 添加 Webhook

并复制 Webhook URL

(5)服务器准备

  • Python 环境
  • 计划任务设置

🧩 关键代码拆解

以下是本项目的主要功能模块

① 安全 GET 请求

欧路分页有时候从 1 开始,为避免请求异常,必须支持自动检测

python 复制代码
def safe_get(url, headers=HEADERS, timeout=10, retries=2, backoff=1.0):
    for i in range(retries + 1):
        try:
            r = requests.get(url, headers=headers, timeout=timeout)
            return r
        except Exception as e:
            if i < retries:
                time.sleep(backoff * (i + 1))
            else:
                raise

② 获取欧路全部生词本

python 复制代码
def get_books():
    """获取生词本列表"""
    r = safe_get(CATS_URL)
    if r.status_code != 200:
        raise RuntimeError(f"获取生词本失败: status={r.status_code} body={r.text[:200]}")
    return r.json().get("data", [])

③ 自动判断分页起始位置

欧路 API 的坑,有些生词本 page=0,另一些 page=1

所以必须自动探测:

python 复制代码
def detect_page_start(category_id, page_size=500):
    url0 = f"{BASE}?language={EUDIC_LANGUAGE}&category_id={category_id}&page=0&page_size={page_size}"
    url1 = f"{BASE}?language={EUDIC_LANGUAGE}&category_id={category_id}&page=1&page_size={page_size}"
    r0, r1 = safe_get(url0), safe_get(url1)
    try:
        ok0 = bool(r0.json().get("data") if r0.status_code == 200 else None)
    except Exception:
        ok0 = False
    try:
        ok1 = bool(r1.json().get("data") if r1.status_code == 200 else None)
    except Exception:
        ok1 = False
    if ok0: return 0
    if ok1: return 1
    return 1

④ 抓取全部单词并去重

python 复制代码
def get_all_words_for_category(category_id, page_size=500):
    start_page = detect_page_start(category_id, page_size)
    page = start_page
    all_items = []
    while True:
        url = f"{BASE}?language={EUDIC_LANGUAGE}&category_id={category_id}&page={page}&page_size={page_size}"
        r = safe_get(url)
        if r.status_code != 200:
            raise RuntimeError(f"请求失败: {url} 状态码 {r.status_code} 返回:{r.text[:200]}")
        try:
            data = r.json().get("data", [])
        except Exception as e:
            raise RuntimeError(f"解析 JSON 出错: {e} body={r.text[:200]}")
        if not data:
            break
        all_items.extend(data)
        if len(data) < page_size:
            break
        page += 1
    # 去重
    seen = OrderedDict()
    for it in all_items:
        w = it.get("word")
        if w and w not in seen:
            seen[w] = None
    return list(seen.keys())

⑤ 写入 TXT 文件

建议使用绝对路径避免文件生成在奇怪的目录

格式为一行一个单词,供备份和更新墨墨云词本使用

python 复制代码
def write_words_to_txt(words, path):
    """将英文单词写入 TXT"""
    with open(path, "w", encoding="utf-8") as f:
        for w in words:
            f.write(w + "\n")

⑥ 更新墨墨云词本

注意请求头必须加:Authorization: Bearer your_token,否则返回无权限

python 复制代码
def update_memo_notepad(content):
    """日志行为"""
    url = f"https://open.maimemo.com/open/api/v1/notepads/{MEMO_NOTEPAD_ID}"

    payload = {
        "notepad": {
            "status": "UNPUBLISHED",
            "content": content,
            "title": "欧路词典生词本",
            "brief": "欧路词典生词,API 自动每月抓取后导入",
            "tags": ["词典"]
        }
    }

    headers = {
        "Authorization": f"Bearer {MEMO_API_TOKEN}",
        "Accept": "application/json",
        "Content-Type": "application/json"
    }

    print("\n开始提交到墨墨云词本...")

    try:
        r = requests.post(url, json=payload, headers=headers, timeout=15)
        if r.status_code in (200, 201):
            print("✅ 墨墨云词本更新成功")
        else:
            print(f"❌ 墨墨提交失败: status={r.status_code}")
            print("返回内容:", r.text[:500])
    except Exception as e:
        print("❌ 墨墨提交异常:", e)

⑦ 飞书通知

同步完成后可选发送飞书消息提醒功能

python 复制代码
def send_feishu_notification(webhook_url, title, message):
    card_content = f"{message}"

    payload = {
        "msg_type": "interactive",
        "card": {
            "config": {"wide_screen_mode": True},
            "header": {"title": {"tag": "plain_text", "content": title}},
            "elements": [
                {
                    "tag": "div",
                    "text": {
                        "tag": "lark_md",
                        "content": card_content
                    }
                }
            ]
        }
    }

    try:
        resp = requests.post(webhook_url, json=payload, timeout=10)
        if resp.status_code == 200:
            print("✅ 飞书通知发送成功")
        else:
            print(f"❌ 飞书通知失败,状态码: {resp.status_code}, 响应: {resp.text}")
    except Exception as e:
        print(f"❌ 飞书通知异常: {e}")

🧩 主流程

抓取 → 过滤 → 写入 → 墨墨更新 → 飞书通知

如果在欧路中打开"查词后自动加入生词本",有时查询中文翻译也会直接加入,故进行过滤,只保留英文单词供同步

python 复制代码
def run_sync():
    print("抓取生词本...")
    books = get_books()
    if not books:
        print("未获取到生词本,请检查 API_TOKEN 或网络。")
        return

    print(f"找到 {len(books)} 个生词本:")
    for b in books:
        print(" -", b.get("name"))

    all_words = OrderedDict()
    for b in books:
        name = b.get("name")
        cid = b.get("id")
        print(f"\n抓取生词本: {name} (id={cid})")
        words = get_all_words_for_category(cid)
        print(f"  => {len(words)} 条")
        for w in words:
            all_words[w] = None

    # 过滤只保留英文单词
    english_words = [w for w in all_words.keys() if EN_WORD_RE.match(w)]
    print(f"\n抓取完成,总单词数(去重 + 英文过滤后): {len(english_words)}")

    # 写入 txt
    try:
        txt_path = TXT_OUTPUT_PATH
        write_words_to_txt(english_words, txt_path)
        print("已将所有英文单词写入 txt_path,每行一个单词。")

        try:
            with open(txt_path, "r", encoding="utf-8") as f:
                memo_text = f.read().strip()
            update_memo_notepad(memo_text)
        except Exception as e:
            print("读取 txt_path 失败:", e)

    except Exception as e:
        print("写文件失败:", e)

    # 发送飞书通知
    send_feishu_notification(
        FEISHU_WEBHOOK,
        title="🎉 Eudic 欧路生词本已同步到墨墨背单词",
        message=f"总计抓取 {len(english_words)} 个有效单词,请及时规划学习!"
    )

🧩 实现效果

源码获取方式

开源地址:https://github.com/pdpeng/eudic-maimomo-words-sync

公粽浩:攻城狮杰森,后台回复"墨墨"

相关推荐
nil1 小时前
shortcutkey:跨平台快捷键管理工具的设计与实现
python·开源·github
Vince的修炼之路1 小时前
用Python将JSON格式文件数据导入到Elasticsearch上
python
不会吉他的肌肉男不是好的挨踢男1 小时前
LLaMA Factory 训练模型未检测到CUDA环境解决
python·ai·llama
Justinyh2 小时前
Notion同步到CSDN + 构建Obsidian本地博客系统指南
python·csdn·图床·notion·obsidian·文档同步·piclist
D***y2012 小时前
【Python】网络爬虫——词云wordcloud详细教程,爬取豆瓣最新评论并生成各式词云
爬虫·python·信息可视化
后台开发者Ethan2 小时前
py文件被初始化执行了2次
python
a3158238062 小时前
Linux部署Python Django工程和Node工程,使用宝塔面板
linux·服务器·python·django·node·strapi·宝塔面板
B站计算机毕业设计之家2 小时前
机器学习:python智能电商推荐平台 大数据 spark(Django后端+Vue3前端+协同过滤 毕业设计/实战 源码)✅
大数据·python·spark·django·推荐算法·电商
豪哥大爷2 小时前
Python datetime模块全面指南
python