利用diagrams渲染mermaid为xml文件

一、背景

        利用chatllama的方法进行数据增广,接入chatgpt 4o得到大量创建图表的mermaid代码。投喂大模型第一步就是将mermaid代码渲染成xml,为后续再将xml渲染成jpg提供铺垫。

mermaid介绍

        Mermaid 是一种基于 JavaScript 的开源图表生成工具,使用一种简单的 Markdown 风格的语法,创建各种类型的图表和图形,包括流程图、序列图、甘特图、类图等。

得到的JSON文档中包含mermaid代码和markdown代码

二、方法

        在 draw.io 网站上自动插入并导出 Mermaid 图表,从json中关键字“id”中的内容作为文件名保存xml文件。

代码功能概述

  1. validate_mermaid_code:清洗并验证 Mermaid 代码。
def validate_mermaid_code(text):
    # 清洗并验证Mermaid代码
    if text.startswith('```mermaid') and text.endswith('```'):
        text = text[11:-3].strip()
    return text
  1. click_menu_option:模拟点击 draw.io 菜单选项。
def click_menu_option(page, menu_text, option_text):
    for ele in page.ele('tag:div@class=geMenubar').eles('tag:a@class=geItem'):
        if ele.text == menu_text:
            ele.click()
            break

    for ele in page.eles('tag:td@class=mxPopupMenuItem'):
        if ele.text == option_text:
            ele.click()
            break
  1. drawio:将 Mermaid 代码插入到 draw.io 中,并导出为 XML 文件。
def drawio(page, text, file_name, error_dir):
    try:
        click_menu_option(page, '调整图形', '插入')
        click_menu_option(page, '插入', '高级')
        click_menu_option(page, '高级', 'Mermaid...')
        textarea = page.ele(
            'tag:textarea@style=box-sizing: border-box; resize: none; width: 100%; height: 354px; margin-bottom: 16px;')            #找到重置tag的属性
        textarea.clear()
        textarea.input(text)          

        for ele in page.eles('tag:button@class=geBtn gePrimaryBtn'):
            if ele.text == '插入':
                ele.click()

        if page.ele('tag:div@title=出错'):
            error_message = page.ele('tag:div@title=出错').text
            print(f"Error detected: {error_message}")
            page.ele('tag:button@class=geBtn').click()
            raise Exception("Error detected in Mermaid code insertion.")

        click_menu_option(page, '文件', '导出为')
        click_menu_option(page, '导出为', 'XML...')

        for ele in page.eles('tag:button@class=geBtn gePrimaryBtn'):
            if ele.text == '导出':
                ele.click()

        ele = page.ele('tag:input@@type=text@@style=box-sizing: border-box; width: 100%;')
        if ele.value == "未命名绘图.drawio.xml":
            ele.clear()
            ele.input(file_name)

        page.ele('tag:select@style=text-overflow: ellipsis; grid-column: 1;').select("设备")
        ele = page.ele('tag:div@style=flex-basis: 100%; text-align: right; margin-top: 16px;').ele(
            'tag:button@class=geBtn gePrimaryBtn')
        if ele.text == "保存":
            ele.click()

        click_menu_option(page, '文件', '关闭')

        ele = page.ele('tag:div@style=text-align: center; white-space: nowrap; margin-top: 12px;').ele(
            'tag:button@class=geBtn')
        if ele.text == "放弃更改":
            ele.click()

        try:
            page.ele('稍后再决定').click()
        except:
            pass
    except Exception as e:
        print(f"Error encountered: {e}")
        os.makedirs(error_dir, exist_ok=True)
        with open(os.path.join(error_dir, file_name.replace('.xml', '.txt')), 'w', encoding='utf-8') as f:
            f.write(text)
        raise e
  • 使用 click_menu_option 依次点击菜单,直到打开 Mermaid 插入窗口。
  • 清空文本区域并插入新的 Mermaid 代码。
  • 检查是否有错误信息,如果有则处理错误。
  • 依次点击菜单,直到导出 XML 文件。
  • 处理导出文件的命名和保存。
  • 如果遇到异常,创建错误文件夹并将出错的 Mermaid 代码保存到文件中。
  1. run:读取 JSON 文件中的 Mermaid 代码,并循环处理这些代码,将其插入到 draw.io 并导出。
def run(url, file_list, start_idx=0):
    page = WebPage()
    page.get(url)
    try:
        page.ele('稍后再决定').click()
    except:
        pass
    for cnt, text in tqdm(enumerate(file_list[start_idx:]), total=6500):
        try:
            file_name = f"{text['id'].replace('#', '_')}.xml"
            validated_text = validate_mermaid_code(text['answer_mode4'])
            drawio(page, validated_text, file_name, error_dir='./error_output')

            os.makedirs("./output", exist_ok=True)
            with open(f"./output/{text['id'].replace('#', '_')}.txt", 'w', encoding='utf-8') as f:
                f.write(text['answer_mode4'])
        except Exception as e:
            print(f"Failed | {e}")
            continue

这里是最麻烦的,网页卡住不动,程序就死掉了。需要设想好各种情况,如果出错或者网页崩溃,能够做到自更新。 

main函数

