使用VBA访问iframe中的对象

时间:2017-07-04 09:48:14

标签: html excel vba iframe web-scraping

重点:

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

  • 使用getElementsByName

  • 登录网站
  • 选择将生成的报告的参数(使用getelementsby ...)

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

需要注意的重要事项 - 网站是客户端

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

  

点击将数据集导出到csv的iframe中的gif图片

我尝试了以下内容:

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

只要i包含read_html,脚本就会返回以下错误:

if(grepl(“&lt; |&gt;”,x)){:参数长度为零

时出错

我怀疑这是指字符(0)

在这里感谢任何指导!

非常感谢,

  

HTML

<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>

3 个答案:

答案 0 :(得分:7)

iframes有时会很棘手。基于您提供的html我创建了此示例。哪个在本地工作,但它也适合你吗?

要访问IFrame,可以使用frames集合。希望您知道name的{​​{1}}?

IFrame

然后转到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'],其anchor属性以给定文本开头。 The ^ denotes the beginning

然后,当我们将图像简单地调用其父级href时,就是所需的click。 HTH

完整示例:

anchor
  

主页HTML使用

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
  

使用IFrame 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>

iframe1.html

答案 1 :(得分:1)

我想我会扩展已经给出的答案。

对于Internet Explorer,您可能需要处理两种常见的iframe情况之一。

1)iframe的src受相同的原产地政策限制:

iframe src与着陆页的来源不同,在这种情况下,由于same origin policy,尝试访问它会导致拒绝访问

解决方案:

考虑使用selenium basic自动执行允许CORS的其他浏览器(例如Chrome),您可以切换到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

2)iframe的src不受相同的原产地政策限制:

解决方案:

已在答案中详述的方法。此外,您可以提取要访问的iframe.Navigate2的src

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

如果您只想使用iframe的内容,则只需进行初始.Navigate2iframe 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

3)ShadowRoot中的iframe

不太可能出现的情况可能是shadowroot中的iframe。您确实应该有one or the other,但彼此之间没有一个。

解决方案:

在这种情况下,您需要

的附加访问器。
Element.shadowRoot.querySelector("iframe").contentDocument

其中Element是附加了shadowRoot的父元素。仅当shadowRoot mode设置为Open时,此方法才有效。

示例:

要关注

答案 2 :(得分:0)

补充给出的答案:

如果您可以使用 DLL 并重写代码,则可以使用 VBA 运行 Microsoft 的 Edge 浏览器(基于 Chrome 的浏览器)。有了它,你几乎可以做任何你想做的事情。但是请注意,对 DOM 的访问是由 javascript 执行的,而不是由像 Dim IE As New InternetExplorer 这样的对象执行的。查看 VBA 示例,您就会掌握。

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

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