用 (Excel) VBA 读取 OneNote!

本文记录,用 VBA 读取 OneNote 的方法,这块似乎一直是空白,研究了好久才找到解决方案!小白贡献,语失莫怪!


问题背景:

我在 OneNote 里有上百篇笔记,可 OneNote 自己,却无法导出全部的标题。于是我千方百计,想要读取 OneNote 的文件,来获取标题和日志信息。尝试了各种方案,都没能读出 OneNote 的数据。最后过了好久好久,才找到了这种,基于 Excel VBA 的解决方式!那么接下来,我们就来看下它的代码!在片尾我也会讲下,尝试过的其他方式,也许哪位路过的大神,能解决掉其中的问题!


代码如下:

  1. 首先要注意,这个代码是要添加到 Excel VBA 编辑器里的!因为 OneNote 里根本就没有 vba 编辑器,所以只能通过 Excel 里的 VBA 编辑器,来实现 OneNote 的操控和读取!
  2. 然后要注意,在运行代码前,需先添加几个 Reference (引用库)!
    1. Microsoft OneNote 12.0 Object Library
    2. Microsoft OneNote 15.0 Object Library
    3. Microsoft XML, V3.0
  3. 不同的 Office 可能会出现,这几个库的版本不一样,那么就需要根据你的库的版本,去微调下这个代码!否者会报错!

'Add the following references (adjust to your office version):
'- Microsoft OneNote 12.0 Object Library
'- Microsoft OneNote 15.0 Object Library
'- Microsoft XML, v3.0

Sub ListOneNotePages()
    'OneNote will be started, if it's not running.
    Dim OneNote As OneNote.Application
    Set OneNote = New OneNote.Application

    'Get the XML that represents the OneNote pages
    Dim oneNotePagesXml As String

    'Use note hierarchy, to get all page id.
    OneNote.GetHierarchy "", OneNote12.HierarchyScope.hsPages, oneNotePagesXml

    'Use the MSXML Library to parse the XML.
    Dim doc As MSXML2.DOMDocument
    Set doc = New MSXML2.DOMDocument

    If doc.LoadXML(oneNotePagesXml) Then
        'Find all the Page nodes in the one namespace.
        Dim nodes As MSXML2.IXMLDOMNodeList
        Set nodes = doc.DocumentElement.SelectNodes("//one:Page")

        Dim node As MSXML2.IXMLDOMNode
        Dim pageName As String
        Dim sectionName As String
        Dim pageContent As String
        Dim temp As String

        'Go through each page, and print out page content
        For Each node In nodes
            pageName = node.Attributes.getNamedItem("name").Text
            Debug.Print "Page name: "; vbCrLf & " " & pageName

            Call OneNote.GetPageContent(GetAttributeValueFromNode(node, "ID"), pageContent, piBasic)
            Debug.Print " content: " & pageContent
        Next
    Else
        MsgBox "OneNote 2010 XML Data failed to load."
    End If
End Sub

Private Function GetAttributeValueFromNode(node As MSXML2.IXMLDOMNode, attributeName As String) As String
    If node.Attributes.getNamedItem(attributeName) Is Nothing Then
        GetAttributeValueFromNode = "Not found."
    Else
        GetAttributeValueFromNode = node.Attributes.getNamedItem(attributeName).Text
    End If
End Function

Remote Procedure Call Failed 报错:

  1. 这个报错,我的理解是,它是在说,无法访问你要用的那个程序!具体为什么无法访问,和什么原因,我到现在也还是不知道!
  2. 在上面 VBA 的解决方案里,我只是把代码里的 OneNote12.Application,改成了 OneNote.Application 就解决了!具体为什么,不知道!但这个报错,确实就没了!我猜测是因为 OneNote 的版本的问题,之前报错,是因为它一直找不到 OneNote14.Application 这个本版本,但细节的问题就不讨论了!
  3. 但在 Python 的 win32com 方案里,则是一直都卡在,这个报错这里,一直没能过去!也因此在 Python 的解决方案里,到了最后也无法读取任何的 OneNote Page!

