--Marc-- 0 Geschrieben 23. Juli Melden Teilen Geschrieben 23. Juli (bearbeitet) Hallo zusammen, ich verwende Powershell und Selenium um Webelemente auf einer Webseite anzusteuern. Bei mehreren Dropdown-Menüs wird eine Auswahl getroffen und dann wird ein Button geklickt um Daten zu erhalten. Funktioniert soweit auch ganz gut, allerdings macht eines der Dropdown-Menüs regelmäßig Zicken und springt nach der Auswahl einfach zurück auf den Ursprungswert und mein Code rennt weiter. Also war mein Gedanke den Wert des Dropdown-Menüs zu prüfen bevor ich den Code weiter laufen lasse, mit Hilfe einer Do-Until Schleife. Nun hat sich dabei aber herausgestellt, dass das verflixte Dropdown-Menü zu allen Zeiten immer die gleiche Text-Eigenschaft besitzt, selbst dann wenn eigentlich ein ganz anderer Wert darin angezeigt wird. Beispiel: Im Dropdown-Menü steht per default "2024 Juli". Wenn ich jedoch mit "Write-Host $Element.Text" die Text-Eigenschaft abfrage dann bekomme ich als Rückgabewert "All". Das Blöde ist, "All" ist genau der Wert, den ich auswählen möchte und die Bedingung, dass mein Code weiterläuft. Also erst wenn "All" wirklich ausgewählt wurde, soll mein Code weiterlaufen. Hier der ganze Code, inklusive dem Laden der Triber für diverse Browser: # Browsertreiber laden function Create-Browser { param( [Parameter(mandatory=$true)][ValidateSet('Chrome','Edge','Firefox')][string]$browser, [Parameter(mandatory=$false)][bool]$HideCommandPrompt = $true, [Parameter(mandatory=$false)][string]$driverversion = '', [Parameter(mandatory=$false)][object]$options = $null ) $driver = $null function Load-NugetAssembly { [CmdletBinding()] param( [string]$url, [string]$name, [string]$zipinternalpath, [switch]$downloadonly ) if($psscriptroot -ne ''){ $localpath = join-path $psscriptroot $name }else{ $localpath = join-path $env:TEMP $name } $tmp = "$env:TEMP\$([IO.Path]::GetRandomFileName())" $zip = $null try{ if(!(Test-Path $localpath)){ Add-Type -A System.IO.Compression.FileSystem write-host "Downloading and extracting required library '$name' ... " -F Green -NoNewline (New-Object System.Net.WebClient).DownloadFile($url, $tmp) $zip = [System.IO.Compression.ZipFile]::OpenRead($tmp) $zip.Entries | ?{$_.Fullname -eq $zipinternalpath} | %{ [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_,$localpath) } write-host "OK" -F Green } if (Get-Item $localpath -Stream zone.identifier -ea SilentlyContinue){ Unblock-File -Path $localpath } if(!$downloadonly.IsPresent){ Add-Type -Path $localpath -EA Stop } }catch{ throw "Error: $($_.Exception.Message)" }finally{ if ($zip){$zip.Dispose()} if(Test-Path $tmp){del $tmp -Force -EA 0} } } # Load Selenium Webdriver .NET Assembly and dependencies Load-NugetAssembly 'https://www.nuget.org/api/v2/package/Newtonsoft.Json' -name 'Newtonsoft.Json.dll' -zipinternalpath 'lib/net45/Newtonsoft.Json.dll' -EA Stop Load-NugetAssembly 'https://www.nuget.org/api/v2/package/Selenium.WebDriver' -name 'WebDriver.dll' -zipinternalpath 'lib/netstandard2.0/WebDriver.dll' -EA Stop if($psscriptroot -ne ''){ $driverpath = $psscriptroot }else{ $driverpath = $env:TEMP } switch($browser){ 'Chrome' { $chrome = Get-Package -Name 'Google Chrome' -EA SilentlyContinue | select -F 1 if (!$chrome){ throw "Google Chrome Browser not installed." return } Load-NugetAssembly "https://www.nuget.org/api/v2/package/Selenium.WebDriver.ChromeDriver/$driverversion" -name 'chromedriver.exe' -zipinternalpath 'driver/win32/chromedriver.exe' -downloadonly -EA Stop # create driver service $dService = [OpenQA.Selenium.Chrome.ChromeDriverService]::CreateDefaultService($driverpath) # hide command prompt window $dService.HideCommandPromptWindow = $HideCommandPrompt # create driver object if ($options){ $driver = New-Object OpenQA.Selenium.Chrome.ChromeDriver $dService,$options }else{ $driver = New-Object OpenQA.Selenium.Chrome.ChromeDriver $dService } } 'Edge' { $edge = Get-Package -Name 'Microsoft Edge' -EA SilentlyContinue | select -F 1 if (!$edge){ throw "Microsoft Edge Browser not installed." return } Load-NugetAssembly "https://www.nuget.org/api/v2/package/Selenium.WebDriver.MSEdgeDriver/$driverversion" -name 'msedgedriver.exe' -zipinternalpath 'driver/win64/msedgedriver.exe' -downloadonly -EA Stop # create driver service $dService = [OpenQA.Selenium.Edge.EdgeDriverService]::CreateDefaultService($driverpath) # hide command prompt window $dService.HideCommandPromptWindow = $HideCommandPrompt # create driver object if ($options){ $driver = New-Object OpenQA.Selenium.Edge.EdgeDriver $dService,$options }else{ $driver = New-Object OpenQA.Selenium.Edge.EdgeDriver $dService } } 'Firefox' { $ff = Get-Package -Name "Mozilla Firefox*" -EA SilentlyContinue | select -F 1 if (!$ff){ throw "Mozilla Firefox Browser not installed." return } Load-NugetAssembly "https://www.nuget.org/api/v2/package/Selenium.WebDriver.GeckoDriver/$driverversion" -name 'geckodriver.exe' -zipinternalpath 'driver/win64/geckodriver.exe' -downloadonly -EA Stop # create driver service $dService = [OpenQA.Selenium.Firefox.FirefoxDriverService]::CreateDefaultService($driverpath) # hide command prompt window $dService.HideCommandPromptWindow = $HideCommandPrompt # create driver object if($options){ $driver = New-Object OpenQA.Selenium.Firefox.FirefoxDriver $dService, $options }else{ $driver = New-Object OpenQA.Selenium.Firefox.FirefoxDriver $dService } } } return $driver } $url = 'https://www.cboe.com/delayed_quotes/nsc/quote_table' Write-Host 'Url: ' $url # $browser = Create-Browser -browser Chrome $browser = Create-Browser -browser Edge $browser.Url = $url $navi = $browser.Navigate() Start-Sleep -Seconds 1 $browser.Manage().Window.FullScreen() #Cookie-Meldung ausblenden (Agree Button) $agreeButton = $browser.FindElement([OpenQA.Selenium.By]::XPath("//button[contains(@class, 'button--solid') and contains(@class, 'privacy-alert__agree-button') and text()='I agree']")) $agreeButton.Click() #Optionsrange (Drowpdown-Menü) auf "All" stellen $optionsrange = $browser.FindElement([OpenQA.Selenium.By]::XPath("//input[contains(@class, 'ReactSelect__input') and contains(@id, 'select_5')]")) $actions = [OpenQA.Selenium.Interactions.Actions]::new($browser) $actions.SendKeys($optionsrange,'All') $actions.SendKeys($optionsrange,[OpenQA.Selenium.Keys]::Enter) $actions.Build() $actions.Perform() Start-Sleep -Seconds 3 #Expirations (Drowpdown-Menü) auf "All" stellen $expirations = $browser.FindElement([OpenQA.Selenium.By]::XPath("//input[contains(@class, 'ReactSelect__input') and contains(@id, 'select_7')]")) $singlevalue = $browser.FindElement([OpenQA.Selenium.By]::XPath("//div[contains(@class, 'ReactSelect__single-value') and contains(@class, 'css-1dimb5e-singleValue')]")) write-host $singlevalue.Text do { $actions = [OpenQA.Selenium.Interactions.Actions]::new($browser) $actions.SendKeys($expirations,'All') $actions.SendKeys($expirations,[OpenQA.Selenium.Keys]::Enter) $actions.Build() $actions.Perform() Start-Sleep -Seconds 1 write-host $singlevalue.Text } until ($singlevalue.Text -eq 'All') # Warten bis der Browser die Daten (im Hintergrund) geladen hat Start-Sleep -Seconds 2 # Dann erst die Optionskette anzeigen $viewchainbutton = $browser.FindElement([OpenQA.Selenium.By]::XPath("//button[contains(@class, 'Button__StyledButton-cui__sc-1ahwe65-2') and contains(@class, 'hMfKkd')]")) Start-Sleep -Seconds 1 $viewchainbutton.Click() # Link-Button klicken $browser.FindElement([OpenQA.Selenium.By]::XPath("//a[contains(@type, 'button') and contains(@download, 'quotedata.csv')]")).click() Start-Sleep -Seconds 2 Write-Output "Ready" $browser.Quit() Hier nochmal explizit die Codezeilen um die es geht: $singlevalue = $browser.FindElement([OpenQA.Selenium.By]::XPath("//div[contains(@class, 'ReactSelect__single-value') and contains(@class, 'css-1dimb5e-singleValue')]")) write-host $singlevalue.Text do { $actions = [OpenQA.Selenium.Interactions.Actions]::new($browser) $actions.SendKeys($expirations,'All') $actions.SendKeys($expirations,[OpenQA.Selenium.Keys]::Enter) $actions.Build() $actions.Perform() Start-Sleep -Seconds 1 write-host $singlevalue.Text } until ($singlevalue.Text -eq 'All') Hat jemand eine Ahnung, wie man das Dropdown-Menü so abfragt, dass man tatsächlich den darin ausgewählten Wert erhält? Es würde mir auch helfen wenn mir jemand sagen kann weshalb dieses Dropdown-Menü in vielen Fällen einfach wieder auf den default Wert zurückspringt. Beste Grüße Marc bearbeitet 23. Juli von --Marc-- Zitieren Link zu diesem Kommentar
daabm 1.354 Geschrieben 24. Juli Melden Teilen Geschrieben 24. Juli Ob Du da hier im richtigen Forum bist? Das klingt eher nach CSS/Webdev - an den Elementen hängen ja auch noch ziemlich viele Events, die Dein Skript nicht auslösen wird. Und dahinter stecken dann noch ein paar 100k Javascript. Und auch wenn ich bei Powershell nicht ganz Noob bin, bei Web bin ich es Selenium dürfte hier eher unbekannt sein, ich habe noch nie davon gehört. Und React ist auch nicht in meinem Werkzeugkasten. Ich verbuche das mal als Reverse Engineering Problem - Website-Funktionen aufzudröseln ist je nach Framework nahezu unmöglich. Siehe das invalid-Event des Dropdowns: function) Dt(e, r); else if (-1 < Ct.indexOf(e)) e = Tt(n, e, t, i, r), yt.push(e); else if (!fu Was ist Ct, was ist Tt, was yt? PS: "Damit überhaupt mal jemand antwortet"... 1 Zitieren Link zu diesem Kommentar
Beste Lösung mwiederkehr 373 Geschrieben 25. Juli Beste Lösung Melden Teilen Geschrieben 25. Juli Die Vermutung von Martin ist richtig. Frameworks wie React stellen Steuerelemente wie eine Auswahlliste gerne aus anderen Elementen zusammen. In diesem Fall ist es im Sourcecode kein "<select>", sondern ein "<div>" mit "<input>". Beim Ausklappen werden die Elemente erzeugt, die Daten werden allenfalls erst dann dynamisch abgerufen. Möchte man das fernsteuern, muss man möglichst genau machen, was der Benutzer auch macht. Also Klick aufs Dropdown, über Optionen iterieren, gewünschte Option anklicken. In Deinem Fall scheint es einfacher zu gehen: Die gewünschten Daten werden nicht beim Klick auf die Schaltfläche geladen, sondern sind von Anfang an im Sourcecode vorhanden: "CTX.contextOptionsData" enthält alle Daten als JSON. Das solltest Du per RegEx extrahieren und "ConvertFrom-Json" übergeben können. 2 Zitieren Link zu diesem Kommentar
--Marc-- 0 Geschrieben 27. Juli Autor Melden Teilen Geschrieben 27. Juli Am 25.7.2024 um 09:16 schrieb mwiederkehr: In Deinem Fall scheint es einfacher zu gehen: Die gewünschten Daten werden nicht beim Klick auf die Schaltfläche geladen, sondern sind von Anfang an im Sourcecode vorhanden: "CTX.contextOptionsData" enthält alle Daten als JSON. Das solltest Du per RegEx extrahieren und "ConvertFrom-Json" übergeben können. Erstmal herzlichen Dank für eure Rückmeldungen! @mwiederkehr: Nach einer solchen Lösung hatte ich zu Beginn gesucht, aufgrund meiner mangelhaften Kenntnisse wurde ich jedoch nicht fündig. Das ist auf jeden Fall mega! So brauche ich die Dropdowns gar nicht mehr anzusteuern, sondern ich muss nur das Element "CTX.contextOptionsData" auslesen. Wird vermutlich auch ein paar Herausforderungen mit sich bringen das zu realisieren aber am Ende ist es wesentlich komfortabler. Tausend Dank! Hat sich also doch gelohnt die Frage hier zu stellen. P.S. Ich hatte das Problem zwischenzeitlich übrigens gelöst indem ich einen Teil des genannten Codes einfach mehrfach ausführe: $actions = [OpenQA.Selenium.Interactions.Actions]::new($browser) $actions.SendKeys($expirations,'All') $actions.SendKeys($expirations,[OpenQA.Selenium.Keys]::Enter) $actions.Build() $actions.Perform() Start-Sleep -Seconds 1 $actions.Perform() Start-Sleep -Seconds 1 $actions.Perform() Ist zwar nicht schön aber hat seinen Zweck bislang erfüllt. Der Fehler ist bislang nicht mehr aufgetreten. Zitieren Link zu diesem Kommentar
Empfohlene Beiträge
Schreibe einen Kommentar
Du kannst jetzt antworten und Dich später registrieren. Falls Du bereits ein Mitglied bist, logge Dich jetzt ein.