一个使用 Autoit 脚本从 MSWord 文档中提取数据并将其发送到 Internet 应用程序的示例





5.00/5 (2投票s)
一个从 MSWord 文档中抓取信息并通过 Ajax 技术发送到 Internet 的程序
引言
这是一个使用 Autoit[1] 访问 MSWord 文档并提取一些数据的示例,但它也旨在展示 COM (Component Object Model) 对象的使用,特别是使用 MSWord 的 COM 对象 和 MSXML2.XMLHTTP (WinHttpRequest 对象),它实现了 Ajax 协议。
Autoit 有一个 MS Word COM 对象库,其中包含一些函数,因此我在这里使用了本机方法和属性,因为该库不包含我需要的函数,因此可以更好地理解 Autoit 如何使用 COM 对象。
本文包含以下主题
- 如何以对象的形式访问 MSWord 文档
- 如何提取数据
- 如何准备和发送数据
访问 MSWord 文档
我们需要实例化一个 MSWord COM 对象:这可以通过多种方式完成
- 如果我们想要一个新文档,可以使用
ObjCreate("Word.Application")
- 对于已打开的文档,可以使用
ObjGet("", "Word.Application")
- 对于特定文档,可以使用
ObjGet("fileName", "Word.Application")
或简写ObjGet("MSWordfileName")
如果 MSWord 未激活,则第二种模式会失败,并在变量 @error
中返回一个不同于 0
的值。 在下面的脚本片段中,所有方法都被使用; 请注意创建新文档的选择,它假定剪贴板包含数据。
Global $oWord = ObjGet("", "Word.Application")
If @error <> 0 Or $oWord.Documents.Count = 0 Then
; This is the case in which MSWord or is not present (@error <> 0)
; or has not documents ($oWord.Documents.Count = 0)
$form = "File,Document,,30,,Documents (*.doc\59*.docx)|All (*.*);" _
& "C,Enter for a New Document"
$parms = formGen("Word File",$form,-1,"",100,100) ; create a form for call a filename
If $parms.Item("fg_button") = "Cancel" Then Exit
If $parms.Item("Document") <> "" Then
$oDoc = ObjGet($parms.Item("Document")) ; open the MSWord document
Else
$oAppl = ObjCreate("Word.Application")
$oAppl.Visible = True
$oDoc = $oAppl.Documents.add ; creates an empty document
$range = $oDoc.Range ; Set range start/end at the end to the document
$range.Collapse($WdCollapseEnd)
$range.paste ; paste the clipboard
EndIf
$oDoc.Application.Visible = 1
Else
; This is the case in which MSWord is present and has one or more documents open
Local $nDocs = $oWord.Documents.Count
$nDoc = 1
If $nDocs <> 01 Then
Local $docs = ""
For $i = 1 to $nDocs
consoleWrite($oWord.Documents($i).Name & @CRLF)
$docs &= "|" & $i & "=" & $oWord.Documents($i).Name
Next
$form = "CMB,Document,,20,," & StringMid($docs,2)
$parms = formGen("Word Files",$form,-1,"",100,100) ; _
create a form to choose which document to process
If $parms.Item("fg_button") = "Cancel" Then Exit
$nDoc = $parms.Item("Document")
EndIf
$oDoc = $oWord.Documents($nDoc)
EndIf
在脚本中,用于请求文档名称的简单表单由我的实用程序 formGen
创建,您可以在我的 网站 上找到它。
数据捕获
MSWord 的对象模型提供了一组复杂的对象,其中每个对象都可以包含对象、方法和属性的集合; 这些集合可以引用整个文档、文档的一部分或选择[2]。
在本脚本中,我们主要关注 Paragraphs
和 Hyperlinks
集合; 特别是,该文档包含一个结构化格式的作业列表(见下文),其中包含一个 Internet 链接
FRANCE: 1 PostDoc position in HISTORY
Ref. 46_16 - City: Orleans - Deadline: 12/02/2016 »
对象 Paragraph
的属性 text
是数据的容器,这些数据通过使用正则表达式[3] (RE) 提取。
For $paragraphCount = 1 To $nParag
$parag = StringReplace($oParag($paragraphCount).Range.Text,chr(11),"") ; clear VT
$aExtract = StringRegExp($parag, '^(.+): (.+)(Ref\..*) - City: (.+) - _
Deadline: (\d\d/\d\d/\d\d\d\d)', $STR_REGEXPARRAYGLOBALMATCH)
If isArray($aExtract) Then
$itemCount += 1
$aExtract[4] = StringRegExpReplace($aExtract[4], '(\d{2})/(\d{2})/(\d{4})', '$3/$2/$1') ; _
date from dd/mm/yyyy to yyy/mm/dd
$data = ""
For $i = 0 To UBound($aExtract) - 1
$data &= "&" & $aFields[$i] & "=" & encode64($aExtract[$i])
Next
$aCategory = StringRegExp($aExtract[1], '.*in (.*)', $STR_REGEXPARRAYGLOBALMATCH) ; Category
If isArray($aCategory) Then $data &= "&Category=" & encode64($aCategory[0]) ; _
extracts the study field
For $hLink In $oParag($paragraphCount).Range.Hyperlinks
$link = $hLink.Address
Next
$data &= "&Link=" & encode64($link)
ConsoleWrite(StringMid($data,2) & @CRLF)
$res = ajax($url,StringMid($data,2))
If $res <> 1 Then ConsoleWrite("--> " & $res & @CRLF)
EndIf
Next
您可以在 Autoit Help 中找到正则表达式语法的文档,这里仅对如何在脚本中使用 RE 进行一些说明:如果文本与 RE 匹配,则 StringRegExp
函数提取由一对括号分隔的内容匹配的数据。 每个提取的标记都有一个名称 $1, $2, ...,这允许 StringRegExpReplace
函数重新塑造日期。
对于段落中包含的链接,脚本访问段落的 Hyperlinks
集合并获取 Address
属性。
发送数据
提取的数据必须以 name<sub>1</sub>=value<sub>1</sub>&name<sub>2</sub>=value<sub>2</sub>...
的形式准备,并且数据可以包含 HTTP 协议中使用的字符,例如 =
和 &
,因此数据必须进行编码,例如以 base64 格式编码,其中只有 =
、+
和 /
是协议中使用的字符[4]。
Global $base64 = StringSplit("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/","",2)
$base64[62] = "%2B" ; +
$base64[63] = "%2F" ; /
...
$aFields = StringSplit("Nation|Title|Notes|Town|Deadline","|",2)
...
For $i = 0 To UBound($aExtract) - 1
$data &= "&" & $aFields[$i] & "=" & encode64($aExtract[$i])
Next
...
Func encode64($in)
$bin = 0
$out = ""
For $i = 1 to StringLen($in)
$bin = BitShift($bin,-8)
$bin += Asc(StringMid($in,$i,1))
If Mod($i,3) = 0 Then
$out &= encode6($bin)
$bin = 0
EndIf
Next
If Mod(StringLen($in),3) <> 0 Then $out &= encode6($bin,Mod(StringLen($in),3))
return $out
EndFunc
Func encode6($bin,$l=3)
If $l <> 3 Then $bin = BitShift($bin,-8*(3-$l))
$out = ""
For $i=3 To 3-$l Step -1
$cod6 = BitShift($bin,$i*6)
$bin -= BitShift($cod6,-$i*6)
$out &= $base64[$cod6]
Next
return $out
EndFunc
如果接收者是一个 PHP 脚本,则此代码段会恢复数据:foreach ($_REQUEST as $key => $value) $$key = base64_decode($value);
我使用 COM 对象 MSXML2.XMLHTTP
以同步模式发送数据:$ajax.open("POST", $url, true)
Global $ajax = ObjCreate("MSXML2.XMLHTTP")
...
Func ajax($url,$data)
$ajax.open("POST", $url, true) ; synchronous !
$ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
$ajax.send($data)
Local $hTimer = TimerInit() ; Begin the timer and store in a variable.
While TimerDiff($hTimer) < 10000
If $ajax.readyState == 4 Then
If $ajax.status = 200 Then return $ajax.responseText
EndIf
Sleep(10)
WEnd
return "Timeout!"
EndFunc
注释
- ^AutoIt 是一种免费的类 BASIC 脚本语言,是 PowerShell 的替代品,最初设计用于自动化与 Windows GUI 的交互。 AutoIt 可以在 Windows 上解释或编译运行。
它附带许多库,这些库能够实现访问 COM 对象和创建图形界面等功能。 - ^文档可以有多个范围,但只有一个选择。
- ^此站点 https://regex101.com/ 对于测试正则表达式非常有用。
- ^代码开销为 33%,我没有使用
=
字符,如果需要编码的数据长度不能被 3 整除,则用于填充; 如果要连接两组数据,则填充是必需的。