
Nachfolger von Undetected-Chromedriver. Bietet ein blitzschnelles Framework für Web-Automatisierung, Web-Scraping, Bots und alle anderen kreativen Ideen, die normalerweise von lästigen Anti-Bot-Systemen wie Captcha / CloudFlare / Imperva / hCaptcha behindert werden.
Die direkte Kommunikation bietet einen noch besseren Schutz gegen Web Application Firewalls (WAFs), während die Leistung massiv gesteigert wird. Dieses Modul ist – anders als undetected-chromedriver – vollständig asynchron.
Was dieses Paket von anderen bekannten Paketen unterscheidet, ist die Optimierung, bei den meisten Anti-Bot-Lösungen unentdeckt zu bleiben.
Ein weiterer Schwerpunkt liegt auf Benutzerfreundlichkeit und schnellem Prototyping, daher kannst du erwarten, dass vieles -as is- funktioniert,
wobei die meisten Methodenparameter best practice-Standardwerte haben.
Mit 1 oder 2 Zeilen ist es einsatzbereit und liefert standardmäßig eine Best-Practice-Konfiguration.
Es räumt anschließend die erzeugten Dateien (Profil) auf.
Bekannt funktionsfähig mit
Während Benutzerfreundlichkeit und Komfort wichtig sind, ist es auch einfach, alles mithilfe der gesamten Palette der verfügbaren CDP-Domänen, Methoden und Ereignisse vollständig anzupassen.
Keine Abhängigkeit von einer Chromedriver-Binärdatei oder Selenium
In 1 Zeile Code einsatzbereit*
verwendet bei jedem Lauf ein frisches Profil und räumt beim Beenden auf
Cookies in Datei speichern und laden, um mühsame Login-Schritte zu vermeiden
tab.find("sometext")
tab.find_all("sometext")
tab.select("a[class*=something]")
tab.select_all("a[href] > div > img")
Intelligente und performante Elementsuche per Selektor oder Text, einschließlich Iframe-Inhalten.
Dies kann auch als Wartebedingung für das Erscheinen eines Elements verwendet werden, da es für die Dauer von erneut versucht, bis es gefunden wird. So kann ein await tab.select('body') als Indikator dafür verwendet werden, ob eine Seite geladen ist.
Die find-Methode sucht nach Text, gibt aber nicht naiv das erste passende Element zurück, sondern gleicht Kandidaten anhand der ähnlichsten Textlänge ab (die kürzeste gewinnt). Dadurch liefern Suchvorgänge wie tab.find('accept all') den tatsächlichen Cookie-Button anstelle eines Skripts im Kopfbereich.
kann sich mit einer laufenden Chrome-Debug-Sitzung verbinden
beschreibendes __repr__ für Elemente, das das Element als HTML darstellt
Hilfsfunktion zum Konvertieren einer laufenden undetected_chromedriver.Chrome-Instanz in eine nodriver.Browser-Instanz und von dort aus fortzufahren
vollgepackt mit Helfern und Hilfsmethoden für die am häufigsten verwendeten und wichtigsten Operationen
Teile wurden neu geschrieben, um im Protokoll Flat-Verbindungen zu verwenden.
Warum?
- Iframes werden in den meisten Operationen berücksichtigt.
- Der Tab hat eine neue Methode: await tab.get_frames(), die überprüfbare Iframes zurückgibt.
- find() berücksichtigt Iframes, sodass du sogar nach „verify you are human“ suchen und das Verifizierungskontrollkästchen in JS-Challenges anklicken kannst.
Da dies einiges an Umschreiben erforderte, teste bitte gründlich, insbesondere wenn du große Projekte ausführst.
tab.xpath(selector, timeout=2.5)findet Knoten mithilfe des XPath-Selektors! siehe tab xpath in der API-Dokumentation
tab.cf_verify()findet das Kontrollkästchen und klickt es erfolgreich dies funktioniert nur, wenn du NICHT im Expertenmodus bist. derzeit ist nur eingebautes Englisch verfügbar erfordert die Installation des Pakets opencv-python
tab.bypass_insecure_connection_warning()Bequemlichkeitsmethode für die Warnung vor unsicheren Seiten, zum Beispiel wenn ein Zertifikat ungültig ist.
tab.open_external_debugger()ermöglicht es dir, den Tab zu inspizieren, ohne deine Verbindung zu unterbrechen
tab.get_local_storage()LocalStorage-Inhalt abrufen
tab.set_local_storage(dict)LocalStorage-Inhalt festlegen
tab.add_handler(someEvent, callback)Der Callback kann ein einzelnes Argument (event) oder 2 Argumente (event, tab) akzeptieren.
start(expert=True)treibt einiges an Hacking für erfahrenere Benutzer. Es deaktiviert Websicherheit und Origin-Trials und stellt außerdem sicher, dass Shadow-Roots immer geöffnet sind. Dadurch wirst du allerdings besser erkennbar!
du benötigst Chrome (oder einen auf Chromium basierenden Browser), installiert vorzugsweise am Standardort auf dem Rechner, auf dem du dieses Paket verwendest.
Wenn du auf einer Headless-Maschine arbeitest, z. B. auf AWS oder in einer anderen Umgebung ohne Display, ist es am besten, ein Xvfb-Werkzeug zu verwenden, um einen Bildschirm zu emulieren. Alternativ kann dieses Paket im Headless-Modus verwendet werden.
pip install nodriver
pip install -U nodriver
Das Ziel dieses Projekts (genau wie bei undetected-chromedriver, vor langer Zeit) ist es, es kurz und einfach zu halten, damit du schnell einen Editor oder eine interaktive Sitzung öffnen, ein paar Zeilen eintippen oder einfügen und loslegen kannst.
import nodriver as uc
async def main():
browser = await uc.start()
page = await browser.get('https://www.nowsecure.nl')
... further code ...
if __name__ == '__main__':
# since asyncio.run never worked (for me)
uc.loop().run_until_complete(main())
Ich lasse den Async-Boilerplate hier weg
from nodriver import *
browser = await start(
headless=False,
user_data_dir="/path/to/existing/profile", # by specifying it, it won't be automatically cleaned up when finished
browser_executable_path="/path/to/some/other/browser",
browser_args=['--some-browser-arg=true', '--some-other-option'],
lang="en-US" # this could set iso-language-code in navigator, not recommended to change
)
tab = await browser.get('https://somewebsite.com')
Ich lasse den Async-Boilerplate hier weg
from nodriver import *
config = Config()
config.headless = False
config.user_data_dir="/path/to/existing/profile", # by specifying it, it won't be automatically cleaned up when finished
config.browser_executable_path="/path/to/some/other/browser",
config.browser_args=['--some-browser-arg=true', '--some-other-option'],
config.lang="en-US" # this could set iso-language-code in navigator, not recommended to change
)
import nodriver
async def main():
browser = await nodriver.start()
page = await browser.get('https://www.nowsecure.nl')
await page.save_screenshot()
await page.get_content()
await page.scroll_down(150)
elems = await page.select_all('*[src]')
for elem in elems:
await elem.flash()
page2 = await browser.get('https://twitter.com', new_tab=True)
page3 = await browser.get('https://github.com/ultrafunkamsterdam/nodriver', new_window=True)
for p in (page, page2, page3):
await p.bring_to_front()
await p.scroll_down(200)
await p # wait for events to be processed
await p.reload()
if p != page3:
await p.close()
if __name__ == '__main__':
# since asyncio.run never worked (for me)
uc.loop().run_until_complete(main())
Automatisierung der Twitter/X-Kontoerstellung
Ein konkreteres Beispiel, das im Ordner ./example/ zu finden ist, zeigt ein Skript zum Erstellen eines Twitter-Kontos.
import random
import string
import logging
logging.basicConfig(level=30)
import nodriver as uc
months = [
"january",
"february",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"october",
"november",
"december",
]
async def main():
driver = await uc.start()
tab = await driver.get("https://twitter.com")
# wait for text to appear instead of a static number of seconds to wait
# this does not always work as expected, due to speed.
print('finding the "create account" button')
create_account = await tab.find("create account", best_match=True)
print('"create account" => click')
await create_account.click()
print("finding the email input field")
email = await tab.select("input[type=email]")
# sometimes, email field is not shown, because phone is being asked instead
# when this occurs, find the small text which says "use email instead"
if not email:
use_mail_instead = await tab.find("use email instead")
# and click it
await use_mail_instead.click()
# now find the email field again
email = await tab.select("input[type=email]")
randstr = lambda k: "".join(random.choices(string.ascii_letters, k=k))
# send keys to email field
print('filling in the "email" input field')
await email.send_keys("".join([randstr(8), "@", randstr(8), ".com"]))
# find the name input field
print("finding the name input field")
name = await tab.select("input[type=text]")
# again, send random text
print('filling in the "name" input field')
await name.send_keys(randstr(8))
# since there are 3 select fields on the tab, we can use unpacking
# to assign each field
print('finding the "month" , "day" and "year" fields in 1 go')
sel_month, sel_day, sel_year = await tab.select_all("select")
# await sel_month.focus()
print('filling in the "month" input field')
await sel_month.send_keys(months[random.randint(0, 11)].title())
# await sel_day.focus()
# i don't want to bother with month-lengths and leap years
print('filling in the "day" input field')
await sel_day.send_keys(str(random.randint(0, 28)))
# await sel_year.focus()
# i don't want to bother with age restrictions
print('filling in the "year" input field')
await sel_year.send_keys(str(random.randint(1980, 2005)))
await tab
# let's handle the cookie nag as well
cookie_bar_accept = await tab.find("accept all", best_match=True)
if cookie_bar_accept:
await cookie_bar_accept.click()
await tab.sleep(1)
next_btn = await tab.find(text="next", best_match=True)
# for btn in reversed(next_btns):
await next_btn.mouse_click()
print("sleeping 2 seconds")
await tab.sleep(2) # visually see what part we're actually in
print('finding "next" button')
next_btn = await tab.find(text="next", best_match=True)
print('clicking "next" button')
await next_btn.mouse_click()
# just wait for some button, before we continue
await tab.select("[role=button]")
print('finding "sign up" button')
sign_up_btn = await tab.find("Sign up", best_match=True)
# we need the second one
print('clicking "sign up" button')
await sign_up_btn.click()
print('the rest of the "implementation" is out of scope')
# further implementation outside of scope
await tab.sleep(10)
driver.stop()
# verification code per mail
if __name__ == "__main__":
# since asyncio.run never worked (for me)
# i use
uc.loop().run_until_complete(main())