Kann scrapy verwendet werden, um dynamische Inhalte von Websites zu schaben, die AJAX verwenden?

Lesezeit: 13 Minuten

Kann scrapy verwendet werden um dynamische Inhalte von Websites zu
Joseph

Ich habe vor kurzem Python gelernt und tauche meine Hand in den Bau eines Web-Scraper ein. Es ist überhaupt nichts Besonderes; Sein einziger Zweck besteht darin, die Daten von einer Wett-Website abzurufen und diese Daten in Excel zu übertragen.

Die meisten Probleme sind lösbar und ich habe ein gutes kleines Durcheinander. Allerdings stoße ich bei einem Problem auf eine massive Hürde. Wenn eine Site eine Tabelle mit Pferden lädt und aktuelle Wettpreise auflistet, sind diese Informationen in keiner Quelldatei enthalten. Der Hinweis ist, dass diese Daten manchmal live sind, wobei die Zahlen offensichtlich von einem entfernten Server aktualisiert werden. Das HTML auf meinem PC hat einfach ein Loch, wo ihre Server all die interessanten Daten durchdrücken, die ich brauche.

Jetzt ist meine Erfahrung mit dynamischen Webinhalten gering, also habe ich Probleme, mich mit dieser Sache zurechtzufinden.

Ich denke, Java oder Javascript ist ein Schlüssel, das taucht oft auf.

Der Scraper ist einfach eine Quotenvergleichsmaschine. Einige Websites haben APIs, aber ich brauche diese für diejenigen, die dies nicht tun. Ich verwende die Scrapy-Bibliothek mit Python 2.7

Ich entschuldige mich, wenn diese Frage zu offen ist. Kurz gesagt, meine Frage lautet: Wie kann Scrapy verwendet werden, um diese dynamischen Daten zu scrapen, damit ich sie verwenden kann? Damit ich diese Wettquotendaten in Echtzeit abrufen kann?

  • Wie kann ich diese Daten erhalten, die Daten, die dynamisch und live sind?

    – Joseph

    18. Dezember 2011 um 6:20 Uhr

  • Wenn Ihre Seite Javascript enthält, versuchen Sie dies

    – wiedereingeschaltetv

    18. Dezember 2011 um 6:36 Uhr

  • Probieren Sie einige an Firefox Erweiterungen wie httpFox oder liveHttpHeaders und laden Sie eine Seite, die eine Ajax-Anforderung verwendet. Scrapy identifiziert die Ajax-Anfragen nicht automatisch, Sie müssen manuell nach der entsprechenden Ajax-URL suchen und dann damit eine Anfrage stellen.

    – Aamir Rind

    18. Dezember 2011 um 7:22 Uhr

  • Prost, ich gebe den Firefox-Erweiterungen einen Zauber

    – Joseph

    20. Dezember 2011 um 11:15 Uhr

  • Es gibt eine Reihe von Open-Source-Lösungen. Wenn Sie jedoch nach einer einfachen und schnellen Möglichkeit suchen, dies insbesondere für große Arbeitslasten zu tun, sehen Sie sich SnapSearch an (snapsearch.io). Es wurde für JS-, HTML5- und SPA-Sites entwickelt, die eine Suchmaschinen-Crawlbarkeit erfordern. Probieren Sie die Demo aus (wenn leerer Inhalt vorhanden ist, bedeutet dies, dass die Website tatsächlich keinen Textinhalt zurückgegeben hat, was möglicherweise eine 301-Weiterleitung bedeutet).

    – CMCDragonkai

    3. April 2014 um 6:21 Uhr

Hier ist ein einfaches Beispiel für scrapy mit einer AJAX-Anfrage. Sehen Sie sich die Website an rubin-kazan.ru.

Alle Nachrichten werden mit einem AJAX-Request geladen. Mein Ziel ist es, diese Nachrichten mit all ihren Attributen (Autor, Datum, …) abzurufen:

Geben Sie hier die Bildbeschreibung ein

