Wie kann ich eine Seite mit dynamischem Inhalt (erstellt durch JavaScript) in Python kratzen?
Lesezeit: 12 Minuten
mokopera
Ich versuche, einen einfachen Web-Scraper zu entwickeln. Ich möchte einfachen Text ohne HTML-Markup extrahieren. Mein Code funktioniert mit einfachem (statischem) HTML, aber nicht, wenn Inhalte durch in die Seite eingebettetes JavaScript generiert werden.
Insbesondere wenn ich benutze urllib2.urlopen(request) um den Seiteninhalt zu lesen, wird nichts angezeigt, was durch den JavaScript-Code hinzugefügt würde, da dieser Code wird nicht ausgeführt überall. Normalerweise würde es vom Webbrowser ausgeführt, aber das ist nicht Teil meines Programms.
Wie kann ich in meinem Python-Code auf diesen dynamischen Inhalt zugreifen?
Siehe auch Kann scrapy verwendet werden, um dynamische Inhalte von Websites zu entfernen, die AJAX verwenden? für Antworten speziell zu Scrapy.
Klingt so, als ob Sie etwas Schwereres brauchen könnten, versuchen Sie es mit Selenium oder Watir.
Bitte beachten Sie, dass die am besten bewertete Antwort zuletzt im Jahr 2017 aktualisiert wurde und ab 2021 veraltet ist, da PhantomJS und Dryscrape veraltet sind. Ich empfehle, den gesamten Thread zu lesen, bevor Sie eine der empfohlenen Techniken ausprobieren.
– ggorlen
30. März 2021 um 21:46 Uhr
avi
EDIT September 2021: phantomjs wird auch nicht mehr gepflegt
BEARBEITEN 30. Dezember 2017: Diese Antwort erscheint in den Top-Ergebnissen der Google-Suche, daher habe ich beschlossen, sie zu aktualisieren. Die alte Antwort ist immer noch am Ende.
dryscape wird nicht mehr gepflegt und die von dryscape-Entwicklern empfohlene Bibliothek ist nur Python 2. Ich habe festgestellt, dass die Verwendung der Python-Bibliothek von Selenium mit Phantom JS als Webtreiber schnell genug und einfach ist, um die Arbeit zu erledigen.
Sobald Sie installiert haben Phantom JSStellen Sie sicher, dass phantomjs binär ist im aktuellen Pfad verfügbar:
phantomjs --version
# result:
2.1.1
#Example Um ein Beispiel zu geben, habe ich eine Beispielseite mit folgendem HTML-Code erstellt. (Verknüpfung):
@Expenzor Ich arbeite an Windows. PhantomJS funktioniert einwandfrei.
– Aakash Choubey
12. Januar 2018 um 10:43 Uhr
Erwähnenswert ist, dass PhantomJS eingestellt wurde und nicht mehr aktiv weiterentwickelt wird, da Chrome jetzt Headless unterstützt. Die Verwendung von Headless Chrome/Firefox wird empfohlen.
– Sytech
23. März 2018 um 20:42 Uhr
Ich bekomme folgende Warnung: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead. Vielleicht hat @sytech von Selenium-Unterstützung gesprochen?
Wir erhalten nicht die richtigen Ergebnisse, da alle mit Javascript generierten Inhalte auf dem DOM gerendert werden müssen. Wenn wir eine HTML-Seite abrufen, rufen wir das anfängliche, nicht durch Javascript modifizierte DOM ab.
Daher müssen wir den Javascript-Inhalt rendern, bevor wir die Seite crawlen.
Da Selen in diesem Thread schon oft erwähnt wurde (und wie langsam es manchmal wird, wurde auch erwähnt), werde ich zwei andere mögliche Lösungen auflisten.
Docker in unserer Maschine installiert. Dies ist bis zu diesem Punkt ein Plus gegenüber anderen Lösungen, da es eine vom Betriebssystem unabhängige Plattform verwendet.
Splash installieren Befolgen Sie die Anweisungen für unser entsprechendes Betriebssystem. Zitat aus Splash-Dokumentation:
Splash ist ein Javascript-Rendering-Dienst. Es ist ein leichter Webbrowser mit einer HTTP-API, implementiert in Python 3 mit Twisted und QT5.
Im Wesentlichen werden wir Splash verwenden, um mit Javascript generierte Inhalte zu rendern.
Führen Sie den Splash-Server aus: sudo docker run -p 8050:8050 scrapinghub/splash.
Angenommen, wir haben bereits ein Scrapy-Projekt erstellt (falls nicht, lass uns einen machen), werden wir der Anleitung folgen und die aktualisieren settings.py:
Dann gehen Sie zu Ihrem Scrapy-Projekt settings.py und legen Sie diese Middlewares fest:
Die URL des Splash-Servers (wenn Sie Win oder OSX verwenden, sollte dies die URL des Docker-Computers sein: How to get a Docker container’s IP address from the host?):
SPLASH_URL = 'http://localhost:8050'
Und schließlich müssen Sie auch diese Werte festlegen:
In einem normalen Spider haben Sie Request-Objekte, mit denen Sie URLs öffnen können. Wenn die Seite, die Sie öffnen möchten, JS-generierte Daten enthält, müssen Sie SplashRequest (oder SplashFormRequest) verwenden, um die Seite zu rendern. Hier ist ein einfaches Beispiel:
class MySpider(scrapy.Spider):
name = "jsscraper"
start_urls = ["http://quotes.toscrape.com/js/"]
def start_requests(self):
for url in self.start_urls:
yield SplashRequest(
url=url, callback=self.parse, endpoint="render.html"
)
def parse(self, response):
for q in response.css("div.quote"):
quote = QuoteItem()
quote["author"] = q.css(".author::text").extract_first()
quote["quote"] = q.css(".text::text").extract_first()
yield quote
SplashRequest rendert die URL als HTML und gibt die Antwort zurück, die Sie in der callback(parse)-Methode verwenden können.
Lösung 2: Nennen wir das im Moment (Mai 2018) experimentell… Diese Lösung ist für Pythons Version 3.6 nur (im Moment).
Kennst du Anfragen Modul (gut, wer nicht)?
Jetzt hat es ein kleines Geschwisterchen, das das Web durchsucht: Anfragen-HTML:
Diese Bibliothek soll das Parsen von HTML (zB das Scrapen des Webs) so einfach und intuitiv wie möglich machen.
from requests_html import HTMLSession
session = HTMLSession()
r = session.get(a_page_url)
Rendern Sie die Antwort, um die von Javascript generierten Bits zu erhalten:
r.html.render()
Endlich scheint sich das Modul zu bieten Schabefähigkeiten.
Alternativ können wir den gut dokumentierten Weg versuchen der Verwendung von BeautifulSoup mit dem r.html Objekt, das wir gerade gerendert haben.
Können Sie erläutern, wie Sie nach dem Aufruf von .render() den vollständigen HTML-Inhalt mit geladenen JS-Bits erhalten? Nach diesem Punkt stecke ich fest. Ich sehe nicht alle iFrames, die normalerweise von JavaScript in die Seite eingefügt werden r.html.html Objekt.
– fIwJlxSzAPHEZIl
13. Dezember 2018 um 20:24 Uhr
@anon58192932 Da dies im Moment eine experimentelle Lösung ist und ich nicht weiß, was genau Sie damit erreichen möchten, kann ich nicht wirklich etwas vorschlagen … Sie können hier auf SO eine neue Frage erstellen, wenn Sie dies nicht getan haben noch eine Lösung gefunden
– John Moutafis
2. Januar 2019 um 13:57 Uhr
Ich habe diesen Fehler erhalten: RuntimeError: HTMLSession kann nicht innerhalb einer vorhandenen Ereignisschleife verwendet werden. Verwenden Sie stattdessen AsyncHTMLSession.
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
Selenium ist für solche Dinge wirklich schwer, das wäre unnötig langsam und erfordert einen Browserkopf, wenn Sie PhantomJS nicht verwenden, aber das würde funktionieren.
– Joshua Hedges
28. Juli 2017 um 16:27 Uhr
@JoshuaHedges Sie können andere Standardbrowser im Headless-Modus ausführen.
Wenn Sie die jemals verwendet haben Requests module for python zuvor habe ich kürzlich herausgefunden, dass der Entwickler ein neues Modul namens erstellt hat Requests-HTML die jetzt auch die Fähigkeit hat, JavaScript zu rendern.
Im Wesentlichen, sobald Sie die Requests-HTML Modul, das folgende Beispiel, das ist auf dem obigen Link angezeigtzeigt, wie Sie dieses Modul verwenden können, um eine Website zu scrapen und in der Website enthaltenes JavaScript zu rendern:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('http://python-requests.org/')
r.html.render()
r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>' #This is the result.
Das habe ich kürzlich durch ein YouTube-Video erfahren. Klicken Sie hier! um das YouTube-Video anzusehen, das die Funktionsweise des Moduls demonstriert.
Es hört sich so an, als ob auf die Daten, nach denen Sie wirklich suchen, über eine sekundäre URL zugegriffen werden kann, die von einem JavaScript auf der Primärseite aufgerufen wird.
Während Sie versuchen könnten, Javascript auf dem Server auszuführen, um dies zu handhaben, könnte ein einfacherer Ansatz darin bestehen, die Seite mit Firefox zu laden und ein Tool wie zu verwenden Karl oder Feuerwanze um genau zu identifizieren, was diese sekundäre URL ist. Dann können Sie diese URL einfach direkt nach den Daten abfragen, an denen Sie interessiert sind.
@Kris Nur für den Fall, dass jemand darüber stolpert und es anstelle von etwas so Schwerem wie Selen ausprobieren möchte, hier ist ein kurzes Beispiel. Das öffnet die Teiledetailseite für eine Sechskantmutter auf der McMaster-Carr-Website. Der Inhalt ihrer Website wird hauptsächlich mit Javascript abgerufen und enthält nur sehr wenige native Seiteninformationen. Wenn Sie die Entwicklertools Ihres Browsers öffnen, zur Registerkarte „Netzwerk“ navigieren und die Seite aktualisieren, können Sie alle von der Seite gestellten Anforderungen sehen und die relevanten Daten finden (in diesem Fall die Teildetail-HTML).
– SweepingsDemon
13. August 2018 um 18:02 Uhr
Das ist eine andere URL, die auf der Registerkarte Netzwerk des Firefox-Devtools zu finden ist, die, wenn sie befolgt wird, den HTML-Code für die meisten Teilinformationen enthält und einige der Parameter offenlegt, die erforderlich sind, um einfach zu anderen Teilinformationen zu navigieren, um das Scraping zu erleichtern. Dieses spezielle Beispiel ist nicht besonders nützlich, da der Preis von einer anderen Javascript-Funktion generiert wird, sollte aber als Einführung für alle dienen, die Stephens Rat befolgen möchten.
– SweepingsDemon
13. August 2018 um 18:10 Uhr
Robbie
Dies scheint auch eine gute Lösung zu sein, entnommen aus a toller Blogbeitrag
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtWebKit import *
from lxml import html
#Take this class for granted.Just use result of rendering.
class Render(QWebPage):
def __init__(self, url):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().load(QUrl(url))
self.app.exec_()
def _loadFinished(self, result):
self.frame = self.mainFrame()
self.app.quit()
url="http://pycoders.com/archive/"
r = Render(url)
result = r.frame.toHtml()
# This step is important.Converting QString to Ascii for lxml to process
# The following returns an lxml element tree
archive_links = html.fromstring(str(result.toAscii()))
print archive_links
# The following returns an array containing the URLs
raw_links = archive_links.xpath('//div[@class="campaign"]/a/@href')
print raw_links
@Kris Nur für den Fall, dass jemand darüber stolpert und es anstelle von etwas so Schwerem wie Selen ausprobieren möchte, hier ist ein kurzes Beispiel. Das öffnet die Teiledetailseite für eine Sechskantmutter auf der McMaster-Carr-Website. Der Inhalt ihrer Website wird hauptsächlich mit Javascript abgerufen und enthält nur sehr wenige native Seiteninformationen. Wenn Sie die Entwicklertools Ihres Browsers öffnen, zur Registerkarte „Netzwerk“ navigieren und die Seite aktualisieren, können Sie alle von der Seite gestellten Anforderungen sehen und die relevanten Daten finden (in diesem Fall die Teildetail-HTML).
– SweepingsDemon
13. August 2018 um 18:02 Uhr
Das ist eine andere URL, die auf der Registerkarte Netzwerk des Firefox-Devtools zu finden ist, die, wenn sie befolgt wird, den HTML-Code für die meisten Teilinformationen enthält und einige der Parameter offenlegt, die erforderlich sind, um einfach zu anderen Teilinformationen zu navigieren, um das Scraping zu erleichtern. Dieses spezielle Beispiel ist nicht besonders nützlich, da der Preis von einer anderen Javascript-Funktion generiert wird, sollte aber als Einführung für alle dienen, die Stephens Rat befolgen möchten.
– SweepingsDemon
13. August 2018 um 18:10 Uhr
seco
Selen eignet sich am besten zum Scrapen von JS- und Ajax-Inhalten.
Klingt so, als ob Sie etwas Schwereres brauchen könnten, versuchen Sie es mit Selenium oder Watir.
– Wim
8. November 2011 um 11:16 Uhr
Ich habe dies erfolgreich in Java gemacht (ich habe das Cobra-Toolkit verwendet lobobrowser.org/cobra.jsp) Da Sie in Python hacken möchten (immer eine gute Wahl), empfehle ich diese beiden Optionen: – packtpub.com/article/web-scraping-with-python-part-2 – blog.databigbang.com/web-scraping-ajax-and-javascript-sites
– bpgergo
8. November 2011 um 11:34 Uhr
Bitte beachten Sie, dass die am besten bewertete Antwort zuletzt im Jahr 2017 aktualisiert wurde und ab 2021 veraltet ist, da PhantomJS und Dryscrape veraltet sind. Ich empfehle, den gesamten Thread zu lesen, bevor Sie eine der empfohlenen Techniken ausprobieren.
– ggorlen
30. März 2021 um 21:46 Uhr