【问题标题】:Unable to parse some links lying within an iframe无法解析 iframe 中的某些链接
【发布时间】:2018-07-21 01:42:06
【问题描述】:

我使用 IE 在 vba 中编写了一个脚本来解析网页中的一些链接。问题是链接在iframe 内。我以某种方式调整了我的代码,以便脚本首先在该iframe 中找到一个链接,然后导航到该新页面并从那里解析所需的内容。如果我这样做,那么我可以获得所有链接。

网页网址:weblink

成功的方法(工作之一):

Sub Get_Links()
    Dim IE As New InternetExplorer, HTML As HTMLDocument
    Dim elem As Object, post As Object

    With IE
        .Visible = True
        .navigate "put here the above link"
        While .Busy = True Or .readyState < 4: DoEvents: Wend
        Set elem = .document.getElementById("compInfo")   #it is within iframe
        .navigate elem.src
        While .Busy = True Or .readyState < 4: DoEvents: Wend
        Set HTML = .document
    End With

    For Each post In HTML.getElementsByClassName("news")
        With post.getElementsByTagName("a")
         If .Length Then R = R + 1: Cells(R, 1) = .Item(0).href
        End With
    Next post
    IE.Quit
End Sub 

我看到很少有网站在iframe 中不存在此类链接,因此,我无法选择使用任何链接来追踪内容。

如果您通过跟踪链接查看以下方法,那么您会注意到我已经解析了来自Iframe 内的网页的内容。 Iframe 中没有这样的链接来导航到新网页以定位内容。所以,我改用了contentWindow.document,发现它完美无缺。

从另一个站点解析Iframe内容的工作代码链接: contentWindow approach

但是,我的问题是:为什么我应该导航到一个新网页来收集链接,因为我可以在登录页面中看到内容?我尝试使用contentWindow.document,但它给了我访问被拒绝的错误。如何像上面那样使用contentWindow.document 使下面的代码工作?

我试过这样但它抛出拒绝访问错误:

Sub Get_Links()
    Dim IE As New InternetExplorer, HTML As HTMLDocument
    Dim frm As Object, post As Object

    With IE
        .Visible = True
        .Navigate "put here the above link"
        While .Busy = True Or .readyState < 4: DoEvents: Wend
        Set HTML = .document
    End With

    ''the code breaks when it hits the following line "access denied error"

    Set frm = HTML.getElementById("compInfo").contentWindow.document

    For Each post In frm.getElementsByClassName("news")
        With post.getElementsByTagName("a")
         If .Length Then R = R + 1: Cells(R, 1) = .Item(0).href
        End With
    Next post
    IE.Quit
End Sub

我附上了一张图片,让您知道我在寻找哪些链接(它们用铅笔标记)。

这些是在其中找到一个这样的链接(我想抓取)的元素:

<div class="news">
    <span class="news-date_time"><img src="images/arrow.png" alt="">19 Jan 2018 00:01</span>
    <a style="color:#5b5b5b;" href="/HomeFinancial.aspx?&amp;cocode=INE117A01022&amp;Cname=ABB-India-Ltd&amp;srno=17019039003&amp;opt=9">ABB India Limited - Press Release</a>
 </div>

我想抓取的那个页面的链接图片:

从创建此线程的第一天起,我就严格要求不要使用此 url http://hindubusiness.cmlinks.com/Companydetails.aspx?cocode=INE117A01022 来定位数据。我从这个main_page_link 请求了任何解决方案,而没有触及 iframe 中的链接。但是,每个人都在尝试提供我已经在我的帖子中展示的解决方案。那我悬赏是为了什么?

【问题讨论】:

  • 为什么不直接将 XHR 设置为 URL http://hindubusiness.cmlinks.com/Companydetails.aspx?cocode=INE117A01022 并从响应中提取链接?
  • 在我完成了您在我的帖子中已经描述过的建议之后,我开始了这篇文章。我希望任何解决方案都无需使用 iframe 中可用的链接导航到新网页。由于所需的链接在第一页中可用,我想应该有任何方法可以在不导航到新页面的情况下获取这些链接。
  • 我不建议你导航,我建议做XHR而不是使用IE。
  • @QHarr 我上面指出的链接中有一些示例,显示两个页面是否具有相同的来源。通常&lt;iframe&gt; src URL 和包含页面 URL 应该具有相同的协议和主机。动态加载的内容不会影响政策。