Wenn ich den Quellcode der Seite analysiere, kann ich nicht alle diese Meldungen sehen, weil die Webseite AJAX-Technologie verwendet. Aber ich kann mit Firebug von Mozilla Firefox (oder einem gleichwertigen Tool in anderen Browsern) die HTTP-Anfrage analysieren, die die Nachrichten auf der Webseite generiert:

Geben Sie hier die Bildbeschreibung ein

Es lädt nicht die ganze Seite neu, sondern nur die Teile der Seite, die Nachrichten enthalten. Dazu klicke ich unten auf eine beliebige Seitenzahl:

Geben Sie hier die Bildbeschreibung ein

Und ich beobachte die HTTP-Anforderung, die für den Nachrichtentext verantwortlich ist:

Geben Sie hier die Bildbeschreibung ein

Nachdem ich fertig bin, analysiere ich die Header der Anfrage (ich muss zitieren, dass ich diese URL aus der Quellseite aus dem var-Abschnitt extrahieren werde, siehe Code unten):

Geben Sie hier die Bildbeschreibung ein

Und der Formulardateninhalt der Anfrage (die HTTP-Methode ist „Post“):

Geben Sie hier die Bildbeschreibung ein

Und der Inhalt der Antwort, die eine JSON-Datei ist:

Geben Sie hier die Bildbeschreibung ein

Was alle Informationen enthält, die ich suche.

Ab jetzt muss ich all dieses Wissen in Scrapy umsetzen. Lassen Sie uns die Spinne für diesen Zweck definieren:

class spider(BaseSpider):
    name="RubiGuesst"
    start_urls = ['http://www.rubin-kazan.ru/guestbook.html']

    def parse(self, response):
        url_list_gb_messages = re.search(r'url_list_gb_messages="(.*)"', response.body).group(1)
        yield FormRequest('http://www.rubin-kazan.ru' + url_list_gb_messages, callback=self.RubiGuessItem,
                          formdata={'page': str(page + 1), 'uid': ''})

    def RubiGuessItem(self, response):
        json_file = response.body

Im parse Funktion Ich habe die Antwort für die erste Anfrage. Im RubiGuessItem Ich habe die JSON-Datei mit allen Informationen.

  • Hallo. Könnten Sie bitte erklären, was „url_list_gb_messages“ ist? Ich kann es nicht verstehen. Danke.

    – polarisieren

    24. Januar 2015 um 20:42 Uhr

  • Dieser ist definitiv besser.

    – 1a1a11a

    8. Juni 2015 um 21:38 Uhr

  • @polarise Dieser Code verwendet die re Modul (reguläre Ausdrücke) sucht es nach der Zeichenfolge 'url_list_gb_messages="(.*)"' und isoliert den Inhalt von Klammern in der gleichnamigen Variablen. Dies ist eine schöne Einführung: guru99.com/python-regular-expressions-complete-tutorial.html

    – MG

    7. November 2017 um 14:05 Uhr

Kann scrapy verwendet werden um dynamische Inhalte von Websites zu
Ski

Webkit-basierte Browser (wie Google Chrome oder Safari) verfügen über integrierte Entwicklertools. In Chrome können Sie es öffnen Menu->Tools->Developer Tools. Die Network Registerkarte können Sie alle Informationen zu jeder Anfrage und Antwort sehen:

Geben Sie hier die Bildbeschreibung ein

Unten im Bild sehen Sie, dass ich die Anfrage nach unten gefiltert habe XHR – Dies sind Anfragen, die per Javascript-Code gestellt werden.

Tipp: Das Protokoll wird jedes Mal gelöscht, wenn Sie eine Seite laden. Am unteren Rand des Bildes speichert die Schaltfläche mit dem schwarzen Punkt das Protokoll.

Nach der Analyse von Anfragen und Antworten können Sie diese Anfragen von Ihrem Web-Crawler simulieren und wertvolle Daten extrahieren. In vielen Fällen ist es einfacher, Ihre Daten zu erhalten, als HTML zu parsen, da diese Daten keine Präsentationslogik enthalten und für den Zugriff durch Javascript-Code formatiert sind.