OneNote 无法启动问题:

  1. 如果发现,运行了 Python 的代码后,自己的 OneNote 显示正在恢复,无法打开!不用担心,只需去资源管理器里,把 OneNote.exe 这个任务关掉即可。然后 OneNote 就可以正常打开了!

其他解决方案:

其实在 Excel VBA 的解决方案之前,还尝试了很多很多其他的方案,那也粗略的说一下吧。

  1. 在 OneNote 中添加 COM 插件,使其能运行宏,类似于 VBA!这个插件叫做Onetastic!据说这个插件的作者,正是来自原 OneNote 的开发团队的成员,
    所以其实这个插件的质量很高,但就是要花钱买,所以最后也没采用这个方案!还有一个插件也很强悍,叫Gem for OneNote,但不符合我的需求!OneNote 的插件还有很多,感兴趣的同学,可以去看下面的链接!

  2. 就是自己开发插件,让其能在 OneNote 中运行。这个要求有点高,这种大神应该就不用,看我这篇文章了。所以如果有能力,这也确实是个选项!

  3. 就是使用 Python 的 win32com,其实 win32com 也是使用的 windows 自己的 com 接口,但 Python 毕竟是通过一个第三库,在做这件事情,所以这个库,要是运行不畅,或者出什么问题,作为小白,就真是很难解决了!就例如一直困扰我的,Remote Procedure Call Failed 的报错问题,就到现在也没能解决!这个报错很可能是,更深层的 COM 服务的问题,很可能是 onenote.dll 没了什么的,但这已经超出小白的范畴了。

  4. 就是还有各种 Python 的第三方库,例如:pyOneNote,onepy 什么的!我自己几乎都试一遍了,基本没一个好使了,都是很久之前的库了!我觉得这可能是,很少有人会去操控 OneNote,所以在这上面花心思的人就很少,这也是为什么,我找了很久,都找不到答案!所以才决定写这篇文章,希望能给需要的人,一点方便!


一些可用的 Python 代码:

至于下列代码里,为什么都是 OneNote.Application.12 我也不知道!但如果你的不好使,也可以改成 OneNote.Application.14 试试!但在我的电脑里,只有 OneNote.Application.12 好使!

# 输出页面结构信息
import win32com.client
oneapp = win32com.client.Dispatch('OneNote.Application.12')
result = oneapp.GetHierarchy("",4)
print(result)

## 输出所有页面的标题
import win32com.client
oneapp = win32com.client.Dispatch('OneNote.Application.12')
result = oneapp.GetHierarchy("",4)

from bs4 import BeautifulSoup
soup = BeautifulSoup(result, 'xml')
for tag in soup.find_all('one:Page'):
    print(tag.attrs['name'])

# 试图输出任何一个页面的内容 (报错暂时无解)
# >>> Error occurred: (-2147023170, 'The remote procedure call failed)
import win32com.client
onapp = win32com.client.Dispatch('OneNote.Application.12')
result = onapp.GetHierarchy("",4)
print(result)

pageID = "任何上面输出过的PageID"
content = onapp.GetPageContent(pageID)
print(content)

后期的应用

被 VBA 读取出来了,是 OneNote 每个页面的全部内容!信息量很大,都是 XML 形式的!之后的处理就不再赘述了,怎么处理就取决于各自的需求了!好的就写到这里了,希望能有帮助,(^_^)b


Reference:

  1. Automate Onenote 2010 From Excel 2007, using VBA - Stack Overflow
  2. Updating OneNote from VBA Excel - Stack Overflow
  3. OneNote developer reference | Microsoft Learn
  4. reading xml files in vb6 - Stack Overflow
  5. Top 11 Microsoft OneNote Add-ins
  6. Onetastic for OneNote