selenio con scrapy para página dinámica

85

Estoy intentando extraer información de un producto de una página web mediante scrapy. Mi página web que se va a eliminar se ve así:

  • comienza con una página de lista de productos con 10 productos
  • un clic en el botón "siguiente" carga los siguientes 10 productos (la URL no cambia entre las dos páginas)
  • uso LinkExtractor para seguir el enlace de cada producto en la página del producto y obtener toda la información que necesito

Traté de replicar el siguiente botón de llamada ajax pero no puedo trabajar, así que estoy probando el selenio. Puedo ejecutar el controlador web de selenium en un script separado, pero no sé cómo integrarlo con scrapy. ¿Dónde debo poner la parte de selenio en mi araña?

Mi araña es bastante estándar, como la siguiente:

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

Se agradece cualquier idea. ¡Gracias!

Z. Lin
fuente

Respuestas:

123

Realmente depende de cómo necesita raspar el sitio y cómo y qué datos desea obtener.

Aquí hay un ejemplo de cómo puede seguir la paginación en eBay usando 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()

Estos son algunos ejemplos de "arañas de selenio":


También hay una alternativa a tener que utilizar Seleniumcon Scrapy. En algunos casos, el uso de ScrapyJSmiddleware es suficiente para manejar las partes dinámicas de una página. Ejemplo de uso en el mundo real:

Alecxe
fuente
gracias por tu ayuda. En realidad, mi mayor problema está en la parte posterior a next.click (). Cada vez que obtengo una página nueva, pero ¿puedo usar un LinkExtractor para extraer todas las URL del producto y luego usar una devolución de llamada para analizarlas?
Z. Lin
2
¿Hay alguna forma de reutilizar la respuesta que ya ha sido capturada por scrapy en lugar de usarla self.driver.get(response.url)?
Ethereal
2
@HalcyonAbrahamRamirez esto es solo un ejemplo con la parte de selenio en la araña chatarra. Una vez hecho el selenio, normalmente self.driver.page_sourcese pasa a una instancia de Selector para que Scrapy analice el HTML, forme las instancias de elementos, las pase a las tuberías, etc. O bien, las cookies de selenio se pueden analizar y pasar a Scrapy para realizar solicitudes adicionales. Pero, si no necesita el poder de la arquitectura de marco fragmentada, entonces, claro, puede usar solo selenio, ya que es bastante poderoso para localizar los elementos.
alecxe
4
@alecxe sí mientras entiendo el concepto. Todavía estoy confundido en la parte donde extrae la fuente de la página usando selenio y pasa los elementos que desea que se raspen a scrapy. por ejemplo. Hay un botón de carga más al hacer clic en él se mostrarán más elementos, pero extrae el xpath para esos elementos. ahora, ¿cómo se pasan esos xpaths a scrapy? porque solo los elementos que se muestran cuando solicitó la página por primera vez serán analizados por scrappy y no los que se encuentran después de hacer clic en el botón cargar más con selenio
Halcyon Abraham Ramirez
2
@HalcyonAbrahamRamirez lo tengo, cargaría más elementos hasta que no haya más para agregar. Luego, tomar la driver.page_sourcey pasarlo a la Selector()..
alecxe
2

Si (la URL no cambia entre las dos páginas), entonces debe agregar dont_filter = True con su scrapy.Request () o scrapy encontrará esta URL como un duplicado después de procesar la primera página.

Si necesita renderizar páginas con javascript, debe usar scrapy-splash , también puede verificar este middleware scrapy que puede manejar páginas de javascript usando selenium o puede hacerlo iniciando cualquier navegador sin cabeza

Pero una solución más eficaz y rápida es inspeccionar su navegador y ver qué solicitudes se realizan durante el envío de un formulario o la activación de un evento determinado. Intente simular las mismas solicitudes que envía su navegador. Si puede replicar las solicitudes correctamente, obtendrá los datos que necesita.

Aquí hay un ejemplo :

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

Cuando la URL de paginación es la misma para todas las páginas y usa la solicitud POST, entonces puede usar scrapy.FormRequest () en lugar de scrapy.Request () , ambos son iguales pero FormRequest agrega un nuevo argumento ( formdata = ) al constructor.

Aquí hay otro ejemplo de araña de este publicación :

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
Destruido en el amor
fuente