97 votes

selenium avec scrapy pour les pages dynamiques

J'essaie de récupérer des informations sur un produit à partir d'une page Web, en utilisant scrapy. La page Web que je veux extraire ressemble à ceci :

  • commence par une page product_list avec 10 produits
  • un clic sur le bouton "suivant" charge les 10 produits suivants (l'url ne change pas entre les deux pages)
  • J'utilise LinkExtractor pour suivre le lien de chaque produit jusqu'à la page du produit, et obtenir toutes les informations dont j'ai besoin.

J'ai essayé de reproduire l'appel next-button-ajax mais je n'ai pas réussi à le faire fonctionner, alors j'ai essayé le selenium. Je peux exécuter le webdriver de selenium dans un script séparé, mais je ne sais pas comment l'intégrer à scrapy. Où dois-je mettre la partie selenium dans mon spider scrapy ?

Mon araignée est assez standard, comme la suivante :

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

Toute idée est appréciée. Merci.

135voto

alecxe Points 50783

Cela dépend vraiment de la manière dont vous avez besoin de gratter le site et de la manière dont vous voulez obtenir les données.

Voici un exemple de la façon dont vous pouvez suivre la pagination sur ebay en utilisant 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()

Voici quelques exemples d'"araignées à sélénium" :


Il existe également une alternative à l'utilisation de Selenium con Scrapy . Dans certains cas, l'utilisation de ScrapyJS intergiciel est suffisant pour gérer les parties dynamiques d'une page. Exemple d'utilisation dans le monde réel :

4voto

Expired Brain Points 1368

Si (l'url ne change pas entre les deux pages) alors vous devez ajouter dont_filter=True avec votre scrapy.Request() ou scrapy trouvera cette url comme un duplicata après le traitement de la première page.

Si vous avez besoin de rendre des pages avec du javascript, vous devez utiliser scrapy-splash vous pouvez également consulter cette page intergiciel scrapy qui peut gérer les pages javascript en utilisant Selenium ou vous pouvez le faire en lançant n'importe quel navigateur sans tête.

Mais une solution plus efficace et plus rapide consiste à inspecter votre navigateur et à voir quelles requêtes sont faites lors de la soumission d'un formulaire ou du déclenchement d'un certain événement. Essayez de simuler les mêmes requêtes que celles envoyées par votre navigateur. Si vous parvenez à reproduire correctement la ou les requêtes, vous obtiendrez les données dont vous avez besoin.

Voici un exemple :

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

Lorsque l'url de pagination est la même pour toutes les pages et utilise une requête POST, vous pouvez utiliser scrapy.FormRequest() au lieu de scrapy.Request() les deux sont identiques mais FormRequest ajoute un nouvel argument ( formdata= ) au constructeur.

Voici un autre exemple d'araignée poste :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X