Firefox hat eine ähnliche Erweiterung, heißt es Feuerwanze. Einige werden argumentieren, dass Firebug noch mächtiger ist, aber ich mag die Einfachheit von Webkit.

  • Wie zum Teufel kann dies eine akzeptierte Antwort sein, wenn sie nicht einmal das Wort “kratzig” enthält?

    – Werkzeugkasten

    2. September 2016 um 16:47 Uhr

  • Es funktioniert und es ist einfach, mit dem Json-Modul in Python zu analysieren. Es ist eine Lösung! Versuchen Sie im Vergleich dazu, Selen oder andere Dinge zu verwenden, die die Leute vorschlagen, es verursacht mehr Kopfschmerzen. Wenn die alternative Methode viel komplizierter wäre, würde ich sie Ihnen geben, aber das ist hier nicht der Fall @Toolkit

    – Arion_Miles

    21. Oktober 2018 um 6:29 Uhr

  • Dies ist nicht wirklich relevant. Die Frage war, wie man scarpy benutzt, um dynamische Webseiten zu schaben.

    – E. Erfan

    22. November 2019 um 9:58 Uhr

Beim Crawlen stoßen wir oft auf Probleme, bei denen Inhalte, die auf der Seite gerendert werden, mit Javascript generiert werden und Scrapy daher nicht dafür crawlen kann (z. B. Ajax-Anforderungen, jQuery-Verrücktheit).

Wenn Sie jedoch Scrapy zusammen mit dem Webtest-Framework Selenium verwenden, können wir alles crawlen, was in einem normalen Webbrowser angezeigt wird.

Einige Dinge zu beachten:

  • Sie müssen die Python-Version von Selenium RC installiert haben, damit dies funktioniert, und Sie müssen Selenium ordnungsgemäß eingerichtet haben. Auch dies ist nur ein Template-Crawler. Man könnte mit den Dingen viel verrückter und fortgeschrittener werden, aber ich wollte nur die Grundidee zeigen. So wie der Code jetzt steht, werden Sie zwei Anfragen für eine beliebige URL ausführen. Eine Anfrage kommt von Scrapy und die andere von Selenium. Ich bin mir sicher, dass es Möglichkeiten gibt, dies zu umgehen, so dass Sie möglicherweise Selenium dazu bringen könnten, die eine und einzige Anfrage zu erledigen, aber ich habe mich nicht darum gekümmert, das zu implementieren, und wenn Sie zwei Anfragen ausführen, können Sie die Seite auch mit Scrapy crawlen.

  • Dies ist ziemlich leistungsfähig, da Sie jetzt das gesamte gerenderte DOM zum Crawlen zur Verfügung haben und weiterhin alle netten Crawling-Funktionen in Scrapy verwenden können. Dies führt natürlich zu einem langsameren Crawling, aber je nachdem, wie sehr Sie das gerenderte DOM benötigen, kann sich das Warten lohnen.

    from scrapy.contrib.spiders import CrawlSpider, Rule
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
    from scrapy.selector import HtmlXPathSelector
    from scrapy.http import Request
    
    from selenium import selenium
    
    class SeleniumSpider(CrawlSpider):
        name = "SeleniumSpider"
        start_urls = ["http://www.domain.com"]
    
        rules = (
            Rule(SgmlLinkExtractor(allow=('\.html', )), callback='parse_page',follow=True),
        )
    
        def __init__(self):
            CrawlSpider.__init__(self)
            self.verificationErrors = []
            self.selenium = selenium("localhost", 4444, "*chrome", "http://www.domain.com")
            self.selenium.start()
    
        def __del__(self):
            self.selenium.stop()
            print self.verificationErrors
            CrawlSpider.__del__(self)
    
        def parse_page(self, response):
            item = Item()
    
            hxs = HtmlXPathSelector(response)
            #Do some XPath selection with Scrapy
            hxs.select('//div').extract()
    
            sel = self.selenium
            sel.open(response.url)
    
            #Wait for javscript to load in Selenium
            time.sleep(2.5)
    
            #Do some crawling of javascript created content with Selenium
            sel.get_text("//div")
            yield item
    
    # Snippet imported from snippets.scrapy.org (which no longer works)
    # author: wynbennett
    # date  : Jun 21, 2011
    

