Selen mit Scrapy für dynamische Seite

84

Ich versuche, Produktinformationen mithilfe von Scrapy von einer Webseite zu entfernen. Meine zu kratzende Webseite sieht folgendermaßen aus:

  • Beginnt mit einer product_list-Seite mit 10 Produkten
  • Ein Klick auf "Weiter" lädt die nächsten 10 Produkte (URL ändert sich nicht zwischen den beiden Seiten)
  • Ich benutze LinkExtractor, um jedem Produktlink auf der Produktseite zu folgen und alle Informationen zu erhalten, die ich brauche

Ich habe versucht, den Next-Button-Ajax-Aufruf zu replizieren, kann aber nicht funktionieren, also versuche ich es mit Selen. Ich kann den Web-Treiber von Selen in einem separaten Skript ausführen, weiß aber nicht, wie ich ihn in Scrapy integrieren soll. Wo soll ich den Selenteil in meine kratzende Spinne stecken?

Meine Spinne ist ziemlich normal, wie die folgende:

class ProductSpider(CrawlSpider):
    name = "product_spider"
    allowed_domains = ['example.com']
    start_urls = ['http://example.com/shanghai']
    rules = [
        Rule(SgmlLinkExtractor(restrict_xpaths='//div[@id="productList"]//dl[@class="t2"]//dt'), callback='parse_product'),
        ]

    def parse_product(self, response):
        self.log("parsing product %s" %response.url, level=INFO)
        hxs = HtmlXPathSelector(response)
        # actual data follows

Jede Idee wird geschätzt. Vielen Dank!

Z. Lin
quelle

Antworten:

122

Es hängt wirklich davon ab, wie Sie die Site kratzen müssen und wie und welche Daten Sie erhalten möchten.

Hier ist ein Beispiel, wie Sie die Paginierung bei ebay mit Scrapy+ verfolgen können Selenium:

import scrapy
from selenium import webdriver

class ProductSpider(scrapy.Spider):
    name = "product_spider"
    allowed_domains = ['ebay.com']
    start_urls = ['http://www.ebay.com/sch/i.html?_odkw=books&_osacat=0&_trksid=p2045573.m570.l1313.TR0.TRC0.Xpython&_nkw=python&_sacat=0&_from=R40']

    def __init__(self):
        self.driver = webdriver.Firefox()

    def parse(self, response):
        self.driver.get(response.url)

        while True:
            next = self.driver.find_element_by_xpath('//td[@class="pagn-next"]/a')

            try:
                next.click()

                # get the data and write it to scrapy items
            except:
                break

        self.driver.close()

Hier einige Beispiele für "Selenspinnen":


Es gibt auch eine Alternative zur Verwendung Seleniummit Scrapy. In einigen Fällen reicht die Verwendung von ScrapyJSMiddleware aus, um die dynamischen Teile einer Seite zu verarbeiten. Beispiel für eine reale Verwendung:

Alecxe
quelle
danke für Ihre Hilfe. Eigentlich ist mein größtes Problem in dem Teil nach next.click (). Jedes Mal, wenn ich eine neue Seite erhalte, kann ich trotzdem einen LinkExtractor verwenden, um alle Produkt-URLs zu extrahieren, und sie dann mithilfe eines Rückrufs analysieren?
Z. Lin
2
Gibt es eine Möglichkeit, die Antwort, die bereits von Scrapy erfasst wurde, wiederzuverwenden, anstatt sie zu verwenden self.driver.get(response.url)?
Ätherische
2
@HalcyonAbrahamRamirez Dies ist nur ein Beispiel für den Selenanteil in der kratzigen Spinne. Nachdem Selen fertig ist, wird das normalerweise self.driver.page_sourcean eine Selector-Instanz übergeben, damit Scrapy den HTML-Code analysiert, die Elementinstanzen bildet, sie an Pipelines weitergibt usw. Oder Selen-Cookies können analysiert und an Scrapy übergeben werden, um zusätzliche Anforderungen zu stellen. Wenn Sie jedoch nicht die Leistung der Scrapy-Framework-Architektur benötigen, können Sie natürlich nur Selen verwenden - es ist selbst sehr leistungsfähig, um die Elemente zu lokalisieren.
Alecxe
4
@alecxe ja, während ich das Konzept bekomme. Ich bin immer noch verwirrt über den Teil, in dem Sie die Seitenquelle mit Selen extrahieren und die Elemente, die Sie kratzen möchten, an Scrapy übergeben. zum Beispiel. Wenn Sie auf die Schaltfläche "Mehr laden" klicken, werden mehr Elemente angezeigt, und Sie extrahieren den x-Pfad für diese Elemente. Wie übergibt man diese X-Pfade an Scrapy? weil nur die Elemente, die angezeigt wurden, als Sie die Seite zum ersten Mal angefordert haben, von Scrappy analysiert werden und nicht diejenigen, die auf die Schaltfläche "Mehr laden" mit Selen geklickt haben
Halcyon Abraham Ramirez
2
@HalcyonAbrahamRamirez hat es verstanden, ich würde mehr Artikel laden, bis es keine weiteren mehr gibt. Dann nehmen Sie die driver.page_sourceund geben Sie es an die Selector()..
Alecxe
2

