【问题标题】:How to extract headings from Word files to Excel?如何从Word文件中提取标题到Excel?
【发布时间】:2020-10-15 20:31:11
【问题描述】:

我有数百个 Word 文件 (docx),每个文件都有不同的标题,定义为标题 1、标题 2、标题 3 等。每个文件都有一个与标题对应的目录。
我想将每个文件中的每个标题提取到 Excel 工作簿中以构建数据库。

我的第一次尝试是将单个 Word 文档中的标题提取到 Excel 工作簿中。我在网上找到了从 Word 中提取标题到 Outlook 的代码,以及从 Word 中提取标题到新 Word 文件的单独代码。
我无法适应其中任何一个。

如何将单个 Word 文件中的标题提取到 Excel?然后我会尝试制定进一步的步骤。

Word 到 Outlook

Sub CopyHeadingsIntoOutlookMail()
    Dim objOutlookApp, objMail As Object
    Dim objMailDocument As Word.Document
    Dim objMailRange As Word.Range
    Dim varHeadings As Variant
    Dim i As Long
    Dim strText As String
    Dim nLongDiff As Integer

    'Create a new Outlook email
    Set objOutlookApp = CreateObject("Outlook.Application")
    Set objMail = objOutlookApp.CreateItem(olMailItem)
    objMail.Display
    Set objMailDocument = objMail.GetInspector.WordEditor
    Set objMailRange = objMailDocument.Range(0, 0)
 
    'Get the headings of the current Word document
    varHeadings = ActiveDocument.GetCrossReferenceItems(wdRefTypeHeading)

    For i = LBound(varHeadings) To UBound(varHeadings)
        strText = Trim(varHeadings(i))
 
        'Get the heading level
        nLongDiff = Len(RTrim$(CStr(varHeadings(i)))) - Len(Trim(CStr(varHeadings(i))))
        nHeadingLevel = (nLongDiff / 2) + 1
 
        'Insert the heading into the Outlook mail
        With objMailRange
            .InsertAfter strText & vbNewLine
            .Style = "Heading " & nHeadingLevel
            .Collapse wdCollapseEnd
        End With
    Next i
End Sub

逐字逐句

Public Sub CreateOutline()
    Dim docOutline As Word.Document
    Dim docSource As Word.Document
    Dim rng As Word.Range
    
    Dim astrHeadings As Variant
    Dim strText As String
    Dim intLevel As Integer
    Dim intItem As Integer
        
    Set docSource = ActiveDocument
    Set docOutline = Documents.Add
    
    ' Content returns only the
    ' main body of the document, not
    ' the headers and footer.
    Set rng = docOutline.Content
    astrHeadings = _
     docSource.GetCrossReferenceItems(wdRefTypeHeading)
    
    For intItem = LBound(astrHeadings) To UBound(astrHeadings)
        ' Get the text and the level.
        strText = Trim$(astrHeadings(intItem))
        intLevel = GetLevel(CStr(astrHeadings(intItem)))
        
        ' Add the text to the document.
        rng.InsertAfter strText & vbNewLine
        
        ' Set the style of the selected range and
        ' then collapse the range for the next entry.
        rng.Style = "Heading " & intLevel
        rng.Collapse wdCollapseEnd
    Next intItem
End Sub

Private Function GetLevel(strItem As String) As Integer
    ' Return the heading level of a header from the
    ' array returned by Word.
    
    ' The number of leading spaces indicates the
    ' outline level (2 spaces per level: H1 has
    ' 0 spaces, H2 has 2 spaces, H3 has 4 spaces.
        
    Dim strTemp As String
    Dim strOriginal As String
    Dim intDiff As Integer
    
    ' Get rid of all trailing spaces.
    strOriginal = RTrim$(strItem)
    
    ' Trim leading spaces, and then compare with
    ' the original.
    strTemp = LTrim$(strOriginal)
    
    ' Subtract to find the number of
    ' leading spaces in the original string.
    intDiff = Len(strOriginal) - Len(strTemp)
    GetLevel = (intDiff / 2) + 1
End Function

