用 (Excel) VBA 讀取 OneNote!

Bitssea發表於2024-10-22

本文記錄,用 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

相關文章