Wenn (URL ändert sich nicht zwischen den beiden Seiten), sollten Sie dont_filter = True mit Ihrem Scrapy hinzufügen. Request () oder Scrapy finden diese URL nach der Verarbeitung der ersten Seite als Duplikat.

Wenn Sie Seiten mit Javascript rendern müssen, sollten Sie Scrapy-Splash verwenden . Sie können auch diese Scrapy-Middleware überprüfen, die Javascript-Seiten mit Selen verarbeiten kann, oder Sie können dies tun, indem Sie einen beliebigen Headless-Browser starten

Eine effektivere und schnellere Lösung besteht jedoch darin, Ihren Browser zu überprüfen und festzustellen, welche Anforderungen beim Senden eines Formulars oder beim Auslösen eines bestimmten Ereignisses gestellt werden. Versuchen Sie, dieselben Anforderungen zu simulieren, die Ihr Browser sendet. Wenn Sie die Anforderung (en) korrekt replizieren können, erhalten Sie die benötigten Daten.

Hier ist ein Beispiel :

class ScrollScraper(Spider):
    name = "scrollingscraper"

    quote_url = "http://quotes.toscrape.com/api/quotes?page="
    start_urls = [quote_url + "1"]

    def parse(self, response):
        quote_item = QuoteItem()
        print response.body
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            quote_item["author"] = item.get('author', {}).get('name')
            quote_item['quote'] = item.get('text')
            quote_item['tags'] = item.get('tags')
            yield quote_item

        if data['has_next']:
            next_page = data['page'] + 1
            yield Request(self.quote_url + str(next_page))

Wenn die Paginierungs-URL für alle Seiten gleich ist und die POST-Anforderung verwendet, können Sie Scrapy.FormRequest () anstelle von Scrapy.Request () verwenden . Beide sind identisch, aber FormRequest fügt dem Konstruktor ein neues Argument ( formdata = ) hinzu.

Hier ist ein weiteres Spinnenbeispiel aus diesem Beitrag :

class SpiderClass(scrapy.Spider):
    # spider name and all
    name = 'ajax'
    page_incr = 1
    start_urls = ['http://www.pcguia.pt/category/reviews/#paginated=1']
    pagination_url = 'http://www.pcguia.pt/wp-content/themes/flavor/functions/ajax.php'

    def parse(self, response):

        sel = Selector(response)

        if self.page_incr > 1:
            json_data = json.loads(response.body)
            sel = Selector(text=json_data.get('content', ''))

        # your code here

        # pagination code starts here
        if sel.xpath('//div[@class="panel-wrapper"]'):
            self.page_incr += 1
            formdata = {
                'sorter': 'recent',
                'location': 'main loop',
                'loop': 'main loop',
                'action': 'sort',
                'view': 'grid',
                'columns': '3',
                'paginated': str(self.page_incr),
                'currentquery[category_name]': 'reviews'
            }
            yield FormRequest(url=self.pagination_url, formdata=formdata, callback=self.parse)
        else:
            return
In Liebe zerstört
quelle