标签: vba excel iframe web-scraping internet-explorer-11


【解决方案1】:

您可以在浏览器中看到&lt;iframe&gt; 中的链接,但由于Same-origin policy 而无法以编程方式访问它们。

这个例子展示了如何使用 XHR 和 RegEx 检索链接:

Option Explicit

Sub Test()

    Dim sContent As String
    Dim sUrl As String
    Dim aLinks() As String
    Dim i As Long

    ' Retrieve initial webpage HTML content via XHR
    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", "https://www.thehindubusinessline.com/stocks/abb-india-ltd/overview/", False
        .Send
        sContent = .ResponseText
    End With
    'WriteTextFile sContent, CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\tmp\tmp.htm", -1
    ' Extract target iframe URL via RegEx
    With CreateObject("VBScript.RegExp")
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        ' Process all a within div.news
        .Pattern = "<iframe[\s\S]*?src=""([^""]*?Companydetails[^""]*)""[^>]*>"
        sUrl = .Execute(sContent).Item(i).SubMatches(0)
    End With
    ' Retrieve iframe HTML content via XHR
    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", sUrl, False
        .Send
        sContent = .ResponseText
    End With
    'WriteTextFile sContent, CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\tmp\tmp.htm", -1
    ' Parse links via XHR
    With CreateObject("VBScript.RegExp")
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        ' Process all anchors within div.news
        .Pattern = "<div class=""news"">[\s\S]*?href=""([^""]*)"
        With .Execute(sContent)
            ReDim aLinks(0 To .Count - 1)
            For i = 0 To .Count - 1
                aLinks(i) = .Item(i).SubMatches(0)
            Next
        End With
    End With
    Debug.Print Join(aLinks, vbCrLf)

End Sub

通常不建议将 RegEx 用于 HTML 解析,因此 there is disclaimer。在这种情况下处理的数据非常简单,这就是使用 RegEx 对其进行解析的原因。

我的输出如下:

/HomeFinancial.aspx?&cocode=INE117A01022&Cname=ABB-India-Ltd&srno=17047038016&opt=9
/HomeFinancial.aspx?&cocode=INE117A01022&Cname=ABB-India-Ltd&srno=17046039003&opt=9
/HomeFinancial.aspx?&cocode=INE117A01022&Cname=ABB-India-Ltd&srno=17045039006&opt=9
/HomeFinancial.aspx?&cocode=INE117A01022&Cname=ABB-India-Ltd&srno=17043039002&opt=9
/HomeFinancial.aspx?&cocode=INE117A01022&Cname=ABB-India-Ltd&srno=17043010019&opt=9

我还尝试使用命令将&lt;iframe&gt; 的内容从 IE 复制到剪贴板(以便进一步粘贴到工作表):

IE.ExecWB OLECMDID_SELECTALL, OLECMDEXECOPT_DODEFAULT
IE.ExecWB OLECMDID_COPY, OLECMDEXECOPT_DODEFAULT

但实际上该命令选择并复制主文档,不包括框架,除非我手动单击框架。因此,如果可以从 VBA 复制单击框架,则可能会应用此方法(.focus.click 等框架节点方法没有帮助)。

