使用VBA访问iframe中的对象

10

简述:

我已成功使用VBA执行以下操作:

  • 通过getElementsByName登录网站

  • 选择生成报告的参数(使用getelementsby...)

  • 在选择参数后生成报告,将结果数据集呈现在同一页面的iframe中

需要注意的是-该网站为客户端站点

以上是简单部分,困难部分如下:

点击iframe中的gif图像,将数据集导出为csv

我尝试了以下方法:

Dim idoc As HTMLDocument
Dim iframe As HTMLFrameElement
Dim iframe2 As HTMLDocument

Set idoc = objIE.document
Set iframe = idoc.all("iframename")
Set iframe2 = iframe.contentDocument

    Do Until InStr(1, objIE.document.all("iframename").contentDocument.innerHTML, "img.gif", vbTextCompare) = 0
        DoEvents
    Loop

为了解释上面的逻辑 -

  • 我访问了主框架
  • 我通过名称元素访问了iframe
  • 我访问了iframe中的内容
  • 我试图找到需要点击以导出到csv的gif图像

就在这一行它失败并显示“对象不支持此属性或方法”

我还尝试通过a元素和href属性访问iframe gif,但是完全失败了。我还尝试从其源URL获取图像,但所有这些都只会带我到图像所在的页面。

注意:iframe没有ID,并且奇怪的是,gif图像没有“onclick”元素/事件

最后考虑 - 尝试使用R爬取iframe

访问iframe的HTML节点很简单,但是尝试访问iframe的属性,随后访问表格节点都没有成功。 它只返回“Character(0)”

library(rvest)
library(magrittr)

Blah <-read_html("web address redacted") %>%
  html_nodes("#iframe")%>%
  html_nodes("#img")%>%
  html_attr("#src")%>%
  #read_html()%>%
  head()
Blah
一旦我包括了read_html,脚本就返回以下错误:
如果(grepl ("<|>", x)),那么错误是 argument is of length zero。
我怀疑这指的是Character(0)。
非常感谢任何指导!
谢谢。
<div align="center"> 
    <table id="table1" style="border-collapse: collapse" width="700" cellspacing="0" cellpadding="0" border="0"> 
        <tbody>
            <tr>
                <td colspan="6"> &nbsp;</td>
            </tr> 
            <tr> 
                <td colspan="6"> 
                    <a href="href redacted">
                        <img src="img.gif" width="38" height="38" border="0" align="right">
                    </a>
                    <strong>x - </strong>
                </td>
            </tr> 
        </tbody>
    </table>
</div>

展示一下 gif 周围的 HTML 代码。你已经可以通过 contentDocument 访问 iframe,是吗?现在需要的就是 GIF 周围的 HTML。然后我们就能看到了。 - MacroMarc
@MacroMarc,您需要查看HTML的哪个部分? - mojo3340
是 href 吗? - mojo3340
gif的父元素和gif本身... - MacroMarc
3个回答

9

有时候使用iframes有些棘手。基于您提供的html,我创建了这个示例。它在本地可以运行,但您也能用吗?

若要访问IFrame,可以使用frames集合。希望您知道IFramename

Dim iframeDoc As MSHTML.HTMLDocument
Set iframeDoc = doc.frames("iframename").document

然后,要访问image,我们可以使用querySelector方法,例如:

Dim img As MSHTML.HTMLImg
Set img = iframeDoc.querySelector("div table[id='table1'] tbody tr td a[href^='https://stackoverflow.com'] img")

选择器 a[href^='https://stackoverflow.com'] 选择具有以给定文本开头的 href 属性的 anchor^ 表示开头
然后,当我们拥有图像时,只需在其父级上调用 click ,该父级是所需的 anchor。希望对你有所帮助。
完整示例:
Option Explicit

' Add reference to Microsoft Internet Controls (SHDocVw)
' Add reference to Microsoft HTML Object Library

Sub Demo()

    Dim ie As SHDocVw.InternetExplorer
    Dim doc As MSHTML.HTMLDocument
    Dim url As String
    
    url = "file:///C:/Users/dusek/Documents/My Web Sites/mainpage.html"
    Set ie = New SHDocVw.InternetExplorer
    ie.Visible = True
    ie.navigate url

    While ie.Busy Or ie.readyState <> READYSTATE_COMPLETE
        DoEvents
    Wend
    
    Set doc = ie.document
    
    Dim iframeDoc As MSHTML.HTMLDocument
    Set iframeDoc = doc.frames("iframename").document
    If iframeDoc Is Nothing Then
        MsgBox "IFrame with name 'iframename' was not found."
        ie.Quit
        Exit Sub
    End If
    
    Dim img As MSHTML.HTMLImg
    Set img = iframeDoc.querySelector("div table[id='table1'] tbody tr td a[href^='https://stackoverflow.com'] img")
    If img Is Nothing Then
        MsgBox "Image element within iframe was not found."
        ie.Quit
        Exit Sub
    Else
        img.parentElement.Click
    End If
    
    ie.Quit
End Sub

主页HTML使用
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<!-- saved from url=(0016)http://localhost -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>x -</title>
</head>

