selen z scrapy dla dynamicznej strony

85

Próbuję wydobyć informacje o produkcie ze strony internetowej za pomocą scrapy. Moja strona internetowa do pobrania wygląda następująco:

  • zaczyna się od strony product_list z 10 produktami
  • kliknięcie przycisku „dalej” ładuje następnych 10 produktów (adres URL nie zmienia się między dwiema stronami)
  • Używam LinkExtractor, aby przejść do każdego linku do strony produktu i uzyskać wszystkie potrzebne informacje

Próbowałem zreplikować wywołanie next-button-ajax-call, ale nie mogę zacząć działać, więc próbuję selenu. Mogę uruchomić webdriver selenu w osobnym skrypcie, ale nie wiem jak zintegrować się z scrapy. Gdzie mam umieścić część selenu w moim pająku drapiącym?

Mój pająk jest dość standardowy, jak poniżej:

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

Każdy pomysł jest mile widziany. Dziękuję Ci!

Z. Lin
źródło

Odpowiedzi:

123

To naprawdę zależy od tego, jak chcesz zeskrobać witrynę oraz jak i jakie dane chcesz uzyskać.

Oto przykład, jak możesz śledzić paginację w serwisie eBay za pomocą Scrapy+ 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()

Oto kilka przykładów „pająków selenu”:


Istnieje również alternatywa dla korzystania Seleniumz Scrapy. W niektórych przypadkach do obsługi dynamicznych części strony wystarczy użycie ScrapyJSoprogramowania pośredniego . Przykładowe użycie w świecie rzeczywistym:

alecxe
źródło
dzięki za pomoc. Właściwie mój największy problem tkwi w części po next.click (). Za każdym razem, gdy otrzymuję nową stronę, ale czy nadal mogę użyć LinkExtractor do wyodrębnienia wszystkich adresów URL produktów, a następnie użyć wywołania zwrotnego, aby je przeanalizować?
Z. Lin
2
Czy istnieje sposób na ponowne wykorzystanie odpowiedzi, która została już przechwycona przez scrapy zamiast używania self.driver.get(response.url)?
Ethereal,
2
@HalcyonAbrahamRamirez to tylko przykład z częścią selenu w pająku drapieżnym. Po zakończeniu selenu, zazwyczaj self.driver.page_sourcejest on przekazywany do instancji selektora, aby Scrapy przeanalizował kod HTML, uformował instancje pozycji, przekazał je do potoków itp. Lub też pliki cookie selenu mogą zostać przeanalizowane i przesłane do Scrapy w celu wykonania dodatkowych żądań. Ale jeśli nie potrzebujesz mocy architektury szkieletowej scrapy, to oczywiście możesz użyć tylko selenu - on sam jest dość potężny w lokalizowaniu elementów.
alecxe
4
@alecxe yeah, podczas gdy rozumiem koncepcję. Nadal jestem zdezorientowany częścią, w której wyodrębniasz źródło strony za pomocą selenu i przekazujesz elementy, które chcesz zeskrobać do złomu. na przykład. Po kliknięciu przycisku załaduj więcej zostanie wyświetlonych więcej elementów, ale wyodrębnisz ścieżkę xpath dla tych elementów. teraz jak przejdziesz te ścieżki do złomowania? ponieważ tylko elementy pokazane, gdy po raz pierwszy zażądałeś strony, zostaną przeanalizowane przez scrappy, a nie te po kliknięciu przycisku załaduj więcej z selenem
Halcyon Abraham Ramirez
2
@HalcyonAbrahamRamirez to rozumiem, ładowałbym więcej elementów, aż nie było już nic do dodania. Następnie weź driver.page_sourcei przekaż go do Selector()..
alecxe
2

Jeśli (adres URL nie zmienia się między dwiema stronami), powinieneś dodać dont_filter = True do twojego scrapy.Request () lub scrapy znajdzie ten adres jako duplikat po przetworzeniu pierwszej strony.

Jeśli chcesz renderować strony za pomocą javascript, powinieneś użyć scrapy-splash , możesz również sprawdzić to scrapy middleware, które obsługuje strony javascript przy użyciu selenu lub możesz to zrobić uruchamiając dowolną przeglądarkę bezgłową

Ale skuteczniejszym i szybszym rozwiązaniem jest zbadanie przeglądarki i sprawdzenie, jakie żądania są wysyłane podczas przesyłania formularza lub wywołania określonego zdarzenia. Spróbuj zasymulować te same żądania, które wysyła Twoja przeglądarka. Jeśli uda Ci się poprawnie powtórzyć żądanie (-a), otrzymasz potrzebne dane.

Oto przykład :

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))

Kiedy adres URL paginacji jest taki sam dla każdej strony i używa żądania POST, możesz użyć scrapy.FormRequest () zamiast scrapy.Request () , oba są takie same, ale FormRequest dodaje nowy argument ( formdata = ) do konstruktora.

Oto kolejny przykład pająka z tego postu :

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
Zniszczony w miłości
źródło