【问题讨论】:

  • 我怀疑你会想从 Excel 开始。您需要决定如何存储在 Excel 中获得的数据。每个文档有多少行?有多少个标题?您是否要为不同的标题级别设置不同的列?
  • 您会在 MVP 常见问题解答网站上找到循环遍历特定文件夹中所有文档以进行查找和替换的代码。一旦您拥有提取和存储标题的代码,就可以将其用于您的任务。 wordmvp.com/FAQs/MacrosVBA/BatchFR.htm
  • 注意,macropod 的宏是 Excel 宏。它不提取标题,而是提取目录。如果目录基于您想要的标题,它应该适合您。 (目录默认基于标题1-3,但可以基于其他内容。)
  • 亲爱的 Charles,非常感谢您抽出宝贵时间回答这个问题。下面的 Macropod 为我提供了完整的代码,所以我很高兴。
  • 抱歉,我在提交我的评论后看到了您的最后一条评论。也许理想情况下,它会在不破坏循环的情况下忽略任何没有目录的文件,或者只是提取标题。但是,它肯定会遇到大量文件来帮助构建数据库

标签: excel vba ms-word tableofcontents heading


【解决方案1】:

试试下面的 Excel 宏。运行时,只需选择要处理的文件夹即可。

Sub GetTOCHeadings()
'Note: this code requires a reference to the Word object model.
'See under the VBE's Tools|References.
Application.ScreenUpdating = False
Dim wdDoc As Word.Document, wdRng As Word.Range, wdPara As Word.Paragraph
Dim strFolder As String, strFile As String
Dim WkSht As Worksheet, i As Long, j As Long
strFolder = GetFolder
If strFolder = "" Then Exit Sub
Dim wdApp As New Word.Application
Set WkSht = ActiveSheet
i = WkSht.Cells(WkSht.Rows.Count, 1).End(xlUp).Row
wdApp.WordBasic.DisableAutoMacros
wdApp.DisplayAlerts = wdAlertsNone
strFile = Dir(strFolder & "\*.doc", vbNormal)
While strFile <> ""
  i = i + 1
  Set wdDoc = wdApp.Documents.Open(Filename:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
  With wdDoc
    j = 1: WkSht.Cells(i, j) = strFile
    If .TablesOfContents.Count > 0 Then
      With .TablesOfContents(1)
        .IncludePageNumbers = False
        .Update
        Set wdRng = .Range
      End With
      With wdRng
        .Fields(1).Unlink
        For Each wdPara In .Paragraphs
          j = j + 1
          WkSht.Cells(i, j).Value = Replace(wdPara.Range.Text, vbTab, " ")
        Next
      End With
    End If
    .Close SaveChanges:=False
  End With
  strFile = Dir()
Wend
wdApp.DisplayAlerts = wdAlertsAll
wdApp.Quit
Set wdDoc = Nothing: Set wdApp = Nothing: Set WkSht = Nothing
Application.ScreenUpdating = True
End Sub
 
Function GetFolder() As String
    Dim oFolder As Object
    GetFolder = ""
    Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
    If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
    Set oFolder = Nothing
End Function

【讨论】:

  • Macropod,这绝对是杰作。非常感谢您花时间写这篇文章,这对我帮助很大。我注意到,如果文档删除了目录(说它正在等待 OLE 操作),则会停止,但这对我来说应该不是问题。
  • 没问题,因为它们都应该有一个 TOC。我想我会稍微修改一下宏,让标题垂直显示在 B 列中,而不是水平显示在一行中,但这些都是我应该能够通过一些玩耍来管理的事情。它还显示了从中提取的文件名,这很棒。我想当我在包含数百个文档的文件夹中运行它时,我可能应该允许它一些时间来完成它的任务而不会干扰。无论如何,再次感谢您。
  • 缺少目录应该不会产生任何影响,因为代码会在尝试处理文档之前测试文档是否有一个(如果 .TablesOfContents.Count > 0 Then)。您应该看到的唯一区别是此类文档没有目录条目。这就是它在我的测试中的工作方式。我可以看到停滞的唯一原因是该文档是否具有指向需要更新的其他文档/文件的链接。如果文档受到保护(例如打开密码或“填写表格”的密码),它也会崩溃
  • «我想我会稍微修改宏以使标题垂直显示在 B 列中,而不是水平显示在一行中» Excel 中的数据库结构通常需要给定记录(即文件)的所有数据出现在同一行,这就是我以这种方式编写宏的原因。
  • 嗨。我已经运行了更多测试,实际上如果没有 TOC,那么就不会填充该行。我不完全确定为什么我在第一次测试时遇到了那个 OLE 错误(我复制了同一个文件 3 次并修改了每个文件中的标题内容,它卡在了第二个文件上 - 可能是因为我故意没有更新更改第二个文件中的标题后的目录)。关于行与列的人口,你说的是有道理的。我最初的想法是,如果它们都在同一列中,那么过滤条目会更容易,但 CTRL + F 基本上做同样的工作。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-24
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
相关资源
最近更新 更多