Ниже приведена инструкция по поиску электронной почты и адреса веб-сайта.У вас уже есть ваш цикл и дедупликация.Ниже приведены вспомогательные методы для извлечения необходимой информации.Вы можете просто назначить из переменных email
и website
свои ячейки в цикле.Я показываю метод, использующий вспомогательную функцию для определения lastRow в целевом листе и выписывания переменных для исправления столбцов за один раз.
Я могу помочь с реализацией интеграции цикла, если необходимо, но акцент здесь был сделан на объяснении того, чтоможет быть сделано для определения этих элементов, представляющих интерес, и как выписать в правильные столбцы.Tbh - дедупликация так легко выполняется в конце листа, но вы также можете использовать макро-рекордер, чтобы получить идеально функциональный код для этого единственного шага / использовать существующие ответы SO.
tl; dr;
Это было бы намного проще, если бы были разрешены псевдоклассы :contains
/ :has
css.Вместо этого мой подход заключается в следующем:
- email - найти атрибут
href
, значение которого начинается с mailto
![image](https://i.stack.imgur.com/0T3B9.png)
веб-сайт - убедитесь, что на странице есть значок веб-сайта
![enter image description here](https://i.stack.imgur.com/3IC5a.png)
Укажите родительский элемент для обоихзначок веб-сайта и адрес веб-сайта
![enter image description here](https://i.stack.imgur.com/NET0z.png)
Зацикливать все совпадения с этой родительской спецификацией, проверяя, содержит ли значок веб-сайта (именно в этом случае селекторы псевдо-классовупрощенные вещи).Если совпадение найдено, то у нас есть общий родитель и для иконки, и, надеюсь, адрес веб-сайта;используйте childOfSiblingCssSelector
(в данном случае мы рассматриваем дочерний элемент следующего div) для выбора URL веб-сайта.
Примечания:
- Все это поддерживается на достаточно высоком / общем уровне, так что вы можете настроить селекторы CSS так, чтобы они соответствовали различным сценариям.Следствие - может показаться немного многословным.
- Предоставляются вспомогательные функции для обработки соответствия элементов.Назовите их так, чтобы они понимали, что они делают.Я думаю, что здесь есть место для улучшения.
- Хотя технически второй помощник,
GetText
, может быть использован для извлечения адреса электронной почты (я бы, вероятно, добавил еще один аргумент в вызов функции, чтобы указать атрибут для извлечения) кака также адрес веб-сайта, в настоящее время кажется гораздо быстрее нацелиться на соответствующий href
, как описано выше. - Я сохранил селекторы css как локальные переменные, близкие к их использованию;Вы могли бы иметь их как константы, ближе к верхней части модуля, где, возможно, проще получить доступ?Не знаете, как это происходит с течением времени / разные URL-адреса
- Селекторы Css выбираются вместо
.getElementsBy
методов по двум причинам: 1) есть оптимизация браузера для селекторов css, поэтому, если правильно сформулировать, css будет быстрее 2Я хочу сохранить гибкость функций code / helper - у вас гораздо больше специфичности с помощью селекторов css с точки зрения того, какие шаблоны вы можете выразить.Я посчитал это важным, так как не знаю, какие будущие случаи вам могут понадобиться. - Я намеренно не использую имя класса и индекс, например,
doc.getElementsByClassName("_50f4")(2).innerText
, так как я не знаком с диапазоном возможных вариантов использования.;это кажется немного хрупким, поскольку зависит от последовательного упорядочения и нумерации элементов (по крайней мере, до этих индексов).
TODO:
- Вместо того, чтобы каждый раз создавать новый
HTMLDocument
в GetText
, более эффективно передавать другой аргумент HTMLDocument
в сигнатуре функции, то есть из вызывающей процедуры.Рефакторинг может принять это во внимание. - Этот тип кодирования может пригодиться в будущем на основе классов.В частности, если необходимо добавить обработку ошибок и дополнительные функции.
VBA:
Option Explicit
'VBE > Tools > References > HTML Object Library
Public Sub test()
Dim ie As Object, ws As Worksheet
Set ie = CreateObject("InternetExplorer.Application")
Set ws = ThisWorkbook.Worksheets("Scraper")
With ie
.Visible = True
.Navigate2 "https://www.facebook.com/pg/SalemFordNH/about/?ref=page_internal%5Blink%5D"
While .Busy Or .readyState < 4: DoEvents: Wend
With .document
Dim email As String, website As String, iconCssSelector As String
'iconCssSelector for website icon in this instance
iconCssSelector = "[src='https://static.xx.fbcdn.net/rsrc.php/v3/yV/r/EaDvTjOwxIV.png']"
If ElementIsPresent(ie.document, "[href^=mailto]") Then
email = ie.document.querySelector("[href^=mailto]").innerText
Else
email = "Not found"
End If
Dim parents As Object, sharedParentCssSelector As String, childOfSiblingCssSelector As String
sharedParentCssSelector = "._5aj7" 'target parent of both icon and the website link
childOfSiblingCssSelector = "._50f4" '< to target website address after finding right parent
If ElementIsPresent(ie.document, iconCssSelector) _
And ElementIsPresent(ie.document, sharedParentCssSelector) Then
Set parents = ie.document.querySelectorAll(sharedParentCssSelector) 'css selector used to allow for greater flexibility in element matching
website = GetText(ie.document, parents, iconCssSelector, childOfSiblingCssSelector)
Else
website = "Not found"
End If
End With
'Assumes headers already present
Dim nextRow As Long
nextRow = GetLastRow(ws, 1) + 1
ws.Cells(nextRow, 1).Resize(1, 2) = Array(email, website)
.Quit
End With
End Sub
Public Function ElementIsPresent(ByVal document As HTMLDocument, ByVal cssSelector As String) As Boolean
ElementIsPresent = document.querySelectorAll(cssSelector).length > 0
End Function
Public Function GetText(ByVal document As HTMLDocument, ByVal parents As Object, ByVal iconCssSelector As String, ByVal childOfSiblingCssSelector As String) As String
'in this instance and with microsoft IE DOM you cannot select for parent of an element with pseudo class _
of :has(>child); nor use :contains... instead pass expected parent selector, that houses _
both the icon element for website and the website address itself, and loop all matches checking for website icon _
if found use childOfSiblingCssSelector to extract
Dim i As Long, html As HTMLDocument
Set html = New HTMLDocument
For i = 0 To parents.length - 1
html.body.innerHTML = parents.item(i).innerHTML
If ElementIsPresent(html, iconCssSelector) Then
GetText = html.querySelector(childOfSiblingCssSelector).innerText
Exit Function
End If
Next
GetText = "Not found"
End Function
Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
With ws
GetLastRow = .Cells(.rows.Count, columnNumber).End(xlUp).Row
End With
End Function
Ссылки на проект (VBE> Инструменты> Ссылки):
- Библиотека объектов Microsoft HTML
Дополнительное чтение:
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors
- https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
- https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
Редактировать:
Пример цикла - предполагается, что в столбце А между URL-адресами нет пустых строк.
Option Explicit
'VBE > Tools > References > HTML Object Library
Public Sub test()
Dim ie As Object, ws As Worksheet, wsUrls As Worksheet, urls()
Set ie = CreateObject("InternetExplorer.Application")
Set ws = ThisWorkbook.Worksheets("Scraper")
Set wsUrls = ThisWorkbook.Worksheets("Url List")
With wsUrls
urls = Application.Transpose(.Range("A2:A" & .Cells(.rows.Count, "A").End(xlUp).Row).Value)
End With
Dim results(), r As Long
ReDim results(1 To UBound(urls), 1 To 2)
With ie
.Visible = True
For r = LBound(urls) To UBound(urls)
.Navigate2 urls(r)
While .Busy Or .readyState < 4: DoEvents: Wend
With .document
Dim email As String, website As String, iconCssSelector As String
'iconCssSelector for website icon in this instance
iconCssSelector = "[src='https://static.xx.fbcdn.net/rsrc.php/v3/yV/r/EaDvTjOwxIV.png']"
If ElementIsPresent(ie.document, "[href^=mailto]") Then
email = ie.document.querySelector("[href^=mailto]").innerText
Else
email = "Not found"
End If
Dim parents As Object, sharedParentCssSelector As String, childOfSiblingCssSelector As String
sharedParentCssSelector = "._5aj7" 'target parent of both icon and the website link
childOfSiblingCssSelector = "._50f4" '< to target website address after finding right parent
If ElementIsPresent(ie.document, iconCssSelector) _
And ElementIsPresent(ie.document, sharedParentCssSelector) Then
Set parents = ie.document.querySelectorAll(sharedParentCssSelector) 'css selector used to allow for greater flexibility in element matching
website = GetText(ie.document, parents, iconCssSelector, childOfSiblingCssSelector)
Else
website = "Not found"
End If
End With
'Assumes headers already present
Dim nextRow As Long
results(r, 1) = email
results(r, 2) = website
Next
.Quit
End With
nextRow = GetLastRow(ws, 1) + 1
ws.Cells(nextRow, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
End Sub
Public Function ElementIsPresent(ByVal document As HTMLDocument, ByVal cssSelector As String) As Boolean
ElementIsPresent = document.querySelectorAll(cssSelector).length > 0
End Function
Public Function GetText(ByVal document As HTMLDocument, ByVal parents As Object, ByVal iconCssSelector As String, ByVal childOfSiblingCssSelector As String) As String
'in this instance and with microsoft IE DOM you cannot select for parent of an element with pseudo class _
of :has(>child); nor use :contains... instead pass expected parent selector, that houses _
both the icon element for website and the website address itself, and loop all matches checking for website icon _
if found use childOfSiblingCssSelector to extract
Dim i As Long, html As HTMLDocument
Set html = New HTMLDocument
For i = 0 To parents.length - 1
html.body.innerHTML = parents.item(i).innerHTML
If ElementIsPresent(html, iconCssSelector) Then
GetText = html.querySelector(childOfSiblingCssSelector).innerText
Exit Function
End If
Next
GetText = "Not found"
End Function
Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
With ws
GetLastRow = .Cells(.rows.Count, columnNumber).End(xlUp).Row
End With
End Function