首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在querySelectorAll中对mshtml.HTMLDocumentClass对象使用PowerShell会导致崩溃

在querySelectorAll中对mshtml.HTMLDocumentClass对象使用PowerShell会导致崩溃
EN

Stack Overflow用户
提问于 2016-05-12 20:12:17
回答 2查看 6.2K关注 0票数 10

我正试图通过PowerShell进行一些网络抓取,因为我最近发现,在没有太多麻烦的情况下这样做是可能的。

一个很好的起点是获取HTML,使用Get成员,看看我能从那里做些什么,如下所示:

代码语言:javascript
复制
$html = Invoke-WebRequest "https://www.google.com"
$html.ParsedHtml | Get-Member

我可以使用的获取特定元素的方法如下:

代码语言:javascript
复制
getElementById()
getElementsByName()
getElementsByTagName()

例如,我可以在文档中获得第一个IMG标记,如下所示:

代码语言:javascript
复制
$html.ParsedHtml.getElementsByTagName("img")[0]

然而,在对我是否可以使用CSS选择器或XPath做了更多的研究之后,我发现有一些未列出的方法可用,因为我们只是使用记录在这里文档对象。

代码语言:javascript
复制
querySelector()
querySelectorAll()

因此,与其做:

代码语言:javascript
复制
$html.ParsedHtml.getElementsByTagName("img")[0]

我能做到:

代码语言:javascript
复制
$html.ParsedHtml.querySelector("img")

所以我希望能做到:

代码语言:javascript
复制
$html.ParsedHtml.querySelectorAll("img")

...in命令获取所有IMG元素。我找到的所有文档和我所做的googling都支持这一点。但是,在我的所有测试中,该函数使调用进程崩溃,并报告事件日志(0xc0000374)中的堆损坏异常代码。

我正在Windows 10的PowerShell 10 x64上使用x64 5。我已经在一个Win10 x64 VM中尝试过它,它是一个干净的构建,并且刚刚修复。我还在升级到Win7 5的x64中尝试过,在PowerShell 5之前我还没有尝试过它,因为我们这里的所有系统都已经升级了,但我可能会有时间为测试准备一个新的普通VM。

以前有人参与过这个问题吗?到目前为止,我所有的研究都是死胡同。有替代querySelectorAll的方法吗?我需要在不可预测的布局中具有可预测的标记集的页面,并且可能没有分配给标记的I或类,所以我希望能够使用允许结构/嵌套/通配符的选择器。

我也尝试在PowerShell中使用PowerShell COM对象,结果是一样的,除了PowerShell崩溃Internet崩溃之外。这实际上是我最初的方法,下面是代码:

代码语言:javascript
复制
# create browser object
$ie = New-Object -ComObject InternetExplorer.Application

# make browser visible for debugging, otherwise this isn't necessary for function
$ie.Visible = $true

# browse to page
$ie.Navigate("https://www.google.com")
# wait till browser is not busy
Do { Start-Sleep -m 100 } Until (!$ie.Busy)

# this works
$ie.document.getElementsByTagName("img")[0]

# this works as well
$ie.document.querySelector("img")

# blow it up
$ie.document.querySelectorAll("img")

# we wanna quit the process, but since we blew it up we don't really make it here
$ie.Quit()

希望我没有违反任何规则和这篇文章是有意义的,是相关的,谢谢。

更新

我测试了早期的PowerShell版本。v2-v4使用InternetExplorer.Application COM方法崩溃。使用调用-WebRequest方法v3-4崩溃,v2不支持它。

EN

回答 2

Stack Overflow用户

发布于 2016-06-06 17:44:55

我也遇到了这个问题,还有贴在reddit上。我认为,当Powershell试图枚举由HTML对象返回的querySelectorAll()时,问题就会发生。相同的对象是由childNodes()返回的,它可以被PS枚举,所以我猜有一些为.ParsedHtml.childNodes编写的胶水代码,而不是.ParsedHtml.querySelectorAll()。试图为对象获取选项卡完整帮助的Intellisense也会触发崩溃。

不过,我找到了绕过它的方法!只需直接访问本机DOM方法.item().length,并将节点对象发出到PowerShell数组中。下面的代码从/r/Powershell中提取最新的帖子页面,通过querySelectorAll()获取post列表锚点,然后使用原生DOM方法将它们手动枚举到Powershell本机数组中。

代码语言:javascript
复制
$Result = Invoke-WebRequest -Uri "https://www.reddit.com/r/PowerShell/new/"

$NodeList = $Result.ParsedHtml.querySelectorAll("#siteTable div div p.title a")

$PsNodeList = @()
for ($i = 0; $i -lt $NodeList.Length; $i++) { 
    $PsNodeList += $NodeList.item($i)
}

$PsNodeList | ForEach-Object {
    $_.InnerHtml
}

编辑.Length似乎工作大写或小写。我原以为DOM是区分大小写的,所以要么有些东西在帮助翻译,要么我误解了一些东西。另外,CSS选择器正在抓取源链接(主要是self.PowerShell),但它是我的CSS选择器逻辑错误,而不是querySelectorAll()的问题。请注意,querySelectorAll()的结果不是活动的,所以修改它们不会修改原始DOM。我还没有尝试修改它们或使用它们的方法,但显然我们至少可以获得.InnerHtml

编辑2:下面是一个更通用的包装器函数:

代码语言:javascript
复制
function Get-FixedQuerySelectorAll {
    param (
        $HtmlWro,
        $CssSelector
    )
    # After assignment, $NodeList will crash powershell if enumerated in any way including Intellisense-completion while coding!
    $NodeList = $HtmlWro.ParsedHtml.querySelectorAll($CssSelector)

    for ($i = 0; $i -lt $NodeList.length; $i++) {
        Write-Output $NodeList.item($i)
    }
}

$HtmlWro是一个HTML响应对象,是Invoke-WebReqest的输出。我最初试图通过.ParsedHtml,但后来它在执行任务时崩溃了。这样做会返回Powershell数组中的节点。

票数 4
EN

Stack Overflow用户

发布于 2016-12-06 18:30:58

@午夜的解决方案以前对我来说很好,但现在它在调用$NodeList.item($i)时抛出了$NodeList.item($i)

我找到了以下解决办法:

代码语言:javascript
复制
function Invoke-QuerySelectorAll($node, [string] $selector)
{
    $nodeList = $node.querySelectorAll($selector)
    $nodeListType = $nodeList.GetType()
    $result = @()
    for ($i = 0; $i -lt $nodeList.length; $i++)
    {
        $result += $nodeListType.InvokeMember("item", [System.Reflection.BindingFlags]::InvokeMethod, $null, $nodeList, $i)
    }
    return $result
}

这个也适用于New-Object -ComObject InternetExplorer.Application

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37196558

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档