Я пытаюсь просмотреть журналы игр питчеров на сайте www.statmuse.com. Основная проблема заключается в том, что, поскольку я пытаюсь сделать это в цикле, часть URL-адреса в настоящее время неизвестна.
Например, если посмотреть журналы игр Мартина Переса за 2024 год, URL-адрес: https://www.statmuse.com/mlb/player/martin-perez-46483/game-log
теперь при попытке пройти через разных питчеров эта 5-значная числовая последовательность (46483 в моем примере) является переменной и меняется в журнале игры каждого питчера.
Я собрал приведенный ниже код. Проблема, конечно же, заключается в циклическом переборе 10000 и 99999 в попытке найти правильную последовательность пятизначных чисел, из-за чего мой Excel выходит из строя и не отвечает. Может ли кто-нибудь посоветовать более эффективный способ добиться этого? Прошу прощения, это мой первый проект с HTTP-запросами и чем-то подобным, поэтому я уверен, что код — это чертовски беспорядок.
код:
Dim ws As Worksheet, PLws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Set PLws = ThisWorkbook.Sheets("Pitcher List T")
Set rng = PLws.Range("B1:B1")
For Each cc In rng
Dim httpRequest As MSXML2.XMLHTTP60: Set httpRequest = New MSXML2.XMLHTTP60
Dim htmldoc As HTMLDocument: Set htmldoc = New HTMLDocument
playerName = CStr(cc.Value)
Dim baseURL As String
baseURL = "https://www.statmuse.com/mlb/player/" & playerName & "-"
Dim lastRow As Long
Dim startNumber As Long
Dim endNumber As Long
startNumber = 10000 ' this loop is the issue (i think)
endNumber = 99999 ' this loop is the issue (i think)
Dim i As Long, target As Long
For i = startNumber To endNumber ' this loop is the issue (i think)
Dim url As String
url = baseURL & CStr(i) & "/game-log"
If CheckUrlExists(url) Then
target = i
Debug.Print "the target i is: " & target
End If
Next i
Dim Murl As String
Murl = baseURL & target & "/game-log"
httpRequest.Open "GET", Murl, False
httpRequest.send
htmldoc.body.innerHTML = httpRequest.responseText
это функция:
Public Function CheckUrlExists(url) As Boolean
On Error GoTo CheckUrlExists_Error
Dim xmlhttp As MSXML2.XMLHTTP60: Set xmlhttp = New MSXML2.XMLHTTP60
Dim htmldoc As HTMLDocument: Set htmldoc = New HTMLDocument
Dim H2el As Object
xmlhttp.Open "GET", url, False
xmlhttp.send
htmldoc.body.innerHTML = xmlhttp.responseText
If xmlhttp.Status = 200 Then
For Each H2el In htmldoc.getElementsByTagName("h2")
If InStr(1, ChangeAccent(H2el.innerText), CStr(cc.Offset(0, -1).Value)) > 0 Then
CheckUrlExists = True
End If
Next H2el
Else
CheckUrlExists = False
End If
Exit Function
CheckUrlExists_Error:
CheckUrlExists = False
End Function
Спасибо
я обновил вопрос, включив в него функцию
Вы делаете два звонка, хотя вам нужен только один... Однако такой подход по-прежнему является грубостью по отношению к сайту.
мои извинения, Тим, поскольку я впервые работаю с https-запросами и готов получить как можно больше рекомендаций. Простите за невежество, а как это грубо по отношению к сайту? Ненужный и дополнительный износ механизма реагирования сайта?
Не могли бы вы указать мне правильное направление, как правильно отправить запрос? И, если возможно, как получить URL-адрес с подстановочными знаками, не повреждая сайт и не отправляя слишком много запросов?


