Hi Alina ,
Lass dich nicht abhalten,
junger Mann.
Danke für das Kompliment, so jung bin ich leider auch nicht mehr 😅 .
Aber ich erinnere meine Frau mal daran 😁 .
Viele Grüße
Sven
Hi Alina ,
Lass dich nicht abhalten,
junger Mann.
Danke für das Kompliment, so jung bin ich leider auch nicht mehr 😅 .
Aber ich erinnere meine Frau mal daran 😁 .
Viele Grüße
Sven
Hi Swiffer,
eine ausführliche Antwort folgt heute oder morgen 🤞 . Habe es zur Kenntnis genommen, aber gerade keine Zeit es ausführlich zu beschreiben.
Bis dann.
Viele Grüße
Sven
Hi BugFix ,
dies eine schöne Möglichkeit für SciTE, Danke 👍 .
Für alle die die kein SciTE nutzen, kann auch der Weg über die Git Aliases genutzt werden, da kann man sich auch Git-Befehle kombinieren 😀 .
Viele Grüße
Sven
Hi BugFix ,
ja es würde ein Hinweis kommen, dass der Ordner bereits vorhanden ist und nicht leer ist, daher bricht dies ab.
An sich wirst du dir nichts so einfach zerschießen können, denn dafür ist GIT clever genug um dir Warnungen zu geben und auch die entsprechenden Vorschläge.
Trotzdem kannst du ja beim ersten Test deinen Ordner kopieren (Backup), nur für den Fall der Fälle 😅 .
Für dein Szenario konkret (so mache ich das bisher immer, ohne Probleme):
git init
git remote add origin https://github.com/BugFix/Test.git
git fetch
git checkout main -f
git branch --set-upstream-to origin/main
Viele Grüße
Sven
Hi Swiffer,
freut mich das es dir zusagt 🤝 .
Die eigentliche Automatisierung der Webseite ist gar nicht so sehr komplex. Die Abhängigkeiten des WebDriver sowie die vielen Vorbedingungen, welche zu erfüllen sind bevor man wirklich zur Seite navigieren kann und loslegen kann, dies ist komplexer, ja. Da lässt sich nicht viel machen. Allerdings kann man das Gerüst (das schaffen der Vorbedingungen) ja nun immer wieder verwenden 😀 .
Wenn es an der Seite Anpassungen geben sollte und du nicht mehr die Ergebnisse bekommst, welche du erwartest, dann musst du bestenfalls erstmal nur an die XPath-Selectoren (XPath-Ausdrücke) heran. Wenn es mehr Änderungen geben sollte, an den Ablauf auch noch. Erfahrungsgemäß ändern sich solche Seiten aber nicht sehr oft (selten).
Wenn es soweit kommen sollte, gib bitte einfach Bescheid oder versuche selbst wie weit du kommst. Eine ausführliche Erklärung "wie", würden den Rahmen sprengen. Daher habe ich nur das kommentiert, was man auch gut fassen kann ohne zu viele Insights zu haben/zu kennen.
Wenn es bei dir funktioniert und alles gut ist, dann schließe gerne diesen Thread hier (auf erledigt) setzen. Falls dann später noch was aufkommt, mach' einfach einen neuen auf 👍 .
Viele Grüße
Sven
Hi Alina ,
wunderbar, Danke dir 👍 .
Funktioniert wie erwartet. Allerdings bin ich mir nicht sicher, ob ich persönlich damit schneller wäre, also wenn ich die Sektion und das Key-Value Paar selbst erstelle.
Trotzdem 'ne feine Sache für Nutzer die ggf. kaum Erfahrung mit INIs haben 🤝 , "Tak".
Als Verbesserungsvorschlag sehe ich hier, dass gleich mehrere Sektionen und Key-Value Paare angelegt werden könnten, dann rockt das richtig 😅 .
Alina: Ist dieser Thread ggf. etwas für diesen Thread? Vielleicht kann man, ( BugFix bspw.), die beiden verbinden? Oder meinst du Alina, dass es ein separater Kontext ist und bleiben soll?
Viele Grüße
Sven
Hi Swiffer 👋 ,
hier nun mein Vorschlag, für deine Website Automatisierung/Steuerung richner.teamonline.ch, bzw. für die Anforderung Daten der Website in eine CSV Datei zu überführen.
#AutoIt3Wrapper_AU3Check_Stop_OnWarning=y
#AutoIt3Wrapper_Run_Au3Stripper=y
#AutoIt3Wrapper_UseUpx=n
#Au3Stripper_Parameters=/sf /sv /mo /rm /rsln
Opt('MustDeclareVars', 1)
#include-once
#include <File.au3>
#include "..\lib\au3WebDriver\wd_helper.au3"
#include "..\lib\au3WebDriver\wd_capabilities.au3"
; initialization ---------------------------------------------------------------
Global Const $sDriver = 'Firefox' ; Chrome|Firefox
Global Const $bIsHeadlessMode = False ; False|True
Global Const $iDelay = 300 ; Verzögerung als Unterstützung für ein robustes Warteverhalten (Seitenaufbau, Klicks, Texte)
Global $sSession = Null
; processing -------------------------------------------------------------------
_GetNewestDriver() ; Dient zum aktualisieren des WebDrivers, kann später auskommentiert werden
_SetLogLevel() ; Log level auf error ($_WD_DEBUG_Error) reicht meiner Meinung nach, kann natürlich variiert werden
_SetupDriver() ; Session für chrome oder firefox (WebDriver) erstellen
_Actions() ; Steuerung der Artikel-mit-Preise Webseite
_TeardownDriver() ; WebDriver wieder herunterfahren
; setup and teardown functions -------------------------------------------------
Func _GetNewestDriver()
_WD_UpdateDriver(StringLower($sDriver), _PathFull('..\util\webDriver'))
EndFunc
Func _SetLogLevel()
;~ $_WD_DEBUG = $_WD_DEBUG_None ; no logging
$_WD_DEBUG = $_WD_DEBUG_Error ; logging in case of Error
;~ $_WD_DEBUG = $_WD_DEBUG_Info ; logging with additional information
;~ $_WD_DEBUG = $_WD_DEBUG_Full ; logging with full details for developers
EndFunc
Func _SetupDriver()
_SetDriverOptions()
_WD_Startup()
_WD_CapabilitiesStartup()
Local $sCapabilities = _
(StringLower($sDriver) == 'chrome') _
? _CreateChromeDriverCapabilities() _
: _CreateFirefoxDriverCapabilities()
$sSession = _WD_CreateSession($sCapabilities)
EndFunc
Func _SetDriverOptions()
Local Const $sDriverExe = _
(StringLower($sDriver) == 'chrome') _
? 'chromedriver.exe' _
: 'geckodriver.exe'
Local Const $sDriverPath = _PathFull('..\util\webDriver')
Local Const $sDriverExeFilePath = $sDriverPath & '\' & $sDriverExe
Local Const $iPort = _
(StringLower($sDriver) == 'chrome') _
? 9515 _
: 4444
Local Const $sDriverParams = _
(StringLower($sDriver) == 'chrome') _
? '--verbose --log-path="' & $sDriverPath & '\chromedriver.log"' _
: '--log trace'
_WD_Option('Driver', $sDriverExeFilePath)
_WD_Option('Port', $iPort)
_WD_Option('DriverParams', $sDriverParams)
EndFunc
Func _CreateChromeDriverCapabilities()
_WD_CapabilitiesAdd('alwaysMatch', 'chrome')
_WD_CapabilitiesAdd('w3c', True)
_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
_WD_CapabilitiesAdd('args', '--window-size=1366,768')
If $bIsHeadlessMode Then
_WD_CapabilitiesAdd('args', '--headless')
EndIf
Return _WD_CapabilitiesGet()
EndFunc
Func _CreateFirefoxDriverCapabilities()
_WD_CapabilitiesAdd('alwaysMatch', 'firefox')
_WD_CapabilitiesAdd('browserName', 'firefox')
_WD_CapabilitiesAdd('acceptInsecureCerts', True)
_WD_CapabilitiesAdd('binary', 'C:\Program Files\Mozilla Firefox\firefox.exe') ; Ggf. einmalig diesen Pfad anpassen
_WD_CapabilitiesAdd('args', '--window-size=1366,768')
If $bIsHeadlessMode Then
_WD_CapabilitiesAdd('args', '--headless')
EndIf
Return _WD_CapabilitiesGet()
EndFunc
Func _TeardownDriver()
_WD_DeleteSession($sSession)
_WD_Shutdown()
EndFunc
; website functions ------------------------------------------------------------
Func _Actions()
Local Const $sCsvFilePath = _CreateArticlesAndPricesCsvFile() ; CSV Datei anlegen
_OpenArticlesWebsite() ; Webseite öffnen
Local Const $iMaxSiteCount = _GetPaginationMaxSiteCount() ; Anzahl der Seiten ermitteln, durch die navigiert werden muss,
; um alle Artikel/Preise zu berücksichtigen (nicht nur die der ersten Seite)
For $i = 1 To $iMaxSiteCount - 1 Step 1 ; Hier "- 1" da wir uns bereits auf der ersten Seite befinden
Local $aListOfArticles = _GetListOfArticles() ; Artikel der jeweiligen Seite holen
Local $aListOfPrices = _GetListOfPrices() ; Preise der jeweiligen Seite holen
; Dieser Schritt dient nur der Sicherheit, dass zu jedem Artikel auch ein dazugehöriger Preis gefunden wurde
Local $aListOfArticlesAndPrices = _VerifyArticleCountAndPriceCountMatches($aListOfArticles, $aListOfPrices)
; CSV Datei mit gefundenen Werte füllen
_WriteArticlesAndPricesToCsvFile($sCsvFilePath, $aListOfArticles, $aListOfPrices)
_ChooseNextArticlePage() ; Zur nächsten Seite navigieren
; und von vorn (Daten sammeln und in CSV schreiben)
Next
EndFunc
Func _CreateArticlesAndPricesCsvFile()
Local Const $sFilePath = @ScriptDir & '\' & @YEAR & @MON & @MDAY & '-' & @HOUR & @MIN & @SEC & '-artikelnummer-verkaufspreis.csv'
Local Const $sCsvColumnNames = 'Artikelnummer;Verkaufspreis' & @CRLF
_WriteFile($sFilePath, $sCsvColumnNames)
Return $sFilePath
EndFunc
Func _OpenArticlesWebsite()
_NavigateTo('https://richner.teamonline.ch/waschtische-bidets-badezimmermoebel/aufsatzwaschtische-keramik')
EndFunc
Func _GetPaginationMaxSiteCount()
Local Const $sPaginationTextSelector = '(//ul[@class="pagination"])[1]/li[contains(@class, "label")]'
Local Const $sPaginationText = _GetElementText($sPaginationTextSelector)
Local Const $sRegExPatternOfMaxSiteCount = ' von (\d+)'
Local Const $iReturnMatchesFlag = 1
Return StringRegExp($sPaginationText, $sRegExPatternOfMaxSiteCount, $iReturnMatchesFlag)[0]
EndFunc
Func _GetListOfArticles()
Local Const $sArticleDescriptionSelector = '//h6[@class="product-itemnumber-desc"]'
Local $aListOfElementsTexts = _GetElementsTexts($sArticleDescriptionSelector)
Return _RemoveUnnecessaryTextsFromList($aListOfElementsTexts)
EndFunc
Func _RemoveUnnecessaryTextsFromList($aList)
Local Const $iCount = _GetCount($aList)
For $i = 0 To $iCount Step 1
$aList[$i] = StringReplace($aList[$i], 'Artikel-Nr.: ', '')
$aList[$i] = StringReplace($aList[$i], 'Farbe: ', '')
$aList[$i] = StringReplace($aList[$i], 'Ausführung: ', '')
$aList[$i] = StringReplace($aList[$i], @LF, '')
Next
Return $aList
EndFunc
Func _GetListOfPrices()
Local Const $sArticlePriceSelector = '//div[@class="product-price "]/span'
Return _GetElementsTexts($sArticlePriceSelector)
EndFunc
Func _VerifyArticleCountAndPriceCountMatches($aArticles, $aPrices)
If _GetCount($aArticles) == _GetCount($aPrices) Then
Return
EndIf
Local Const $iErrorIconFlag = 16
Local Const $sTimeoutInSeconds = 10
Local Const $sErrorMessage = 'Anzahl gefundener Artikel und die Anzahl der gefundenen Preise stimmt nicht überein.'
MsgBox($iErrorIconFlag, 'Error', $sErrorMessage, $sTimeoutInSeconds)
_TeardownDriver() ; WebDriver vorzeitig herunterfahren (bei Bedarf auskommentieren)
EndFunc
Func _WriteArticlesAndPricesToCsvFile($sFile, $aArticles, $aPrices)
For $i = 0 To _GetCount($aArticles) Step 1
_AppendToFile($sFile, $aArticles[$i] & ';' & $aPrices[$i] & @CRLF)
Next
EndFunc
Func _ChooseNextArticlePage()
Local Const $sPaginationArrowNextSelector = '(//ul[@class="pagination"])[1]//li[@class="arrow next "]'
_ClickElement($sPaginationArrowNextSelector)
EndFunc
; webdriver functions ----------------------------------------------------------
Func _NavigateTo($sUrl)
_WD_Navigate($sSession, $sUrl)
_WD_LoadWait($sSession, $iDelay)
EndFunc
Func _FindElement($sSelector)
Local $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, $sSelector)
If @error <> $_WD_ERROR_Success Then
ConsoleWrite('Error for XPath selector ''' & $sSelector & '''.' & @CRLF)
_TeardownDriver() ; WebDriver vorzeitig herunterfahren (bei Bedarf auskommentieren)
EndIf
Return $sElement
EndFunc
Func _FindElements($sSelector)
Return _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, $sSelector, Default, True)
EndFunc
Func _WaitFor($sSelector)
Local Const $iTimeoutInMilliseconds = 5000
Local Const $iElementVisibleFlag = 1
_WD_WaitElement($sSession, $_WD_LOCATOR_ByXPath, $sSelector, $iDelay, $iTimeoutInMilliseconds, $iElementVisibleFlag)
EndFunc
Func _GetElementText($sSelector)
_WaitFor($sSelector)
Return _WD_ElementAction($sSession, _FindElement($sSelector), 'text')
EndFunc
Func _GetElementsTexts($sSelector)
Local Const $aListOfElements = _FindElements($sSelector)
Local Const $iCount = _GetCount($aListOfElements)
Local $aListOfElementsTexts[$iCount + 1]
For $i = 0 To $iCount Step 1
$aListOfElementsTexts[$i] = _WD_ElementAction($sSession, $aListOfElements[$i], 'text')
Next
Return $aListOfElementsTexts
EndFunc
Func _ClickElement($sSelector)
_WaitFor($sSelector)
_WD_ElementAction($sSession, _FindElement($sSelector), 'click')
EndFunc
; helper functions -------------------------------------------------------------
Func _WriteFile($sFile, $sText)
Local Const $iUtf8WithoutBomAndOverwriteCreationMode = 256 + 2 + 8
Local $hFile = FileOpen($sFile, $iUtf8WithoutBomAndOverwriteCreationMode)
FileWrite($hFile, $sText)
FileClose($hFile)
EndFunc
Func _GetCount($aList)
Return UBound($aList) - 1
EndFunc
Func _AppendToFile($sFile, $sText)
Local Const $iUtf8WithoutBomAndAppendMode = 256 + 1
Local $hFile = FileOpen($sFile, $iUtf8WithoutBomAndAppendMode)
FileWrite($hFile, $sText)
FileClose($hFile)
EndFunc
Alles anzeigen
Das Skript allein, ist so nicht lauffähig. Alle Abhängigkeiten1, welche benötigt werden, inklusive des Main.au3 Skriptes können hier heruntergeladen werden.
Das Ergebnis, der Ausführung ist, dass eine CSV Datei mit den gefundenen Artikelnummern und Verkaufspreisen angelegt wird. Also Beispiel für den heutigen Lauf:
Artikelnummer;Verkaufspreis
234057100242;893.00
234057100244;998.00
234064100242;942.00
234064100244;1'047.00
234051100242;439.00
234051100244;547.00
234072100242;535.00
234072100244;640.00
212144242;770.00
212144244;875.00
212149242;842.00
212149244;947.00
212156241;806.00
212156243;911.00
212157241;842.00
212157243;947.00
213120100241;330.00
213120100243;435.00
234551100;683.00
234552100;974.00
234155100183;710.00
234155100184;835.00
234156100183;767.00
234156100184;892.00
234157100183;668.00
234157100184;793.00
234158100183;725.00
234158100184;850.00
234161100183;835.00
234161100184;959.00
234162100183;891.00
234162100184;1'015.00
234163100183;851.00
234163100184;977.00
234164100183;909.00
234164100184;1'035.00
234188100183;484.00
234188184;609.00
234189100183;540.00
234189184;665.00
234190100183;519.00
234190184;644.00
234191100183;575.00
234191184;700.00
234192100183;608.00
234192184;733.00
234193100183;665.00
234193184;790.00
234194100183;625.00
234194184;750.00
234195100183;681.00
234195184;806.00
234196100183;608.00
234196184;733.00
234197100183;665.00
234197184;790.00
234198100183;625.00
234198184;750.00
234199100183;681.00
234199184;806.00
234171242;909.00
234171244;1'034.00
234172242;781.00
234172244;909.00
234173242;790.00
234173244;918.00
234174242;926.00
234174244;1'051.00
234176100244;989.00
234177100244;1'153.00
212650100242;326.00
212650100244;437.00
212651100241;326.00
212651100243;437.00
212652100241;326.00
212652100243;437.00
212653100242;352.00
212653100244;461.00
212654100241;371.00
212654100243;478.00
212655100241;371.00
212655100243;478.00
212656100242;362.00
212656100244;473.00
212657100241;352.00
212657100243;461.00
212658100241;352.00
212658100243;461.00
212659100241;380.00
212659100243;484.00
212660100241;380.00
212660100243;484.00
212661100242;376.00
212661100244;480.00
212662100241;390.00
212662100243;497.00
212663100241;390.00
212663100243;497.00
212699100241;329.00
212699100243;426.00
292145;926.00
292146;926.00
292147;926.00
292148;926.00
292149;926.00
292150;926.00
292151;926.00
292152;813.00
292153;669.00
212892100242;351.00
212625241;560.00
212625242;560.00
212625100293;694.00
212625100294;694.00
212627241;1'104.00
212627242;1'104.00
212627100293;1'238.00
212627100294;1'238.00
212900241;488.00
212900242;488.00
212900100293;626.00
212900100294;626.00
Alles anzeigen
Dateiname ist @ScriptDir & '\' & @YEAR & @MON & @MDAY & '-' & @HOUR & @MIN & @SEC & '-artikelnummer-verkaufspreis.csv'. Anpassbar in Zeile 142.
Viel Erfolg Swiffer damit 😀 .
Viele Grüße
Sven
1 Der benötigte WebDriver (entweder Chrome oder Firefox), wird via "_WD_UpdateDriver()" heruntergeladen. Daher ist dieser nicht im verlinktem Verzeichnis vorhanden.
Kurzes Update Swiffer:
Ich plane dir heute, morgen oder übermorgen meinen fertigen Vorschlag (eine WebDriver Variante) zu kommen zu lassen. Ähnlich wie bei diesem Thread post für Windi, wird das "Wie" per Kommentare beschrieben sein und die Funktionsnamen sowie Variablennamen entsprechend aussagekräftig sein.
Hilf mir nochmal bitte kurz:
Welchen Browser nutzt du? Ich habe dies glaube ich schon mal gefragt, finde es aber auf die Schnelle nicht 😅 .
Viele Grüße
Sven
Freut mich Werner, dass es auch bei dir gut läuft => so sollte es sein 😀 .
Was Opt('MustDeclareVars', 1) angeht kann ich sagen ja, es geht auch ohne, doch mein Entwicklerherz sagt nein 😅 .
Also dies sagt, dass du alle Variablen mit einem scope (bspw. Local oder Global (global immer nur außerhalb von Funktionen)) deklarieren musst, bevor du sie verwenden kannst. Dies dient der Fehlervermeidung unter gewissen Umständen. Wenn du also darauf verzichten willst - kein Problem. So lange du deine Variablen unter Kontrolle hast, sollte es wenige/keine Probleme mit sich bringen. Wenn du es strikt machst, daher mein Ansatz es einzuschalten, dann kannst du eben nicht in gewisse Probleme laufen.
💡 Lösche einfach die Zeile heraus oder passe alle Stellen die dir als Fehlermeldungen angezeigt werden an. Wenn du das nächste Mal ein Programm schreibst, dann nutze die Option vielleicht gleich von Anfang an, dann wird du nicht wieder vor dieser Entscheidung stehen müssen 😀 .
Übrigens:
Wenn dies deiner Anforderung entspricht (also mein Skript, die Abhängigkeiten etc.), dann schließe gern diesen Thread hier (auf erledigt setzen). Bei Bedarf mache dann einfach einen neuen auf.
Viele Grüße
Sven
Hi Windi (Werner) 👋 ,
hier nun mein Vorschlag, für deine Website Automatisierung/Steuerung mydrive.tomtom.com.
#AutoIt3Wrapper_AU3Check_Stop_OnWarning=y
#AutoIt3Wrapper_Run_Au3Stripper=y
#AutoIt3Wrapper_UseUpx=n
#Au3Stripper_Parameters=/sf /sv /mo /rm /rsln
Opt('MustDeclareVars', 1)
#include-once
#include <File.au3>
#include "..\lib\au3WebDriver\wd_helper.au3"
#include "..\lib\au3WebDriver\wd_capabilities.au3"
; initialization ---------------------------------------------------------------
Global Const $sDriver = 'Firefox' ; Chrome|Firefox
Global Const $bIsHeadlessMode = False ; False|True
Global Const $iDelay = 1000 ; Nicht unter eine Sekunde gehen, da durch die Animationen das Skript nicht mehr robust wäre
Global $sSession = null
; processing -------------------------------------------------------------------
_GetNewestDriver() ; Dient zum Aktualisieren, kann später auskommentiert werden
_SetLogLevel() ; Log level auf error ($_WD_DEBUG_Error) reicht meiner Meinung nach, kann natürlich variiert werden
_SetupDriver() ; Session für chrome oder firefox (WebDriver) erstellen
_Actions() ; Steuerung der MyDriveTomTom Webseite
_TeardownDriver() ; WebDriver wieder herunterfahren
; setup and teardown functions -------------------------------------------------
Func _GetNewestDriver()
_WD_UpdateDriver(StringLower($sDriver), _PathFull('..\util\webDriver'))
EndFunc
Func _SetLogLevel()
;~ $_WD_DEBUG = $_WD_DEBUG_None ; no logging
$_WD_DEBUG = $_WD_DEBUG_Error ; logging in case of Error
;~ $_WD_DEBUG = $_WD_DEBUG_Info ; logging with additional information
;~ $_WD_DEBUG = $_WD_DEBUG_Full ; logging with full details for developers
EndFunc
Func _SetupDriver()
_SetDriverOptions()
_WD_Startup()
Local $sCapabilities = _
(StringLower($sDriver) == 'chrome') _
? _CreateChromeDriverCapabilities() _
: _CreateFirefoxDriverCapabilities()
$sSession = _WD_CreateSession($sCapabilities)
EndFunc
Func _SetDriverOptions()
Local Const $sDriverExe = _
(StringLower($sDriver) == 'chrome') _
? 'chromedriver.exe' _
: 'geckodriver.exe'
Local Const $sDriverPath = _PathFull('..\util\webDriver')
Local Const $sDriverExeFilePath = $sDriverPath & '\' & $sDriverExe
Local Const $iPort = _
(StringLower($sDriver) == 'chrome') _
? 9515 _
: 4444
Local Const $sDriverParams = _
(StringLower($sDriver) == 'chrome') _
? '--verbose --log-path="' & $sDriverPath & '\chromedriver.log"' _
: '--log trace'
_WD_Option('Driver', $sDriverExeFilePath)
_WD_Option('Port', $iPort)
_WD_Option('DriverParams', $sDriverParams)
EndFunc
Func _CreateChromeDriverCapabilities()
_WD_CapabilitiesStartup()
_WD_CapabilitiesAdd('alwaysMatch', 'chrome')
_WD_CapabilitiesAdd('w3c', True)
_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
_WD_CapabilitiesAdd('args', '--window-size=1366,768')
If $bIsHeadlessMode Then
_WD_CapabilitiesAdd('args', '--headless')
EndIf
Return _WD_CapabilitiesGet()
EndFunc
Func _CreateFirefoxDriverCapabilities()
_WD_CapabilitiesStartup()
_WD_CapabilitiesAdd('alwaysMatch', 'firefox')
_WD_CapabilitiesAdd('browserName', 'firefox')
_WD_CapabilitiesAdd('acceptInsecureCerts', True)
_WD_CapabilitiesAdd('binary', 'C:\Program Files\Mozilla Firefox\firefox.exe') ; Ggf. einmalig diesen Pfad anpassen
_WD_CapabilitiesAdd('args', '--window-size=1366,768')
If $bIsHeadlessMode Then
_WD_CapabilitiesAdd('args', '--headless')
EndIf
Return _WD_CapabilitiesGet()
EndFunc
Func _TeardownDriver()
_WD_DeleteSession($sSession)
_WD_Shutdown()
EndFunc
; website functions ------------------------------------------------------------
Func _Actions()
_OpenMyDriveTomTomWebsite() ; Webseite öffnen
_AcceptsCookieConsent() ; Cookie consent bestätigen
Local Const $sUsername = 'abcabcb@email.de' ; Hier die richtige Email eintragen
Local Const $sPassword = 'abcabcabcab' ; Hier das richtige Passwort eintragen
_LoginToMyDrive($sUsername, $sPassword) ; Login
; Testdaten (55 Einträge)
Local $aTableOfAddresses[][2] = _ ; Diese Testdaten müssen natürlich mit deinen Echtdaten ersetzt werden
[ _
['Test Name Landhotel', 'Westfälische Str. 52 57368 Lennestadt'], _
['Test Name München', 'Landsberger Straße 10 80339 München'], _
['Test Name Nürnberg', 'Hermannstrasse 9 90439 Nürnberg'], _
['Test Name Berlin', 'Pohlstrasse 67 10785 Berlin'], _
['Test Name Köln', 'Aachener Straße 300 50933 Köln'], _
['Test Name 06 Berlin', 'Alt-Lübars 11 13469 Berlin'], _
['Test Name 07 Berlin', 'Am Großen Wannsee 69 14109 Berlin'], _
['Test Name 08 Berlin', 'Am Studio 16 12489 Berlin'], _
['Test Name 09 Berlin', 'Am Zeppelinpark 51 13591 Berlin'], _
['Test Name 10 Berlin', 'Andreasstraße 21a 10243 Berlin'], _
['Test Name 11 Berlin', 'Berkaer Straße 5 14199 Berlin'], _
['Test Name 12 Berlin', 'Berliner Allee 187 13088 Berlin'], _
['Test Name 13 Berlin', 'Boothstraße 1C 12207 Berlin'], _
['Test Name 14 Berlin', 'Boxhagener Straße 97 10245 Berlin'], _
['Test Name 15 Berlin', 'Daumstraße 63 13599 Berlin'], _
['Test Name 16 Berlin', 'Dessauer Straße 13 10963 Berlin'], _
['Test Name 17 Berlin', 'Drei-Linien-Weg 39 13125 Berlin'], _
['Test Name 18 Berlin', 'Emser Straße 40 12051 Berlin'], _
['Test Name 19 Berlin', 'Fabeckstraße 9 14195 Berlin'], _
['Test Name 20 Berlin', 'Flurweg 72d 12357 Berlin'], _
['Test Name 21 Berlin', 'Freystadter Weg 73 12489 Berlin'], _
['Test Name 22 Berlin', 'Grünbergallee 300 12526 Berlin'], _
['Test Name 23 Berlin', 'Güldenhofer Ufer 4 12437 Berlin'], _
['Test Name 24 Berlin', 'Havelchaussee 83A 14193 Berlin'], _
['Test Name 25 Berlin', 'Helene-Weigel-Platz 14 12681 Berlin'], _
['Test Name 26 Berlin', 'Hoppestraße 30 13409 Berlin'], _
['Test Name 27 Berlin', 'Kolmarer Straße 2 10405 Berlin'], _
['Test Name 28 Berlin', 'Kommandantenstraße 95 12205 Berlin'], _
['Test Name 29 Berlin', 'Lehmusstraße 38 12524 Berlin'], _
['Test Name 30 Berlin', 'Lotosweg 26 13467 Berlin'], _
['Test Name 31 Berlin', 'Ludwigsburger Weg 2 12247 Berlin'], _
['Test Name 32 Berlin', 'Müggelseedamm 300 12587 Berlin'], _
['Test Name 33 Berlin', 'Neukladower Allee 12 14089'], _
['Test Name 34 Berlin', 'Niederheideweg 25 13589 Berlin'], _
['Test Name 35 Berlin', 'Nonnendammallee 175 13599 Berlin'], _
['Test Name 36 Berlin', 'Osdorfer Straße 59 12207 Berlin'], _
['Test Name 37 Berlin', 'Pembabogen 1 13587 Berlin'], _
['Test Name 38 Berlin', 'Petunienweg 46 12357 Berlin'], _
['Test Name 39 Berlin', 'Porzer Straße 15 12524 Berlin'], _
['Test Name 40 Berlin', 'Réaumurstraße 39A 12207 Berlin'], _
['Test Name 41 Berlin', 'Rehbrücker Weg 3 14165 Berlin'], _
['Test Name 42 Berlin', 'Ribbecker Straße 7 10315 Berlin'], _
['Test Name 43 Berlin', 'Rudolfstraße 16 10245 Berlin'], _
['Test Name 44 Berlin', 'Schleizer Straße 67 13055 Berlin'], _
['Test Name 45 Berlin', 'Steinauer Straße 9 13125 Berlin'], _
['Test Name 46 Berlin', 'Straße 101 5 13125 Berlin'], _
['Test Name 47 Berlin', 'Tillmannsweg 5b 14109 Berlin'], _
['Test Name 48 Berlin', 'Trachenbergring 8 12249 Berlin'], _
['Test Name 49 Berlin', 'Waldstraße 73 12621 Berlin'], _
['Test Name 50 Berlin', 'Weisestraße 10 12049 Berlin'], _
['Test Name 51 Berlin', 'Wikingerufer 9a 10555 Berlin'], _
['Test Name 52 Berlin', 'Windenweg 58a 12357 Berlin'], _
['Test Name 53 Berlin', 'Windmühlenweg 11 13469 Berlin'], _
['Test Name 54 Berlin', 'Yorckstraße 74 10965 Berlin'], _
['Test Name 55 Berlin', 'Zeisigweg 18 12209 Berlin'] _
]
_AddAddressesToFavorites($aTableOfAddresses) ; Adress- und Namensdaten in Favoriten eintragen
EndFunc
Func _OpenMyDriveTomTomWebsite()
_NavigateTo('https://mydrive.tomtom.com/de_de/#mode=viewport+viewport=49.9071,7.8184,13,0,-0+ver=3')
EndFunc
Func _AcceptsCookieConsent()
Local Const $sAcceptCookieSelector = '//button[@id="cookie_banner_allow_button"][text()="Akzeptieren"]'
_ClickElement($sAcceptCookieSelector)
EndFunc
Func _LoginToMyDrive($sUsername, $sPassword)
Local Const $sLoginButtonSelector = '//button[contains(@id, "-loginButton")][text()="Anmelden"]'
Local Const $sIframeSelector = '//iframe[@id="iFrameResizer0"]'
_ClickElement($sLoginButtonSelector)
_EnterIFrame($sIframeSelector)
Local Const $sEmailInputSelector = '//input[@id="IDToken1"][@name="username"]'
Local Const $sPasswordInputSelector = '//input[@id="IDToken2"][@name="password"]'
Local Const $sLoginInputSelector = '//input[@id="loginButton"][@value="Anmelden"]'
_SetElementText($sEmailInputSelector, $sUsername)
_SetElementText($sPasswordInputSelector, $sPassword)
_ClickElement($sLoginInputSelector)
_LeaveIFrame()
EndFunc
Func _AddAddressesToFavorites($aTableOfAddresses)
Local Const $sMyPlacesSelector = '//div[@id="my_places_menu_item"][contains(., "Meine Orte")]'
Local Const $sFavoritesSelector = '//span[@id="my_places_favourites"][text()="Favoriten"]'
_ClickElement($sMyPlacesSelector)
_ClickElement($sFavoritesSelector)
Local Const $sAddNewPlaceSelector = '//div[@id="favourites_view_add_favourite_button"]//div[text()="Neuen Ort hinzufügen"]'
Local Const $sAddressInputSelector = '//input[@id="favourite_location_search_input_box"]'
Local Const $sNameInputSelector = '//div[@id="favourite_name_input_box"]/input'
Local Const $sFirstSuggestionListItemSelector = '//div[@id="favourite_search_suggestions_panel"]//div[contains(@id, "list_item_address_0")]'
Local Const $sDoneButtonSelector = '//button[@id="favourite_add_done_btn"][text()="Fertig"]'
Local Const $iAddressCount = Ubound($aTableOfAddresses) - 1
For $i = 0 To $iAddressCount Step 1
_ClickElement($sAddNewPlaceSelector)
Local $sName = $aTableOfAddresses[$i][0]
Local $sAddress = $aTableOfAddresses[$i][1]
ConsoleWrite($i & '/' & $iAddressCount & ' ==> ' & $sAddress & @CRLF)
_SetElementText($sAddressInputSelector, $sAddress)
_SetElementText($sNameInputSelector, $sName)
_ClickElement($sFirstSuggestionListItemSelector, $sAddress)
_ClickElement($sDoneButtonSelector)
Next
EndFunc
; webdriver functions ----------------------------------------------------------
Func _NavigateTo($sUrl)
_WD_Navigate($sSession, $sUrl)
_WD_LoadWait($sSession, $iDelay)
EndFunc
Func _FindElement($sSelector, $sMessage = '')
Local $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, $sSelector)
If @error <> $_WD_ERROR_Success Then
ConsoleWrite($sMessage & ' ==> Error for XPath selector ''' & $sSelector & '''.' & @CRLF)
_TeardownDriver() ; WebDriver vorzeitig herunterfahren (bei Bedarf auskommentieren)
EndIf
Return $sElement
EndFunc
Func _ClickElement($sSelector, $sMessage = '')
_WaitFor($sSelector)
_WD_ElementAction($sSession, _FindElement($sSelector, $sMessage), 'click')
EndFunc
Func _EnterIFrame($sSelector)
_WD_FrameEnter($sSession, _FindElement($sSelector))
EndFunc
Func _SetElementText($sSelector, $sValue)
_WaitFor($sSelector)
_WD_ElementAction($sSession, _FindElement($sSelector), 'value', $sValue)
EndFunc
Func _LeaveIFrame()
_WD_FrameLeave($sSession)
Sleep($iDelay)
EndFunc
Func _WaitFor($sSelector)
Local Const $iTimeoutInMilliseconds = 5000
Local Const $iElementVisibleFlag = 1
_WD_WaitElement($sSession, $_WD_LOCATOR_ByXPath, $sSelector, $iDelay, $iTimeoutInMilliseconds, $iElementVisibleFlag)
EndFunc
Alles anzeigen
Das Skript allein, ist so nicht lauffähig. Alle Abhängigkeiten2, welche benötigt werden, inklusive des Main.au3 Skriptes können hier heruntergeladen werden3.
Viel Erfolg Werner damit 😀 .
Viele Grüße
Sven
1 Als Hinweis für alle die, die dies komplett nachstellen wollen, bitte an Werner direkt wenden.
2 Der benötigte WebDriver (entweder Chrome oder Firefox), wird via "_WD_UpdateDriver()" heruntergeladen. Daher ist dieser nicht im verlinktem Verzeichnis vorhanden.
3 Achtung, auch wieder fake Login-Daten.
[...] Ps.: Einmal passt Dir mein Schreibstil für Code nicht - dann vermisst Du ein Vorwort und Inhaltsverzeichnis - Oberlehrer konnte ich schon auf der Schule nicht leiden!
Ich glaube wir finden sachlich einfach nicht zueinander Peter S. Taler 😔 . Müssen wir aber auch nicht unbedingt.
Ich unterstelle uns beiden gute Absichten 😀 , nur in unterschiedlichen Ausführungen, Ideen und Ansprüchen.
Daher ist es für mich in Ordnung wenn wir nicht einer Meinung sind. Streiten müssen wir jedoch wegen sowas nicht!
Ich lass dich in Zukunft einfach machen Peter S. Taler und nutze die Zeit um Anderen hier im Forum zu helfen oder sie zu unterstützen.
Viele Grüße
Sven
Danke für deine Bemühungen Peter S. Taler 👍 .
Ich nehme an, du willst später noch ein Inhaltsverzeichnis einfügen? Genau wie solche Kategorien wie ein Vorwort, an wen richtet sich das Buch, was kann man erwarten (was deckt das Buch ab, was nicht) usw.? Das wird eine ganze Menge Arbeit, um ein sachlich fundiertes Buch zu schreiben 🤔 .
Viele Grüße
Sven
Ja Fuffi778,
zumindest laut Hilfe wäre es so korrekt. Hier mal als Funktion dargestellt:
_MapNetworkDrive()
Func _MapNetworkDrive()
Local Const $sDevice = 'K:'
Local Const $sShare = '\\xy.dummy.ch\' & @UserName & '$'
Local Const $iMappingFlag = 0
Local Const $sDomainAndUser = 'domain\username'
Local Const $sPassword = 'password'
DriveMapAdd($sDevice, $sShare, $iMappingFlag, $sDomainAndUser, $sPassword)
If @error <> 0 Then
; Failure
EndIf
EndFunc
Alles anzeigen
Vielleicht hilft dir dies etwas besser beim Verständnis. Bzw. für den Fall, dass du etwas mehr Dynamic (Funktionsparameter bei Bedarf) brauchst. Weitere Shares, weitere User mit anderen Passwörtern etc.
Viel Erfolg 🤞 .
Viele Grüße
Sven
Ein Grund für mich es niemals zu nutzen
Kaffeetassen gehören einfach nit in einen PC
🤣 Jeder nach seiner Fasson Moombas. Gut für dich wenn du daran vorbei kommst, ich schaffe es beruflich nicht 🤪 .
Viele Grüße
Sven
Alles klar Werner, dann ist mein Array oben also genau vertauscht? Erst Name, dann Adresse, okay - passe ich noch an.
Die Angabe das es bis zu 100 Adressen sein können ist gut, dann Teste ich mal mit 75 und schaue wie es sich verhält.
Viele Grüße
Sven
Hallo erneut Windi ,
kannst du mir bitte mal ein Beispiel senden oder zumindest einen Auszug aus deiner Adresstabelle oder Adressliste geben?
Also ich nehme folgendes an:
läuft alles in einer Schleife, richtig? Also so würde ich es tun. Doch du kennst deinen fachlichen Ablauf und Kontext besser 😅 .
Im Moment habe ich zum Test einfach nur ein Array (eine Dummy Tabelle) erstellt, die in der Schleife durchgearbeitet wird. Diese besteht einfach aus Adresse und Name (was ja optional ist):
Local $aTableOfAddresses[][2] = _
[ _
['Westfälische Str. 52 57368 Lennestadt', 'Landhotel'], _
['Landsberger Straße 10 80339 München', 'Test München'], _
['Hermannstrasse 9 90439 Nürnberg', 'Test Nürnberg'], _
['Pohlstrasse 67 10785 Berlin', 'Test Berlin'], _
['Aachener Straße 300 50933 Köln', 'Test Köln'] _
]
Wenn deine CSV deutlich abweicht, dann müsste ich das in meinem Test berücksichtigen, darum frage ich.
Danke dir Werner 🤝 .
Viele Grüße
Sven
[...] Ein Buch ist immer gut aus meiner Sicht. Da kann man dann mal nachschlagen ohne onlien zu sein, man kann Notizen machen und Markierungen (farblich hinterlegen) tätigen. [...]
Gut finde ich das auch, absolut Alina. Doch es braucht auch Abnehmer, die den initialen Aufwand rechtfertigen und noch wichtiger, die Pflege und Erweiterung des Buches. Da sehe ich eben aus gegebenen Fakten heraus, kaum eine Chance 😔 .
Viele Grüße
Sven
Hi BugFix ,
ich bin begeistert ehrlich gesagt 😀 . Ich nutze zwar den ScITE Editor, wie du weißt, nicht mehr, dennoch finde ich es toll wie SciTE mit Lua interagiert bzw. andersherum.
Super nett finde ich es auch von dir, dass du dir (wenn ich es richtig beobachtet und verfolgt habe) einen konkreten Thread zum Anlass genommen hast, um das ganze umzusetzen 👍 .
Bei VSCode läuft eben fast alles was Extensions usw. angeht, über JavaScript (hatten wir schon das Thema). Danke aber für diese Arbeit und das Beispiel was mit Lua geht, denn ich muss mir unbedingt mal Lua genauer anschauen 🤝 .
Viele Grüße
Sven
Ehrlich gesagt sehe ich für ein AutoIt-Buch keine Zukunft Peter S. Taler. Dafür gibt es einfach bereits zu viele Tutorials in Form von Videos (auf Youtube oder Udemy) oder Tutorials/How-to's auf irgendwelchen Blog-Seiten sowie eben die beiden Foren (deutsch, englisch) und deren Wiki-Einträge, welche eher genutzt werden als ein Buch fürchte ich.
Bin leider der Auffassung das zu wenige sich noch mit Büchern zum Thema Programmierung auseinander setzen. Zumindest nicht für diesen zum Großteil semi-professionellen Bereich hier. Ich sehe schon den Vorteil und Mehrwert, doch wie bereits von Gun-Food erwähnt, ist der Anteil derer die mitwirken würden, wahrscheinlich einfach zu klein 😔 .
Schon allein der Fakt das Tweaky diesen Thread hier am 31. Dez. 2022 eröffnet hat und sich bisher nur sechs verschiedene Leute zu diesem Thema überhaupt geäußert haben, sich bisher nur water dazu bereit erklärt hat, eine Übersetzung vorzunehmen, macht doch leider deutlich wie gering das Interesse oder der Bedarf bei den Nutzern ist. Ein weiterer Fakt ist der Rückgang an Helfenden über die Jahre und die Versionen von AutoIt, siehe post #17.
[...] Generell bin ich nicht dagegen und ich richte gern eine entsprechende Seite ein, wenn sich eine große Anzahl an entsprechender Meldungen nicht nur in der Benutzung sondern auch in der Pflege des Wikis wiederfindet. [...]
Was bedeutet es denn für dich Rene, ein Wiki einzurichten? Sowohl monetär als auch zeitlich? Viel Aufwand? Was hälst du von der Idee ein GitHub Wiki ggf. zu nutzen, welches du public schaltest (siehe post #41)? Dein Projekt/Repo in dem die Hilfe liegt könnte man ja gleich nehmen? Hier im Forum würde es mir persönlich sogar besser gefallen (Wiki), aber wenn es den Aufwand nicht wert sein sollte, dann ist der einfache Weg auf GitHub vielleicht eine Option?
Danke das du dich als Betreiber der Seite/des Forums, zu Wort gemeldet hast 🤝 .
Weiterhin alles Gute!
[...] So Seite 1 unseres Autoit Buches ist schon fertig - wenn Ihr wollt? [...]
Leider hat sich ein Tippfehler im PDF eingeschlichen Peter S. Taler "ERTELLT" anstatt "ERSTELLT".
Viele Grüße
Sven
ich glaube wir reden zwar vom Gleichen, jedoch mit einer unterschiedlichen Idee dahinter 😅 .
Punkt (1)
[...] Damals hatte mir Dan, vom englischen Forum geantwortet.
Von ihm habe ich auch die Fehlerabfrage übernommen. [...]
[...] Prinzipiell mache ich das auch immer so, wie oben angegeben.
AutoIt$sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "/html/body/div[1]/aside[1]/div[2]/div/div[2]/div/div/form/button") ;_WD_HighlightElement($sSession, $sElement, 3) If @error = $_WD_ERROR_Success Then _WD_ElementAction($sSession, $sElement, 'click') Sleep(500) ;oder so $sElement_login_btn = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//*[@id="btnLogin"]')[...] Gruß gmmg
Beides okay, in Ordnung und richtig 👍 . Allerdings ist eure Herangehensweise, wenn ich das richtig bewerte diese, dass ihr nur wenn kein Fehler auftritt den nächsten Schritt tun wollt. Also bei @error = $_ED_ERROR_Success dann in die nächste Aktion geht.
Mein Hinweis bezog sich darauf, dass ich genau andersherum auswerten möchte und beim Auftritt von einem Fehler aus dem Programm aussteigen möchte (oder einfach nur eine MsgBox() oder einen Hinweis auf der Konsole ConsoleWrite() ausgeben möchte), aber eben nicht weiter im Kontext machen möchte. Vereinfacht gesagt vertausche ich die if-else Bedingungen, mehr nicht (IF "Fehler", DANN Return ELSE "weiter im Kontext"). Dies entspricht dem "REP (return early pattern)". Ist jedoch nicht weiter wichtig (dient aber der Erklärung).
gmmg macht dies sicher richtig und du auch Werner, allerdings in dem konkreten Beispiel deines Skriptes, macht es keinen Sinn meiner Meinung nach. Du weißt das es "irgendwo" instabil ist und eine Aktion nicht korrekt ausgeführt wird, weißt jedoch auf Grund des Logs in der Konsole auch nicht genau welche Stelle es ist. Daher die Variante mit "sobald fehlgeschlagen, aussteigen, den letzten Log Eintrag anschauen und "aha, dort ist es", Ende, finito 😅 .
Ich denke ich kann morgen das Skript und die Abhängigkeiten zur Verfügung stellen Werner, dann könnt ihr sehr gern nochmal etwas dazu sagen: ob gut oder nicht, bin absolut offen für Verbesserungen 😇 .
Punkt (2)
[...] Dan schrieb dass es an den iFrames hängt.
Bei meine Orte z. B. [...]
Ja, konkret ist die Seite in der Automatisierung etwas instabil, weil sie mehrerer iframes einbindet und mit _WD_FrameEnter() und _WD_FrameLeave() gearbeitet werden muss.
[...] Weder ChroPath noch SelectorsHub findet da was vernünftiges.
Hab dann solange rumprobiert bis ich auf den xPath kam.
Aber wie du schon geschrieben hast, es läuft nicht stabil. [...]
Des Weiteren nutzt die Webseite Animationen, mit denen der WebDriver nicht immer ohne Zutun umgehen kann. Dies erzeugt das Zeitverhaltenproblem. Nun kann man dies durch genügend Wartezeiten (keine eigenen Sleeps Sleep() sondern über _WaitFor() bzw. _WD_WaitElement()) beheben und in Kauf nehmen, dass das Skript nicht gerade das Schnellste ist (so löse ich das übrigens auch bei deinem Skript/deinem Fall Windi) oder man macht es ganz genau und robust in dem man mit JavaScript Events arbeitet und wirklich wartet bis eine gewissen Animation abgeschlossen ist. Diese Variante ist deutlich aufwändiger und zumindest für mich, nur im beruflichen Kontext relevant.
Naja, Verbesserungspotenzial gibt es immer 😅 . Melde mich morgen wieder.
Viele Grüße
Sven