【问题标题】:Navigate html table using tags to extract inner text (vbs)使用标签导航 html 表格以提取内部文本 (vbs)
【发布时间】:2018-07-05 11:43:33
【问题描述】:

我试图获取的数据示例(查看源代码):https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=8B7B2B88CE03567735560917596FA6BD

源码如下:

<table width="100%" border="0" cellspacing="0" cellpadding="2" bgcolor="#e9edf2">
<tr>
    <td width="2%" height="20"><font size="1">&nbsp;</font></td>
    <td width="30%" valign="top"><font face="verdana" size="-1">Name:</font></td>
    <td width="48%">
        <font face="verdana" size="-1"><b>ABBOTT, HUGH ALLAN&nbsp;<small>(Primary Name)</small></b></font></td>

</tr>

我不知道如何故意导航到源的这一部分。我需要以某种方式告诉它在所有带有 TD 的标签中搜索“名称:”,如果存在,请给我标签的下一个内部文本

<b> 

在这种情况下是雅培,休艾伦。我需要这种类型的方法,因为使用 item(#) 查找特定文本并不可靠,因为项目的位置发生了变化。我尝试了几种不同的方法,但到目前为止都没有成功。比如“for each td in ....”类型的方法。我最终可以找到正确的项目,但它在多条记录中不可靠。

TIA

编辑 - 这是我拥有的接近的代码:

这假设您有一个具有此路径/名称的文本文件(尽管它表示路径中的电子邮件抓取,而不是在这种情况下尝试获取电子邮件):“C:\Emailgrab\myfloridalicense.com\Extract URL\AgentURLsRaw_Clean. txt”,其中包含以下链接:

https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=2BEA648A94BA20C0C989E9E0071103AF https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=AB8F78E2835A25C2D443B09DE9CDD16F https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=A6DBB6CDEE69A637B4497807A1FE45A6 https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=8B7B2B88CE03567735560917596FA6BD https://www.myfloridalicense.com/LicenseDetail.asp?SID=&id=27A84B8EF8F96AD4F09AF94774456A39

还假设您在此路径/名称处拥有此头文件:“C:\Emailgrab\myfloridalicense.com\Extract URL\Complete.csv”,其中包含以下标头:

姓氏、名字/中间名、地址、许可证编号、许可证状态(a)、许可证状态(b)、许可证到期、URL

VBS 代码:

Dim URLFile 
Dim fName
set ie = createobject("internetexplorer.application")
IE.Visible = True
Set objShell = CreateObject("WScript.Shell")
Set WshShell = WScript.CreateObject("WScript.Shell") 
set fso = createobject("scripting.filesystemobject")
Set URLFile = fso.OpenTextFile("C:\Emailgrab\myfloridalicense.com\Extract URL\AgentURLsRaw_Clean.txt")

do while not URLFile.AtEndOfStream 
fName = URLFile.ReadLine()

ie.navigate fName
do until ie.readystate = 4 : wscript.sleep 10: loop 

For Each elm In IE.Document.getElementsByTagName("table")
If elm.getElementsByTagName("TABLE").Length = 16 THEN

name = elm.document.getElementsByTagName("b").item(3).innertext
address = elm.document.getElementsByTagName("b").item(5).innertext
licensenumber = elm.document.getElementsByTagName("b").item(12).innertext
licensestatus = elm.document.getElementsByTagName("b").item(13).innertext
licenseexp = elm.document.getElementsByTagName("b").item(15).innertext

myData =  name & ", " & replace(address, vbCrLf, "") & ", " & licensenumber & ", " & licensestatus & ", " & licenseexp & "," & fName & ", " & vbCrlf

set fso = createobject("scripting.filesystemobject")
set ts = fso.opentextfile("C:\Emailgrab\myfloridalicense.com\Extract URL\Complete.csv",8,true) 
ts.write myData 
ts.close 

end if
next

loop

Wscript.Echo "All Data Copied!"

我的方法很接近,但我不太明白为什么它适用于某些链接而不适用于其他链接。您可以在 CSV 中看到,在某些情况下,从许可证号开始数据已关闭,这会导致其余列关闭。此外,在某些情况下,数据中有逗号,所以我添加了一个额外的 LicenseStatus 列来尝试解决这个问题。我只需要一种(最好是简单的)比使用 Item(#) 计数更可靠的方法。希望这可以帮助。谢谢!

【问题讨论】:

  • 可能复制到[VBS脚本循环浏览网页和复制数据](stackoverflow.com/q/28384650/3439404),相同的OP。正如您知道如何获取页面源代码,那么您可以知道最原始的方法:split 将页面转换为一个从零开始的一维数组,其中包含子串,例如&lt;/tr&gt; 分隔符。循环数组,找到一个包含Name:的字符串,用&lt;b&gt;分隔符...
  • 不完全确定我是否在关注,但我会研究拆分。如果我能把这块弄下来,那么我想我几乎可以拥有我需要的东西来做我需要的一切。谢谢
  • 我发现了这个:webmasterworld.com/forum47/1363.htm,但我不明白如何应用它。也没有看到任何关于跳过那个找到下一个内部文本的信息,我不知道如何问谷歌这个问题。我想使用“名称:”来知道我在正确的区域,所以我会知道下一个 是我需要的文本。我还有其他人也可以应用它,所以理解这一点会有所帮助。听起来你在说这是可能的。即使是一个粗略的例子也会有所帮助。

标签: regex web-scraping vbscript xmlhttprequest wsh


【解决方案1】:

更新

这里是基于 HTTP 请求和 RegExp 解析到 Dictionary 的实现,它将带有 URLs 的 txt 文件作为输入,并将结果写入 csv 文件:

arrUrls = Split(ReadTextFile("C:\Emailgrab\myfloridalicense.com\Extract URL\AgentURLsRaw_Clean.txt", 0), vbCrLf)
sCsv = ""
For Each sUrl in arrUrls
    XmlHttpRequest "GET", sUrl, "", "", "", sRespText
    HtmlSimplify sRespText
    ParseToDict "<tr><td></td><td>([^<]*?)</td><td>([^<]*?)(?:</td>){0,1}</tr>", sRespText, oResult
    sCsv = sCsv & """" & oResult("Name:") & """" & ","
    sCsv = sCsv & """" & oResult("Main Address:") & """" & ","
    sCsv = sCsv & """" & oResult("License Number:") & """" & ","
    sCsv = sCsv & """" & oResult("Status:") & """" & ","
    sCsv = sCsv & """" & oResult("Expires:") & """" & ","
    sCsv = sCsv & """" & sUrl & """" & vbCrLf
Next
WriteTextFile sCsv, "C:\Emailgrab\myfloridalicense.com\Extract URL\Complete.csv", 0
WScript.Echo "All Data Copied!"

Function ReadTextFile(sPath, iFormat)
    With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, iFormat)
        ReadTextFile = ""
        If Not .AtEndOfStream Then ReadTextFile = .ReadAll
        .Close
    End With
End Function

Sub WriteTextFile(sCont, sPath, iFormat)
    With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 2, True, iFormat)
        .Write(sCont)
        .Close
    End With