Referenz: http://snipplr.com/view/66998/

  • Saubere Lösung! Haben Sie Tipps zum Verbinden dieses Skripts mit Firefox? (Betriebssystem ist Linux Mint). Ich erhalte “[Errno 111] Verbindung abgelehnt”.

    – Ajax

    31. Juli 2013 um 10:05 Uhr


  • Dieser Code funktioniert nicht mehr für selenium=3.3.1 und python=2.7.10Fehler beim Importieren von Selen aus Selen

    – Benjaminz

    19. März 2017 um 19:44 Uhr

  • In dieser Version von Selen wäre Ihre Import-Anweisung: from selenium import webdriver oder chromedriver oder was auch immer du gerade verwendest. Dokumente BEARBEITEN: Dokumentationsreferenz hinzufügen und meine schreckliche Grammatik ändern!

    – Nulltron

    1. April 2017 um 2:58 Uhr


  • Selenium Remote Control wurde laut Selenium WebDriver ersetzt ihre Webseite

    – Regenbogensorbet

    4. August 2017 um 20:04 Uhr

Kann scrapy verwendet werden um dynamische Inhalte von Websites zu
rocktheartsm4l

Eine andere Lösung wäre die Implementierung eines Download-Handlers oder einer Download-Handler-Middleware. (sehen kratzige Dokumente für weitere Informationen über Downloader-Middleware) Das Folgende ist eine Beispielklasse, die Selen mit dem Headless Phantomjs-Webtreiber verwendet:

1) Klasse innerhalb der definieren middlewares.py Skript.

from selenium import webdriver
from scrapy.http import HtmlResponse

class JsDownload(object):

    @check_spider_middleware
    def process_request(self, request, spider):
        driver = webdriver.PhantomJS(executable_path="D:\phantomjs.exe")
        driver.get(request.url)
        return HtmlResponse(request.url, encoding='utf-8', body=driver.page_source.encode('utf-8'))

2) Addieren JsDownload() Klasse zu Variable DOWNLOADER_MIDDLEWARE innerhalb settings.py:

DOWNLOADER_MIDDLEWARES = {'MyProj.middleware.MiddleWareModule.MiddleWareClass': 500}

3) Integrieren Sie die HTMLResponse innerhalb your_spider.py. Durch Decodieren des Antworttexts erhalten Sie die gewünschte Ausgabe.

class Spider(CrawlSpider):
    # define unique name of spider
    name = "spider"

    start_urls = ["https://www.url.de"] 

    def parse(self, response):
        # initialize items
        item = CrawlerItem()

        # store data as items
        item["js_enabled"] = response.body.decode("utf-8") 

Optionales Addon:

Ich wollte die Möglichkeit haben, verschiedenen Spidern mitzuteilen, welche Middleware sie verwenden sollen, also habe ich diesen Wrapper implementiert:

def check_spider_middleware(method):
@functools.wraps(method)
def wrapper(self, request, spider):
    msg = '%%s %s middleware step' % (self.__class__.__name__,)
    if self.__class__ in spider.middleware:
        spider.log(msg % 'executing', level=log.DEBUG)
        return method(self, request, spider)
    else:
        spider.log(msg % 'skipping', level=log.DEBUG)
        return None

return wrapper

Damit der Wrapper funktioniert, müssen alle Spinnen mindestens Folgendes haben:

middleware = set([])

um eine Middleware einzubinden:

middleware = set([MyProj.middleware.ModuleName.ClassName])

Vorteil:

