XHR:
Вся информация доступна через XMLHTTP (XHR) запрос - намного быстрее, чем открытие браузера.
Сначала я получаю количество строк с помощью селектора css .main li[class]
.
"."
- это селектор класса , li
- селектор типа , а [class]
- селектор атрибута . Пробел, " "
, между ними - комбинатор-потомок . Это указывает, что я хочу получить все li
элементы тега / типа с атрибутом класса, имеющим родителя, чье имя класса main
.
Это соответствует следующему:

Как видите, это дает мне счетчик строк; количество родительских элементов li
для получения информации для результирующего набора.
Эта коллекция li элементов возвращается в виде nodeList querySelectorAll
. Я не могу зациклить этот список, применяя getElementsByClassName
/ querySelector
к отдельным узлам, поскольку элементы li
не предоставляют методов, которые я могу использовать.
Теперь, поскольку я не использую браузер, я вынужден полагаться на методы, доступные для HTMLDocument объекта. В отличие от браузера, у меня нет доступа к ограниченным селекторам псевдокласса , которые они поддерживают, при автоматизации через VBA, что позволило бы мне использовать синтаксис селектора, такой как :nth-of-type
для доступа к отдельным строкам. Это раздражающее ограничение веб-скребка с VBA.
Итак, что мы можем сделать? Ну, в этом случае я могу записать innerHTML
каждого узла в другую переменную HTMLDocument
, html2
, чтобы я мог получить доступ к методам querySelector/querySelectorAll
этого объекта. HTML тогда будет ограничен только текущими li
.
Если мы посмотрим на рассматриваемый HTML-код:

Мы можем видеть, что элементы li
- это общие братья и сестры. Они сидят рядом друг с другом на одном уровне. Зацикливая свой nodeList listings
, я передаю innerHTML
из текущего узла в html2
; моя вторая HTMLDocument
переменная.
Стоит отметить, что я, вероятно, опустился в каждый список, используя children
, например:
listings.item(i).Children(2)......
Тогда я мог бы разделиться на новые строки и т. Д., Чтобы получить доступ ко всей информации. Я думаю, что данный метод быстрее и надежнее.
VBA:
Option Explicit
Public Sub GetInfo()
Dim ws As Worksheet, html As HTMLDocument, s As String
Const URL As String = "https://www.neighborhoodselfstorage.net/self-storage-delmar-md-f1426"
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set html = New HTMLDocument
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", "Amenities", "Offer1", "Offer2", "RateType", "Price")
Set listings = html.querySelectorAll(".main li[class]")
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
'size,description, amenities,specials offer1 offer2, rate type, price
results(r, 1) = Trim$(html2.querySelector(".size").innerText)
results(r, 2) = Trim$(html2.querySelector(".description").innerText)
Set icons = html2.querySelectorAll("i[title]")
ReDim amenitiesInfo(0 To icons.Length - 1)
For icon = 0 To icons.Length - 1
amenitiesInfo(icon) = icons.item(icon).getAttribute("title")
Next
amenities = Join$(amenitiesInfo, ", ")
results(r, 3) = amenities
results(r, 4) = html2.querySelector(".offer1").innerText
results(r, 5) = html2.querySelector(".offer2").innerText
results(r, 6) = html2.querySelector(".rate-label").innerText
results(r, 7) = html2.querySelector(".price").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
Internet Explorer:
Предположим, не перенаправлено с данного URL. Здесь я использую: селектор псевдокласса nth-of-type для нацеливания на каждую строку списка. Эти строки являются элементами li
(list), содержащими информацию для каждого списка блоков. Я создаю строку селектора CSS, которая определяет строку, а затем элемент в строке, которую я ищу. Я передаю эту строку в querySelector
или querySelectorAll
, которая возвращает совпадающий элемент / с.
Option Explicit
Public Sub UseIE()
Dim ie As New InternetExplorerm, ws As Worksheet
Const Url As String = "https://www.neighborhoodselfstorage.net/self-storage-delmar-md-f142"
Set ws = ThisWorkbook.Worksheets("Sheet1")
With ie
.Visible = True
.Navigate2 Url
While .Busy Or .readyState < 4: DoEvents: Wend
Dim headers(), results(), listings As Object, listing As Object, amenities As String
headers = Array("Size", "Description", "Amenities", "Offer1", "Offer2", "RateType", "Price")
Set listings = .document.querySelectorAll(".main li[class]")
Dim rowCount As Long, numColumns As Long, r As Long, c As Long
Dim icons As Object, icon As Long, amenitiesInfo(), i As Long
rowCount = listings.Length
numColumns = UBound(headers) + 1
ReDim results(1 To rowCount, 1 To numColumns)
For Each listing In listings
r = r + 1
'size,description, amenities,specials offer1 offer2, rate type, price
With .document
results(r, 1) = Trim$(.querySelector(".main li:nth-of-type(" & r & ") .size").innerText)
results(r, 2) = Trim$(.querySelector(".main li:nth-of-type(" & r & ") .description").innerText)
Set icons = .querySelectorAll("." & Join$(Split(listing.className, Chr$(32)), ".") & ":nth-of-type(" & r & ") i[title]")
ReDim amenitiesInfo(0 To icons.Length - 1)
For icon = 0 To icons.Length - 1
amenitiesInfo(icon) = icons.item(icon).getAttribute("title")
Next
amenities = Join$(amenitiesInfo, ",")
results(r, 3) = amenities
results(r, 4) = .querySelector(".main li:nth-of-type(" & r & ") .offer1").innerText
results(r, 5) = .querySelector(".main li:nth-of-type(" & r & ") .offer2").innerText
results(r, 6) = .querySelector(".main li:nth-of-type(" & r & ") .rate-label").innerText
results(r, 7) = .querySelector(".main li:nth-of-type(" & r & ") .price").innerText
End With
Next
.Quit
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
Ссылки (VBE> Инструменты> Ссылки):
- Библиотека объектов Microsoft HTML
- Microsoft Internet Controls