End Sub

Sub HtmlSimplify(sCont)
    With CreateObject("VBScript.RegExp")
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        .Pattern = "(<[\w\/^<]*)[\s\S]*?>"
        sCont = .Replace(sCont, "$1>")
        .Pattern = "(?:<font>|</font>|<b>|</b>|<small>|</small>|<br>)"
        sCont = .Replace(sCont, "")
        .Pattern = "&nbsp;"
        sCont = .Replace(sCont, " ")
        .Pattern = "[\f\n\r\t\v]"
        sCont = .Replace(sCont, "")
        .Pattern = " +"
        sCont = .Replace(sCont, " ")
        .Pattern = "> <"
        sCont = .Replace(sCont, "><")
    End With
End Sub

Sub XmlHttpRequest(sMethod, sUrl, arrSetHeaders, sFormData, sRespHeaders, sRespText)
    Dim arrHeader
    With CreateObject("Msxml2.ServerXMLHTTP.3.0")
        .SetOption 2, 13056 ' SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS
        .Open sMethod, sUrl, False
        If IsArray(arrSetHeaders) Then
            For Each arrHeader In arrSetHeaders
                .SetRequestHeader arrHeader(0), arrHeader(1)
            Next
        End If
        .Send sFormData
        sRespHeaders = .GetAllResponseHeaders
        sRespText = .ResponseText
    End With
End Sub

Sub ParseToDict(sPattern, sResponse, oDict)
    Dim oMatch, arrSMatches, sSubMatch
    Set oDict = CreateObject("Scripting.Dictionary")
    With CreateObject("VBScript.RegExp")
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        .Pattern = sPattern
        For Each oMatch In .Execute(sResponse)
            If Trim(oMatch.SubMatches(0)) <> "" Then oDict(oMatch.SubMatches(0)) = oMatch.SubMatches(1)
        Next
    End With
End Sub