Der Hauptvorteil bei der Implementierung auf diese Weise und nicht in der Spinne besteht darin, dass Sie am Ende nur eine Anfrage stellen. In der Lösung von AT zum Beispiel: Der Download-Handler verarbeitet die Anfrage und übergibt dann die Antwort an die Spinne. Die Spinne macht dann eine brandneue Anfrage in ihrer parse_page-Funktion – das sind zwei Anfragen für denselben Inhalt.

1646646332 922 Kann scrapy verwendet werden um dynamische Inhalte von Websites zu
Ivan Tscher

Ich habe eine benutzerdefinierte Downloader-Middleware verwendet, war aber nicht sehr zufrieden damit, da ich es nicht geschafft habe, den Cache damit zum Laufen zu bringen.

Ein besserer Ansatz bestand darin, einen benutzerdefinierten Download-Handler zu implementieren.

Es gibt ein funktionierendes Beispiel Hier. Es sieht aus wie das:

# encoding: utf-8
from __future__ import unicode_literals

from scrapy import signals
from scrapy.signalmanager import SignalManager
from scrapy.responsetypes import responsetypes
from scrapy.xlib.pydispatch import dispatcher
from selenium import webdriver
from six.moves import queue
from twisted.internet import defer, threads
from twisted.python.failure import Failure


class PhantomJSDownloadHandler(object):

    def __init__(self, settings):
        self.options = settings.get('PHANTOMJS_OPTIONS', {})

        max_run = settings.get('PHANTOMJS_MAXRUN', 10)
        self.sem = defer.DeferredSemaphore(max_run)
        self.queue = queue.LifoQueue(max_run)

        SignalManager(dispatcher.Any).connect(self._close, signal=signals.spider_closed)

    def download_request(self, request, spider):
        """use semaphore to guard a phantomjs pool"""
        return self.sem.run(self._wait_request, request, spider)

    def _wait_request(self, request, spider):
        try:
            driver = self.queue.get_nowait()
        except queue.Empty:
            driver = webdriver.PhantomJS(**self.options)

        driver.get(request.url)
        # ghostdriver won't response when switch window until page is loaded
        dfd = threads.deferToThread(lambda: driver.switch_to.window(driver.current_window_handle))
        dfd.addCallback(self._response, driver, spider)
        return dfd

    def _response(self, _, driver, spider):
        body = driver.execute_script("return document.documentElement.innerHTML")
        if body.startswith("<head></head>"):  # cannot access response header in Selenium
            body = driver.execute_script("return document.documentElement.textContent")
        url = driver.current_url
        respcls = responsetypes.from_args(url=url, body=body[:100].encode('utf8'))
        resp = respcls(url=url, body=body, encoding="utf-8")

        response_failed = getattr(spider, "response_failed", None)
        if response_failed and callable(response_failed) and response_failed(resp, driver):
            driver.close()
            return defer.fail(Failure())
        else:
            self.queue.put(driver)
            return defer.succeed(resp)

    def _close(self):
        while not self.queue.empty():
            driver = self.queue.get_nowait()
            driver.close()

Angenommen, Ihr Schaber heißt “Schaber”. Wenn Sie den erwähnten Code in eine Datei namens handlers.py im Stammverzeichnis des Ordners „scraper“ einfügen, können Sie Folgendes zu Ihrer settings.py hinzufügen:

DOWNLOAD_HANDLERS = {
    'http': 'scraper.handlers.PhantomJSDownloadHandler',
    'https': 'scraper.handlers.PhantomJSDownloadHandler',
}