Для каждого запроса POST время ответа удаленного сервера для кодов состояния 200 (ОК) или 502 (Плохой шлюз) различается. В моем тестировании это обычно занимает от полсекунды до одной секунды. Проверка 90 000 URL-адресов с помощью этого метода нецелесообразна.
Действительно, попытка проверки большого количества URL-адресов может создать значительную нагрузку на серверы веб-сайта. Фактически, это можно интерпретировать как форму незначительной DDoS-атаки (распределенный отказ в обслуживании).
Приведенный ниже код имитирует search на https://www.statmuse.com/ с использованием Internet Explorer, который, хотя и немного устарел, но все же выполняет свою работу. Selenium WebDriver может быть лучшей альтернативой.
Sub Demo()
Debug.Print GetURL("martin perez")
Debug.Print GetURL("lebron james")
End Sub
Function GetURL(ByVal sFullName As String) As String
Dim IE As Object
Dim doc As Object
' Initialize Internet Explorer
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = False
' Navigate to the webpage
IE.Navigate "https://www.statmuse.com/ask?q = " & Replace(sFullName, " ", "-")
' Wait for IE to finish loading the page
Do While IE.Busy Or IE.readyState <> 4
DoEvents
Loop
' Get the document object
Set doc = IE.Document
' Get the HTML content of the webpage
'Dim htmlContent As String: htmlContent = doc.DocumentElement.outerHTML
Dim oLink As Object
Set oLink = doc.getElementsByTagName("link")(0)
' Extract the href attribute value from the link tag
If Not oLink Is Nothing Then
GetURL = oLink.getAttribute("href")
Else
GetURL = ""
End If
' Close IE
IE.Quit
Set IE = Nothing
End Function
Вывод в немедленном окне:
https://www.statmuse.com/mlb/player/mart%C3%ADn-p%C3%A9rez-46483
https://www.statmuse.com/nba/player/lebron-james-1780
спасибо, я получил ошибку во время выполнения «ошибка автоматизации, вызванный объект отключился от своих клиентов» в строке «Do While IE.Busy Or IE.readyState <> 4», и я включил соответствующие ссылки для элементов управления IE.
Я не уверен, что означает ошибка. Я протестировал код без проблем на Win11+O365.
Просто получите URL-адрес напрямую с помощью:
Function GetPlayerLogURLs(ByRef playerName As Variant) As Collection
Dim httpRequest As MSXML2.XMLHTTP60: Set httpRequest = New MSXML2.XMLHTTP60
Const baseURL As String = "https://www.statmuse.com"
'
httpRequest.Open "POST", baseURL & "/ask", False
httpRequest.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
httpRequest.send "question%5Bquery%5D = " _
& LCase$(Replace(playerName, " ", "+")) _
& "&question%5Bpreferred_domain%5D=&question%5Bconversation_token%5D = "
'
Dim response As String: response = httpRequest.responseText
Dim i As Long
Dim j As Long
Dim res As New Collection
Dim temp As String
'
i = InStr(1, response, "/game-log""")
If i = 0 Then
i = InStr(1, response, """/mlb/player/")
On Error Resume Next
Do While i > 0
j = InStr(i + 1, response, """")
If j > 0 Then
temp = baseURL & Mid$(response, i + 1, j - i - 1) & "/game-log"
res.Add temp, temp
End If
i = InStr(j + 1, response, """/mlb/player/")
Loop
On Error GoTo 0
Else
j = InStrRev(response, """", i)
If j > 0 Then res.Add baseURL & Mid$(response, j + 1, i - j + 8)
End If
Set GetPlayerLogURLs = res
End Function
а затем просто используйте его вот так:
Sub TestPlayerURLs()
Dim v As Variant
Dim w As Variant
Dim coll As Collection
'
For Each v In Array("Martin Perez", "Derrick White", "Logan Allen", "This name doesn't exist")
Debug.Print "Player name: " & v
Set coll = GetPlayerLogURLs(v)
Debug.Print "Results found: " & coll.Count
For Each w In coll
Debug.Print w
Next w
Debug.Print
Next v
End Sub
если у вас есть время, не могли бы вы ответить еще на один вопрос. Как мне учитывать, есть ли игроки с таким же именем? Например. Логан Аллен о «Даймондбэках» (statmuse.com/mlb/player/logan-allen-90330/game-log ) и Логан Аллен о «Стражах» ( statmuse.com/mlb/player/logan-allen-92509 /game-log)? В данный момент функция в этом сценарии ничего не возвращает, я предполагаю, что существует несколько результатов. Спасибо за ваше время.
@Курт, я отредактировал ответ. функция теперь возвращает объект Collection с 0, 1 или несколькими ответами.
@CristianBuse Очень изящное решение. Могу я задать простой вопрос: как разобраться в аргументе send ? например. "&question%5Bpreferred_domain%5D=&question%5Bconversation_token%5D = " в вашем коде.
@taller Конечно. В своем браузере используйте инструменты разработчика. Для Chrome нажмите клавишу F12. Затем перейдите на вкладку «Сеть» и начните запись. Затем, какие бы действия вы ни совершали, они записываются, и вы можете затем просмотреть каждый запрос GET или POST. Вы сможете увидеть заголовки запросов, URL-адреса, содержимое форм, ответы и т. д.
@CristianBuse Есть идеи, почему сегодня утром у меня это работало идеально, а теперь, примерно через час тестирования и адаптации результатов к моему листу, он не возвращает URL-адрес даже в простом тесте debug.print?
@Курт, я только что проверил, и у меня все еще работает. Должно быть, вы выполнили слишком много запросов за короткий период времени. Попробуйте изменить свой IP-адрес, например. используйте VPN
Пожалуйста, поделитесь кодом в
Function CheckUrlExists().