每个网页解析的数据都以项目名称作为关键字放入字典中。 csv 的内容是通过其名称而不是相对位置引用值来创建的,因此对于任何列出的 URL,所有列都在它们的位置。
此外,我还添加了双引号,以避免使用 Current,Inactive (RFC 4180 点 2.6)等逗号分隔成单独的列值。
它不是任何网站的通用解决方案。对于另一个网站,您必须创建适当的 RegExp 模式进行解析。在这种情况下,我将 HTML 内容保存到 HtmlSimplify 之后的文件中,并检查它以找出所需的模式。更重要的是,要将这种方法用于更复杂的网站,您可能需要通过切断无关的 HTML 部分从而缩小搜索范围,从而分几步进行解析(可能会被循环)。

考虑一下这个VBS解析器:

' sCont contains table HTML at the moment

With CreateObject("VBScript.RegExp")
    .Global = True
    .MultiLine = True
    .IgnoreCase = True
    ' content simplification
    .Pattern = "(<[\w\/^<]*)[\s\S]*?>"
    sCont = .Replace(sCont, "$1>")
    .Pattern = "(?:<font>|</font>|<b>|</b>|<small>|</small>|<br>)"
    sCont = .Replace(sCont, "")
    .Pattern = "&nbsp;"
    sCont = .Replace(sCont, " ")
    .Pattern = "[\f\n\r\t\v]"
    sCont = .Replace(sCont, "")
    .Pattern = " +"
    sCont = .Replace(sCont, " ")
    .Pattern = "> <"
    sCont = .Replace(sCont, "><")
End With
ParseToArray "<tr><td></td><td>([^<]*?)</td><td>([^<]*?)(?:</td>){0,1}</tr>", sCont, arrResult

' continue processing of arrResult
WScript.Echo arrResult(0)(1) ' eg shows name
' ...

Sub ParseToArray(sPattern, sResponse, arrMatches)
    Dim oMatch, arrSMatches, sSubMatch
    arrMatches = Array()
    With CreateObject("VBScript.RegExp")
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        .Pattern = sPattern
        For Each oMatch In .Execute(sResponse)
            arrSMatches = Array()
            For Each sSubMatch in oMatch.SubMatches
                PushItem arrSMatches, sSubMatch
            Next
            PushItem arrMatches, arrSMatches
        Next
    End With
End Sub

Sub PushItem(arrList, varItem)
    ReDim Preserve arrList(UBound(arrList) + 1)
    arrList(UBound(arrList)) = varItem
End Sub

sCont开头如下:

<table cellspacing="0" cellpadding="1" width="100%" border="0" bgcolor="#b6c9dc"><tr><td>
    <table width="100%" border="0" cellspacing="0" cellpadding="3"><tr>
        <td width="32%"><font face="verdana" size="-1"><b>Licensee Information</b></font></td>
    </tr></table>
    <table width="100%" border="0" cellspacing="0" cellpadding="2" bgcolor="#e9edf2">
    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">Name:</font></td>
        <td width="48%">
            <font face="verdana" size="-1"><b>ABBOTT, HUGH ALLAN&nbsp;<small>(Primary Name)</small></b></font></td>

    </tr>
    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1"></font></td>
        <td width="48%"><font face="verdana" size="-1"><b>&nbsp;<small>(DBA Name)</small></b></font></td>
    </tr>
    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">Main Address:</font></td>
        <td width="68%"><font face="verdana" size="-1"><b>318 TURKEY CREEK 
        <br>ALACHUA&nbsp;&nbsp;Florida&nbsp;&nbsp;32615</b></font>
        </td>
    </tr>

    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">County:</font></td>
        <td width="68%"><font face="verdana" size="-1"><b>ALACHUA
    </tr>



    <tr><td>&nbsp;</td></tr>
    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">License Mailing:</font></td>
        <td width="68%"><font face="verdana" size="-1"><b>318 TURKEY CREEK

        <br>ALACHUA&nbsp;&nbsp;FL &nbsp;&nbsp;32615</b></font>

    </tr>

    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">County:</font></td>
        <td width="68%"><font face="verdana" size="-1"><b>ALACHUA
    </tr>

    <tr><td>&nbsp;</td></tr>
    <tr>
        <td width="2%" height="20"><font size="1">&nbsp;</font></td>
        <td width="30%" valign="top"><font face="verdana" size="-1">LicenseLocation:</font></td>
        <td width="68%"><font face="verdana" size="-1"><b> 

        <br>&nbsp;&nbsp; &nbsp;&nbsp; </b></font>

    </tr>

    </table>

</td></tr></table>