<body>
<iframe name="iframename" src="iframe1.html">
</iframe>
</body>

</html>

使用IFrame HTML(保存为文件iframe1.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<!-- saved from url=(0016)http://localhost -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Untitled 2</title>
</head>

<body>
<div align="center"> 
    <table id="table1" style="border-collapse: collapse" width="700" cellspacing="0" cellpadding="0" border="0"> 
        <tbody>
            <tr>
                <td colspan="6"> &nbsp;</td>
            </tr> 
            <tr> 
                <td colspan="6"> 
                    <a href="https://dev59.com/8VcP5IYBdhLWcg3wFmcw">
                        <img src="img.gif" width="38" height="38" border="0" align="right">
                    </a>
                    <strong>x - </strong>
                </td>
            </tr> 
        </tbody>
    </table>
</div>

</body>

</html>

顺便提一下,框架也可以通过索引进行引用doc.frames(0).document。 感谢Paulo Bueno。

好的,成功地让它点击了gif!谢谢!但是... gif导出了一个包含iframe内数据的csv文件。导出为空... csv中没有任何内容,但我可以看到iframe中有。 - mojo3340
你需要看到什么才能解决这个问题?在DOM资源管理器/F12工具中有很多区域。 - mojo3340
当请求CSV文件时,您应该检查浏览器发送到服务器的Request。它在Network-Tab上。因此,您可以找出为什么手动单击时它能够工作,而编程单击时却无法工作。 - Daniel Dušek
当我说“锚点元素”时,我的意思是类似于上面的示例,其中你有“选择器a [href ^ ='https://stackoverflow.com']”。 - mojo3340
你能否在这里发布这些信息,例如屏幕截图?以某种被审查的形式,以便客户信息保持隐藏?我不知道原因是什么。也许是因为手动点击时会运行一些JavaScript,因为它对例如“mouse-up”事件做出反应(而在仅通过编程调用“click”时不存在该事件)? - Daniel Dušek
显示剩余8条评论

2
我想进一步阐述已经给出的答案。
对于Internet Explorer而言,你可能需要处理关于iframes的两种常见情况之一。
1. iframe的src受到同源策略限制:

如果 iframe 的 src 与落地页的来源不同,则由于 同源策略, 尝试访问它将导致访问被拒绝

解决方法:

考虑使用 Selenium 基础版来自动化不同的浏览器,如 Chrome,在那里允许 CORS/您可以切换到 iframe 并继续使用 iframe 文档。

示例:

Option Explicit
'download selenium https://github.com/florentbr/SeleniumBasic/releases/tag/v2.0.9.0
'Ensure latest applicable driver e.g. ChromeDriver.exe in Selenium folder
'VBE > Tools > References > Add reference to selenium type library
Public Sub Example()
    Dim d As WebDriver
    Const URL As String = "https://www.rosterresource.com/mlb-roster-grid/"
    Set d = New ChromeDriver
    With d
        .Start "Chrome"
        .get URL
        .SwitchToFrame .FindElementByCss("iframe") '< pass the iframe element as the identifier argument
        ' .SwitchToDefaultContent ''to go back to parent document.
        Stop '<== delete me later
        .Quit
    End With
End Sub

  1. iframe 的 src 不受同源策略限制:

解析:

分辨率:

方法已在之前的答案中详细说明。此外,您可以提取iframe的src并使用.Navigate2访问它。

.Navigate2 .document.querySelector("iframe").src

如果您只想处理 iframe 的内容,那么只需将初始的 .Navigate2 定位到 iframe src,而无需访问初始着陆页面。 示例:
Option Explicit
Public Sub NavigateUsingSrcOfIframe()
    Dim IE As New InternetExplorer
    With IE
        .Visible = True
        .Navigate2 "http://www.bursamalaysia.com/market/listed-companies/company-announcements/5978065"

        While .Busy Or .readyState < 4: DoEvents: Wend
        
        .Navigate2 .document.querySelector("iframe").src
        
        While .Busy Or .readyState < 4: DoEvents: Wend

        Stop '<== delete me later
        .Quit
    End With
End Sub

  1. ShadowRoot 中的 iframe

一个不太可能的情况可能是在影子根中有一个iframe。你应该真正拥有其中之一,而不是一个在另一个内部。 解析:

分辨率:

在这种情况下,您需要一个额外的访问器。

Element.shadowRoot.querySelector("iframe").contentDocument

其中,Element 是带有 shadowRoot 的父元素。仅当 shadowRootmode 设置为 Open 时,此方法才能正常运行。
附注:
使用 selenium 的基于 ExecuteScript 返回 shadowRoot 的示例代码,在此链接中提供:How Do I Access Elements in the Shadow DOM using Selenium in VBA?

0

除了之前提供的答案:

如果你愿意使用 DLL 并重写代码,你可以通过 VBA 运行微软的 Edge 浏览器(基于 Chrome 的浏览器),从而实现几乎任何你想要的功能。需要注意的是,访问 DOM 是通过 JavaScript 而不是对象(例如 Dim IE As New InternetExplorer)来完成的。查看 VBA 示例,你就能理解。

https://github.com/peakpeak-github/libEdge

附注:还包括C#和C++的示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接