Und voilà, das von JS geparste DOM, mit Scrapy-Cache, Wiederholungen usw.

  • Ich mag diese Lösung!

    – rocktheartsm4l

    26. Juli 2016 um 15:14 Uhr

  • Schöne Lösung. Ist der Selenium-Treiber immer noch die einzige Option?

    – Motheus

    10. August 2018 um 18:28 Uhr

  • Tolle Lösung. Vielen Dank.

    – CrazyGeek

    2. Juni 2019 um 6:32 Uhr

  • Hallo @ivan, ich habe genau wie Ihre Antwort. Die Antwort kommt jedoch nicht bei der parse(callback)-Methode der Spinne an. Wenn ich den Antworttext im Handler überprüfe, ist es wie erwartet. Wo kann das Problem sein? Kannst du helfen? Danke.

    – Vipool

    28. Februar 2021 um 13:14 Uhr

  • Hallo @Vipool, es ist eine Weile her, dass ich diesen Code nicht mehr ausführe … Ich verwende nodejs. sdk.apify.com/docs/examples/crawl-multiple-urls in letzter Zeit mit js-Parsing zu kriechen.

    – Ivan Chaer

    4. Juni 2021 um 7:49 Uhr


Wie kann scrapy verwendet werden, um diese dynamischen Daten zu scrapen, damit ich sie verwenden kann?

Ich frage mich, warum niemand die Lösung nur mit Scrapy gepostet hat.

Sehen Sie sich den Blogbeitrag des Scrapy-Teams an SCRAPING VON UNENDLICH SCROLLENDEN SEITEN
. Das Beispiel Fetzen http://spidyquotes.herokuapp.com/scroll Website, die unendliches Scrollen verwendet.

Die Idee ist Verwenden Sie die Entwicklertools Ihres Browsers und beachten Sie die AJAX-Anforderungen. Erstellen Sie dann auf der Grundlage dieser Informationen die Anforderungen für Scrapy.

import json
import scrapy


class SpidyQuotesSpider(scrapy.Spider):
    name="spidyquotes"
    quotes_base_url="http://spidyquotes.herokuapp.com/api/quotes?page=%s"
    start_urls = [quotes_base_url % 1]
    download_delay = 1.5

    def parse(self, response):
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            yield {
                'text': item.get('text'),
                'author': item.get('author', {}).get('name'),
                'tags': item.get('tags'),
            }
        if data['has_next']:
            next_page = data['page'] + 1
            yield scrapy.Request(self.quotes_base_url % next_page)

  • Ich mag diese Lösung!

    – rocktheartsm4l

    26. Juli 2016 um 15:14 Uhr

  • Schöne Lösung. Ist der Selenium-Treiber immer noch die einzige Option?

    – Motheus

    10. August 2018 um 18:28 Uhr

  • Tolle Lösung. Vielen Dank.

    – CrazyGeek

    2. Juni 2019 um 6:32 Uhr

  • Hallo @ivan, ich habe genau wie Ihre Antwort. Die Antwort kommt jedoch nicht bei der parse(callback)-Methode der Spinne an. Wenn ich den Antworttext im Handler überprüfe, ist es wie erwartet. Wo kann das Problem sein? Kannst du helfen? Danke.

    – Vipool

    28. Februar 2021 um 13:14 Uhr

  • Hallo @Vipool, es ist eine Weile her, dass ich diesen Code nicht mehr ausführe … Ich verwende nodejs. sdk.apify.com/docs/examples/crawl-multiple-urls in letzter Zeit mit js-Parsing zu kriechen.

    – Ivan Chaer

    4. Juni 2021 um 7:49 Uhr


1646646333 414 Kann scrapy verwendet werden um dynamische Inhalte von Websites zu
Enttäuscht von UnaccountableMod

Ja, Scrapy kann dynamische Websites scrapen, Websites, die über Javascript gerendert werden.

Es gibt zwei Ansätze, um diese Art von Websites zu kratzen.

Zuerst,

Sie können verwenden splash um Javascript-Code zu rendern und dann den gerenderten HTML-Code zu parsen. Sie finden das Dokument und das Projekt hier Kratziger Spritzer, Idiot

Sekunde,

Wie alle sagen, durch die Überwachung der network callsja, Sie können den API-Aufruf finden, der die Daten abruft, und sich darüber lustig machen, dass der Aufruf in Ihrer Scrapy-Spider Ihnen helfen könnte, die gewünschten Daten zu erhalten.

964900cookie-checkKann scrapy verwendet werden, um dynamische Inhalte von Websites zu schaben, die AJAX verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy