- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试从下面提到的网站复制数据,我需要各种尺寸、价格、设施、特价、预订。我在代码下方构图,但我能够正确复制元素。第一件事只有三个元素在处理重复,我也没有得到 Amenities 和 Reserve 的结果。有人可以看看这个吗?
Sub text()
Dim ie As New InternetExplorer, ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Unit Data")
With ie
.Visible = True
.Navigate2 "https://www.safeandsecureselfstorage.com/self-storage-lake-villa-il-86955"
While .Busy Or .readyState < 4: DoEvents: Wend
Sheets("Unit Data").Select
Dim listings As Object, listing As Object, headers(), results()
Dim r As Long, list As Object, item As Object
headers = Array("size", "features", "Specials", "Price", "Reserve")
Set list = .document.getElementsByClassName("units_table")
'.unit_size medium, .features, .Specials, .price, .Reserve
Dim rowCount As Long
rowCount = .document.querySelectorAll(".tab_container li").Length
ReDim results(1 To rowCount, 1 To UBound(headers) + 1)
For Each listing In list
For Each item In listing.getElementsByClassName("unitinfo even")
r = r + 1
results(r, 1) = listing.getElementsByClassName("size secondary-color-text")(0).innerText
results(r, 2) = listing.getElementsByClassName("amenities")(0).innerText
results(r, 3) = listing.getElementsByClassName("offer1")(0).innerText
results(r, 4) = listing.getElementsByClassName("rate_text primary-color-text rate_text--clear")(0).innerText
results(r, 5) = listing.getElementsByClassName("reserve")(0).innerText
Next
Next
ws.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
ws.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
.Quit
End With
Worksheets("Unit Data").Range("A:G").Columns.AutoFit
End Sub
最佳答案
tl;博士;
为答案的长度提前(向某些人)道歉,但我想我会接受这个
详细说明正在发生的事情的教学时刻。
我使用的整体方法与您的代码相同:找到一个 css 选择器来隔离行(尽管在不同的选项卡中,小、中、大实际上仍然存在于页面上):
Set listings = html.querySelectorAll(".unitinfo")
以上生成行。和以前一样,我们将其转储到新的
HTMLDocument
中所以我们可以利用
querySelector/querySelectorAll
方法。
5x5</TD> <TD class=features>
<DIV id=a5x5-1 class="icon a5x5">
<DIV class=img><IMG src="about:/core/resources/images/units/5x5_icon.png"></DIV>
<DIV class=display>
<P>More Information</P></DIV></DIV>
<SCRIPT type=text/javascript>
// Refine Search
//
$(function() {
$("#a5x5-1").tooltip({
track: false,
delay: 0,
showURL: false,
left: 5,
top: 5,
bodyHandler: function () {
return " <div class=\"tooltip\"> <div class=\"tooltop\"></div> <div class=\"toolmid clearfix\"> <div class=\"toolcontent\"> <div style=\"text-align:center;width:100%\"> <img alt=\"5 x 5 storage unit\" src=\"/core/resources/images/units/5x5.png\" /> </div> <div class=\"display\">5 x 5</div> <div class=\"description\">Think of it like a standard closet. Approximately 25 square feet, this space is perfect for about a dozen boxes, a desk and chair, and a bicycle.</div> </div> <div class=\"clearfix\"></div> </div> <div class=\"toolfoot\"></div> <div class=\"clearfix\"></div> </div> "}
});
});
</SCRIPT>
</TD><TD class=rates>
<DIV class="discount_price secondary-color-text standard_price--left">
<DIV class=price_text>Web Rate: </DIV>
<DIV class="rate_text primary-color-text rate_text--clear">$39.00 </DIV></DIV>
<SCRIPT>
$( document ).ready(function() {
$('.units_table tr.unitinfo').each(function(index, el) {
if ($(this).find('.standard_price').length != '' && $(this).find('.discount_price').length != '') {
$(this).parents('.units_table').addClass('both');
$(this).addClass('also-both');
$(this).find('.rates').addClass('rates_two_column');
}
});
});
</SCRIPT>
</TD><TD class=amenities>
<DIV title="Temperature Controlled" class="amenity_icon icon_climate"></DIV>
<DIV title="Interior Storage" class="amenity_icon icon_interior"></DIV>
<DIV title="Ground Floor" class="amenity_icon icon_ground_floor"></DIV></TD><TD class=offers>
<DIV class=offer1>Call for Specials </DIV>
<DIV class=offer2></DIV></TD><TD class=reserve><A id=5x5:39:00000000 class="facility_call_to_reserve cta_call primary-color primary-hover" href="about:blank#" rel=nofollow>Call </A></TD>
html2
中都会有类似的 html。多变的。如果您有疑问,请查看上面显示的函数中的 javascript:
$('.units_table tr.unitinfo').each(function(index, el)
它使用相同的选择器(但也指定了父表类和元素类型(
tr
))。基本上,正在为表中的每一行调用该函数。
td
标签正在被删除(我已经看到这个缺少父
<table>
我认为标签)所以对于大小,而不是按类抓取,我正在寻找结束标签的开始并将字符串提取到那里。我通过传递由
Instr 给出的返回值来做到这一点。 (其中 < 在字符串中找到)-1 到
Left$
(打字)功能。
results(r, 1) = Left$(html2.body.innerHTML, InStr(html2.body.innerHTML, "<") - 1)
这将返回
5x5
.
$("#a5x5-1").tooltip
- 告诉它目标在哪里,然后函数的返回语句提供了带有
div
的 html , 与类(class)
description
,包含我们想要的文本。由于我们没有使用浏览器,而且我使用的是 64 位窗口,因此我无法评估此脚本,但我可以使用
split
提取
"description\">
之间的字符串(描述)和收盘相关的开始
div
标签:
results(r, 2) = Split(Split(html2.querySelector("SCRIPT").innerHTML, """description\"">")(1), "</div>")(0)
这将返回:
results(r, 3) = Replace$(html2.querySelector(".price_text").innerText, ":", vbNullString)
results(r, 4) = Trim$(html2.querySelector(".rate_text").innerText)
返回(分别)
<TD class=amenities>
<DIV title="Temperature Controlled" class="amenity_icon icon_climate"></DIV>
<DIV title="Interior Storage" class="amenity_icon icon_interior"></DIV>
<DIV title="Ground Floor" class="amenity_icon icon_ground_floor"></DIV></TD>
td
有一个类
amenities
, 有 child
div
具有复合类名的元素;后者在每种情况下都用作便利设施类型的标识符,例如
icon_climate
.
amenity_icon icon_climate
(当转换为
.amenity_icon.icon_climate
的适当 css 选择器时,复合类需要将“”替换为“.”)到相关的描述。您可以浏览 php 文件
here .
function LoadTooltips() {
$(".units_table .amenity_icon.icon_climate").tooltip({
track: false,
delay: 0,
showURL: false,
left: -126,
top: -100,
bodyHandler: function () {
return "<div class=\"sidebar_tooltip\"><h4>Temperature Controlled</h4><p>Units are heated and/or cooled. See manager for details.</p></div>"
}
});
LoadTooltips
. CSS 类选择器用于定位每个图标:
$(".units_table .amenity_icon.icon_climate").tooltip
我们有指定返回文本的 bodyhandler:
bodyHandler: function () {
return "<div class=\"sidebar_tooltip\"><h4>Temperature Controlled</h4><p>Units are heated and/or cooled. See manager for details.</p></div>"
.amenity_icon.icon_climate
:我们使用它来将 php 文件描述映射到我们行中的便利设施图标的类名。 CSS 选择器 Temperature Controlled
;内h4
工具提示函数返回文本的标签。 简短说明 Units are heated and/or cooled. See manager for details.
;内p
工具提示函数返回文本的标签。 详细说明 GetMatches
和
GetAmenitiesDescriptions
,使用正则表达式为每个图标提取所有重复项,并返回一个以 css 选择器为键的字典,以及短
description : long description
作为值(value)。
Set icons = html2.querySelectorAll(".amenity_icon")
我使用字典根据图标的类名返回工具提示说明
For icon = 0 To icons.Length - 1 'use class name of amenity to look up description
amenitiesInfo(icon) = amenitiesDescriptions("." & Replace$(icons.item(icon).className, Chr$(32), "."))
Next
然后我将描述加入
vbNewLine
以确保输出在输出单元格内的不同行上。
|
(或)语法,所以我在一个列表中返回所有匹配的模式。
arr = GetMatches(re, s, "(\.amenity_icon\..*)""|<h4>(.*)<\/h4>|<p>(.*)<\/p>")
因为我需要不同的子匹配(0,1 或 2 又名 css 类选择器,短描述,长描述)我使用
Select Case i mod 3
, 带计数器变量
i
, 提取适当的子匹配。
Offer2
未填充,因此您可以删除。
results(r, 6) = html2.querySelector(".offer1").innerText
results(r, 7) = html2.querySelector(".offer2").innerText
返回(分别):
results
;然后写入
Sheet1
一气呵成。我可以看到一些小的改进,但这很快。
Option Explicit
Public Sub GetInfo()
Dim ws As Worksheet, html As HTMLDocument, s As String, amenitiesDescriptions As Object
Const URL As String = "https://www.safeandsecureselfstorage.com/self-storage-lake-villa-il-86955"
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set html = New HTMLDocument
Set amenitiesDescriptions = GetAmenitiesDescriptions
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", URL, False
.setRequestHeader "User-Agent", "Mozilla/5.0"
.send
s = .responseText
html.body.innerHTML = s
Dim headers(), results(), listings As Object, amenities As String
headers = Array("Size", "Description", "RateType", "Price", "Amenities", "Offer1", "Offer2")
Set listings = html.querySelectorAll(".unitinfo")
Dim rowCount As Long, numColumns As Long, r As Long, c As Long
Dim icons As Object, icon As Long, amenitiesInfo(), i As Long, item As Long
rowCount = listings.Length
numColumns = UBound(headers) + 1
ReDim results(1 To rowCount, 1 To numColumns)
Dim html2 As HTMLDocument
Set html2 = New HTMLDocument
For item = 0 To listings.Length - 1
r = r + 1
html2.body.innerHTML = listings.item(item).innerHTML
results(r, 1) = Left$(html2.body.innerHTML, InStr(html2.body.innerHTML, "<") - 1)
results(r, 2) = Split(Split(html2.querySelector("SCRIPT").innerHTML, """description\"">")(1), "</div>")(0)
results(r, 3) = Replace$(html2.querySelector(".price_text").innerText, ":", vbNullString)
results(r, 4) = Trim$(html2.querySelector(".rate_text").innerText)
Set icons = html2.querySelectorAll(".amenity_icon")
ReDim amenitiesInfo(0 To icons.Length - 1)
For icon = 0 To icons.Length - 1 'use class name of amenity to look up description
amenitiesInfo(icon) = amenitiesDescriptions("." & Replace$(icons.item(icon).className, Chr$(32), "."))
Next
amenities = Join$(amenitiesInfo, vbNewLine) 'place each amenity description on a new line within cell when written out
results(r, 5) = amenities
results(r, 6) = html2.querySelector(".offer1").innerText
results(r, 7) = html2.querySelector(".offer2").innerText
Next
ws.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
ws.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
End With
End Sub
Public Function GetAmenitiesDescriptions() As Object 'retrieve amenities descriptions from php file on server
Dim s As String, dict As Object, re As Object, i As Long, arr() 'keys based on classname, short desc, full desc
' view regex here: https://regex101.com/r/bII5AL/1
Set dict = CreateObject("Scripting.Dictionary")
Set re = CreateObject("vbscript.regexp")
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://www.safeandsecureselfstorage.com/core/resources/js/src/common.tooltip.php", False
.setRequestHeader "User-Agent", "Mozilla/5.0"
.send
s = .responseText
arr = GetMatches(re, s, "(\.amenity_icon\..*)""|<h4>(.*)<\/h4>|<p>(.*)<\/p>")
For i = LBound(arr) To UBound(arr) Step 3 'build up lookup dictionary for amenities descriptions
dict(arr(i)) = arr(i + 1) & ": " & arr(i + 2)
Next
End With
Set GetAmenitiesDescriptions = dict
End Function
Public Function GetMatches(ByVal re As Object, inputString As String, ByVal sPattern As String) As Variant
Dim matches As Object, iMatch As Object, s As String, arrMatches(), i As Long
With re
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = sPattern
If .test(inputString) Then
Set matches = .Execute(inputString)
ReDim arrMatches(0 To matches.Count - 1)
For Each iMatch In matches
Select Case i Mod 3
Case 0
arrMatches(i) = iMatch.SubMatches.item(0)
Case 1
arrMatches(i) = iMatch.SubMatches.item(1)
Case 2
arrMatches(i) = iMatch.SubMatches.item(2)
End Select
i = i + 1
Next iMatch
Else
ReDim arrMatches(0)
arrMatches(0) = vbNullString
End If
End With
GetMatches = arrMatches
End Function
关于html - 按类和标签名称的网页抓取元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55761018/
我正在尝试做这样的事情:Name[i] = "Name"+ (i+1) 在 forloop 中,这样数组的值将是:Name[0] = Name1,Name[1] = Name2,Name[2] = N
我读了here,在GSP中我们可以这样写: ${params.action} 从GSP中,我们可以使用${params.action}作为参数调用Javascript函数(请参阅here)。 是否有其
我的问题:非常具体。我正在尝试想出解析以下文本的最简单方法: ^^domain=domain_value^^version=version_value^^account_type=account_ty
我创建了一条与此类似的路线: Router::connect("/backend/:controller/:action/*"); 现在我想将符合此模式的每个 Controller 路由重命名为类似
我在 Visual Studio 2013 项目中收到以下警告: SQL71502 - Procedure has an unresolved reference to object 最佳答案 这可以
任何人都可以指导我使用名称/值 .NET 集合或 .NET 名称/值字典以获得最佳性能吗?请问最好的方法是什么?我的应用程序是 ASP.NET、WCF/WF Web 应用程序。每个集合应该有 10 到
我在 Zend Framework 2 中有一个默认模块: namespace Application\Controller; use Zend\Mvc\Controller\AbstractActi
这是表格: 关于javascript - 在 javascript 中,这是一个有效的结构吗? : document. 名称.名称.值?,我们在Stack Overflow上找到一个类似的
HtmlHelper.ActionLink(htmlhelper,string linktext,string action) 如何找出正确的路线? 如果我有这个=> HtmlHelper.Actio
我需要一些有关如何将 Controller 定义传递给嵌套在 outer 指令中的 inner 指令的帮助。请参阅http://plnkr.co/edit/Om2vKdvEty9euGXJ5qan一个
请提出一个数据结构来表示内存中的记录列表。每条记录由以下部分组成: 用户名 积分 排名(基于积分)- 可选字段- 可以存储在记录中或可以动态计算 数据结构应该支持高效实现以下操作: Insert(re
错误 : 联合只能在具有兼容列类型的表上执行。 结构(层:字符串,skyward_number:字符串,skyward_points:字符串)<> 结构(skyward_number:字符串,层:字符
我想要一个包含可变数量函数的函数,但我希望在实际使用它们之前不要对它们求值。我可以使用 () => type 语法,但我更愿意使用 => type 语法,因为它似乎是为延迟评估而定制的。 当我尝试这样
我正在编写一个 elisp 函数,它将给定键永久绑定(bind)到当前主要模式的键盘映射中的给定命令。例如, (define-key python-mode-map [C-f1] 'pytho
卡在R中的错误上。 Error in names(x) <- value : 'names' attribute must be the same length as the ve
我有字符串,其中包含名称,有时在字符串中包含用户名,后跟日期时间戳: GN1RLWFH0546-2020-04-10-18-09-52-563945.txt JOHN-DOE-2020-04-10-1
有人知道为什么我会收到此错误吗?这显示将我的项目升级到新版本的Unity3d之后。 Error CS0103: The name `Array' does not exist in the curre
由于 Embarcadero 的 NNTP 服务器从昨天开始就停止响应,我想我可以在这里问:我使用非数据库感知网格,我需要循环遍历数据集以提取列数、它们的名称、数量行数以及每行中每个字段的值。 我知道
在构建Android应用程序的子项目中,我试图根据根build.gradle中的变量设置版本代码/名称。 子项目build.gradle: apply plugin: 'com.android.app
示例用例: 我有一个带有属性“myProperty”的对象,具有 getter 和 setter(自 EcmaScript 5 起支持“Property Getters 和 Setters”:http
我是一名优秀的程序员,十分优秀!