if __name__ == '__main__':
    url = "https://app.diagrams.net/"
    data = []
    with open(r'g1149_syn_mermaid_v2.json', 'r', encoding='utf-8') as f:
        for line in f.readlines():
            rec = json.loads(line.strip())
            if 'answer_mode4' in rec:
                text = rec['answer_mode4']
                data.append({"id": rec['id'], "answer_mode4": text})

    run(url, data, start_idx=0)  #设置start_idx方便调试

三、实际问题

1、需要科学上网才能跑,如果科学上网工具不行,网络波动大,那这个是跑不了的。

2、页面容易崩溃,崩溃后需要手动重启,这是最抓马的。大概4h崩溃一次。所以添加调试功能非常重要。

3、生成流程图的时候很快,但是在保存xml的时候就很慢很慢,我搜索了很多资料,也问了老师,老师说是页面渲染需要时间,无法解决。但是我知道肯定不是这样的原因,如果有大佬能优化这个,球球告知分享。

4、开始时,窗口要最大化,不然第一条的属性栏会莫名其貌的被隐藏。导致程序无法正常运行。

四、完整代码

# -*- coding: utf-8 -*-
import os
import json
from DrissionPage import WebPage
from tqdm import tqdm



def validate_mermaid_code(text):
    # 清洗并验证Mermaid代码
    if text.startswith('```mermaid') and text.endswith('```'):
        text = text[11:-3].strip()
    return text


def click_menu_option(page, menu_text, option_text):
    for ele in page.ele('tag:div@class=geMenubar').eles('tag:a@class=geItem'):
        if ele.text == menu_text:
            ele.click()
            break

    for ele in page.eles('tag:td@class=mxPopupMenuItem'):
        if ele.text == option_text:
            ele.click()
            break


def drawio(page, text, file_name, error_dir):
    try:
        print("Attempting to insert and export Mermaid code.")
        click_menu_option(page, '调整图形', '插入')
        click_menu_option(page, '插入', '高级')
        click_menu_option(page, '高级', 'Mermaid...')

        textarea = page.ele(
            'tag:textarea@style=box-sizing: border-box; resize: none; width: 100%; height: 354px; margin-bottom: 16px;')
        textarea.clear()
        textarea.input(text)

        for ele in page.eles('tag:button@class=geBtn gePrimaryBtn'):
            if ele.text == '插入':
                ele.click()

        # 检查是否有错误信息
        if page.ele('tag:div@title=出错'):
            error_message = page.ele('tag:div@title=出错').text
            print(f"Error detected: {error_message}")
            # 点击确认按钮
            page.ele('tag:button@class=geBtn').click()
            raise Exception("Error detected in Mermaid code insertion.")

        click_menu_option(page, '文件', '导出为')
        click_menu_option(page, '导出为', 'XML...')

        for ele in page.eles('tag:button@class=geBtn gePrimaryBtn'):
            if ele.text == '导出':
                ele.click()

        ele = page.ele('tag:input@@type=text@@style=box-sizing: border-box; width: 100%;')
        if ele.value == "未命名绘图.drawio.xml":
            ele.clear()
            ele.input(file_name)

        page.ele('tag:select@style=text-overflow: ellipsis; grid-column: 1;').select("设备")
        ele = page.ele('tag:div@style=flex-basis: 100%; text-align: right; margin-top: 16px;').ele(
            'tag:button@class=geBtn gePrimaryBtn')
        if ele.text == "保存":
            ele.click()

        click_menu_option(page, '文件', '关闭')

        ele = page.ele('tag:div@style=text-align: center; white-space: nowrap; margin-top: 12px;').ele(
            'tag:button@class=geBtn')
        if ele.text == "放弃更改":
            ele.click()

        try:
            page.ele('稍后再决定').click()
        except:
            pass
    except Exception as e:
        print(f"Error encountered: {e}")
        # 将出错文件复制到新文件夹
        os.makedirs(error_dir, exist_ok=True)
        with open(os.path.join(error_dir, file_name.replace('.xml', '.txt')), 'w', encoding='utf-8') as f:
            f.write(text)
        raise e

def run(url, file_list, start_idx=0):
    page = WebPage()
    page.get(url)
    try:
        page.ele('稍后再决定').click()
    except:
        pass
    for cnt, text in tqdm(enumerate(file_list[start_idx:]),total=6500):
        try:
            # 使用文件名中的ID命名文件
            file_name = f"{text['id'].replace('#', '_')}.xml"
            validated_text = validate_mermaid_code(text['answer_mode4'])
            drawio(page, validated_text, file_name, error_dir='./error_output')

            # 写入TXT文件
            os.makedirs("./output", exist_ok=True)
            with open(f"./output/{text['id'].replace('#', '_')}.txt", 'w', encoding='utf-8') as f:
                f.write(text['answer_mode4'])
        except Exception as e:
            print(f"Failed | {e}")
            continue


if __name__ == '__main__':
    url = "https://app.diagrams.net/"
    data = []
    with open(r'g1149_syn_mermaid_v2.json', 'r', encoding='utf-8') as f:
        for line in f.readlines():
            rec = json.loads(line.strip())
            if 'answer_mode4' in rec:
                text = rec['answer_mode4']
                data.append({"id": rec['id'], "answer_mode4": text})
    
    run(url, data, start_idx=0)

五、后续工作

将渲染的xml文件再次渲染为JPG文件 。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gatinaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值