【讨论】:

    【解决方案2】:

    这样的事情应该可以工作。他们的关键是要意识到iFrame 在技术上是另一个Document。查看您列出的页面上的iFrame,您可以轻松地使用网络请求来获取您需要的数据。如前所述,您收到错误的原因是同源策略。您可以写一些东西来获取iFramesrc,然后像我在下面显示的那样执行网络请求,或者,使用IE 抓取页面,获取src,然后加载看起来像你所做的那样的页面。

    我建议使用网络请求方法,Internet Explorer 会变得烦人,速度很快。

    代码

    Public Sub SOExample()
        Dim html     As Object 'To store the HTML content
        Dim Elements As Object 'To store the anchor collection
        Dim Element  As Object 'To iterate the anchor collection
        Set html = CreateObject("htmlFile")
    
        With CreateObject("MSXML2.XMLHTTP")
            'Navigate to the source of the iFrame, it's another page
            'View the source for the iframe. Alternatively -
            'you could navigate to this page and use IE to scrape it
            .Open "GET", "https://stocks.thehindubusinessline.com/Companydetails.aspx?&cocode=INE117A01022"
            .send ""
    
            'See if the request was ok, exit it there was an error
            If Not .Status = 200 Then Exit Sub
    
            'Assign the page's HTML to an HTML object
            html.body.InnerHTML = .responseText
            Set Elements = html.body.document.getElementByID("hmstockchart_CompanyNews1_updateGLVV")
            Set Elements = Elements.getElementsByTagName("a")
    
            For Each Element In Elements
                'Print out the data to the Immediate window
                Debug.Print Element.InnerText
            Next
    
        End With
    End Sub
    

    结果


    ABB India Limited - AGM/Book Closure
    Board of ABB India recommends final dividend
    ABB India to convene AGM
    ABB India to pay dividend
    ABB India Limited - Outcome of Board Meeting
    More ?
    

    【讨论】:

      【解决方案3】:

      大家建议的简单解决方法是直接去链接。这将使 IFRAME 脱离图片,并且您可以更轻松地循环链接。但如果你仍然不喜欢这种方法,那么你需要深入一点。

      下面是我很久以前在 VB.NET 中编写的库中的一个函数

      https://github.com/tarunlalwani/ScreenCaptureAPI/blob/2646c627b4bb70e36fe2c6603acde4cee3354b39/Source%20Code/ScreenCaptureAPI/ScreenCaptureAPI/ScreenCapture.vb#L803

      Private Function _EnumIEFramesDocument(ByVal wb As HTMLDocumentClass) As Collection
          Dim pContainer As olelib.IOleContainer = Nothing
          Dim pEnumerator As olelib.IEnumUnknown = Nothing
          Dim pUnk As olelib.IUnknown = Nothing
          Dim pBrowser As SHDocVW.IWebBrowser2 = Nothing
          Dim pFramesDoc As Collection = New Collection
      
          _EnumIEFramesDocument = Nothing
      
          pContainer = wb
      
          Dim i As Integer = 0
      
          ' Get an enumerator for the frames
          If pContainer.EnumObjects(olelib.OLECONTF.OLECONTF_EMBEDDINGS, pEnumerator) = 0 Then
      
              pContainer = Nothing
      
              ' Enumerate and refresh all the frames
              Do While pEnumerator.Next(1, pUnk) = 0
      
                  On Error Resume Next
      
                  ' Clear errors
                  Err.Clear()
      
                  ' Get the IWebBrowser2 interface
                  pBrowser = pUnk
      
                  If Err.Number = 0 Then
                      pFramesDoc.Add(pBrowser.Document)
                      i = i + 1
                  End If
      
              Loop
      
              pEnumerator = Nothing
      
          End If
      
          _EnumIEFramesDocument = pFramesDoc
      End Function
      

      所以基本上这是一个低于 C++ 版本的 VB.NET 版本

      Accessing body (at least some data) in a iframe with IE plugin Browser Helper Object (BHO)

      现在您只需将其移植到 VBA。您可能遇到的唯一问题是找到olelib 参考。其余大部分都与 VBA 兼容

      所以一旦你得到对象数组,你会找到属于你的框架的那个,然后你就可以找到那个了

      frames = _EnumIEFramesDocument(IE)
      frames.Item(1).document.getElementsByTagName("A").length
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-04-08
        • 1970-01-01
        • 1970-01-01
        • 2019-02-03
        • 2011-06-18
        • 2016-04-26
        • 2014-12-25
        相关资源
        最近更新 更多