它获取子数组的数组,如下所示的本地调试器屏幕截图:

【讨论】:

  • 这看起来很复杂。我很欣赏这项工作!原谅我,不完全确定如何运行它。输出看起来像是告诉我这些项目落在表中的哪个位置,这很棒……还是您引用的“本地调试器”?原谅我的新手...我想知道如何在没有模式匹配的情况下专门引用子数组...只是因为我希望能够将其应用于其他事物...或者这将始终适用于任何表格情况?跨度>
  • 谢谢你!看起来很有希望。对于更新的解决方案,我收到错误“参数不正确”代码:80070057,来源:msxml3.dll。
  • 你能指定错误行吗?如果您调试代码 e 将很有帮助。 G。使用 MS 脚本编辑器(通过快捷方式运行,其中命令行应为 x86 的 wscript.exe //d c:\test\test.vbs 或 x64 的 c:\windows\syswow64\wscript.exe //d c:\test\test.vbs)、VbsEdit 或其他,以找出变量值,尤其是 sUrl。最后你可以添加WScript.Echo(sUrl)。看起来arrUrls = Split(...) 工作错误,'sUrl' 给出了空字符串值。
  • 我按照你说的运行了 x64 命令,得到了同样的 msxml3.dll 错误消息。
  • 嗯,我期待那行,sUrl 的值是多少? 你安装了 MS 脚本编辑器吗?它的窗口是否出现错误?我猜你的 txt 文件有过多的换行符。我尝试将空的sUrl 传递给.Open 方法,得到了和你一样的错误。
【解决方案2】:

我无法完全实施您的方法,但您的回复中的一些元素确实让我找到了一个可行的解决方案。如果“许可证号”列包含许可证号以外的内容,我使用 IF 语句的组合将每个项目移动到必要的列数上。还使用您的建议让替换功能处理逗号问题。那是我的问题的一部分,那就是把事情扔掉。

Dim URLFile 
Dim fName
set ie = createobject("internetexplorer.application")
IE.Visible = True
Set objShell = CreateObject("WScript.Shell")
Set WshShell = WScript.CreateObject("WScript.Shell") 
set fso = createobject("scripting.filesystemobject")
Set URLFile = fso.OpenTextFile("C:\Emailgrab\myfloridalicense.com\Extract URL\AgentURLsRaw_Clean.txt")

do while not URLFile.AtEndOfStream 
fName = URLFile.ReadLine()

ie.navigate fName
do until ie.readystate = 4 : wscript.sleep 10: loop 

For Each elm In IE.Document.getElementsByTagName("table")
If elm.getElementsByTagName("TABLE").Length = 16 THEN

name = elm.document.getElementsByTagName("b").item(3).innertext
address = replace(elm.document.getElementsByTagName("b").item(5).innertext,","," ")

'License Number
If InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Real Estate Broker or Sales") THEN 
licensenumber = elm.document.getElementsByTagName("b").item(14).innertext 
ELSEIf InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Broker") or InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Sales Associate") THEN
licensenumber = elm.document.getElementsByTagName("b").item(13).innertext
Else licensenumber = elm.document.getElementsByTagName("b").item(12).innertext
End If

'License Status
If InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Real Estate Broker or Sales") THEN 
licensestatus = elm.document.getElementsByTagName("b").item(15).innertext 
ELSEIf InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Broker") or InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Sales Associate") THEN
licensestatus = elm.document.getElementsByTagName("b").item(14).innertext
Else licensestatus = elm.document.getElementsByTagName("b").item(13).innertext
End If

'License Exp
If InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Real Estate Broker or Sales") THEN 
licenseexp = elm.document.getElementsByTagName("b").item(17).innertext 
ELSEIf InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Broker") or InStr(elm.document.getElementsByTagName("b").item(12).innertext, "Sales Associate") THEN
licenseexp = elm.document.getElementsByTagName("b").item(16).innertext
Else licenseexp = elm.document.getElementsByTagName("b").item(15).innertext
End If

myData =  name & ", " & replace(address, vbCrLf, "") & ", " & replace(licensenumber, ","," ") & ", " & replace(licensestatus, ","," ") & ", " & replace(licenseexp,","," ") & "," & fName & ", " & vbCrlf

set fso = createobject("scripting.filesystemobject")
set ts = fso.opentextfile("C:\Emailgrab\myfloridalicense.com\Extract URL\Complete.csv",8,true) 
ts.write myData 
ts.close 

end if
next

loop

Wscript.Echo "All Data Copied!"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 2018-06-15
    • 2021-08-08
    • 2020-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多