Получение контента, динамически загружаемого по щелчку мыши с помощью Scrapy Splash и Lua

У меня есть скребок Scrapy Splash со скриптом Lua. В настоящее время сценарий Lua только инициирует прокрутку страницы для загрузки дополнительных результатов на страницу поиска. Со страницы поиска я перехожу к страницам с подробностями, которые я очищаю. Однако на странице сведений карусель фотографий еще не присутствует в модели DOM, она загружается динамически, когда пользователь щелкает элемент #showphotos.

После щелчка по этому элементу загружается следующий HTML-код фотокарусели:

<div id="slider">
    <div class="slider-inner">
        <div class="item active">
            <img src="https://www.example.com/images/1.jpg">
        </div>
        <div class="item">
            <img src="https://www.example.com/images/2.jpg">
        </div>
    </div>
</div>

Я уже проверял здесь и здесь.

Итак, я попытался написать сценарий:

click_script = """
        function main(splash, args)

            btn = splash:select_all('#showphotos')[0]
            btn:mouse_click()
            assert(splash:wait(0.5))
              return {
                num = #splash:select_all('#slider div.slider-inner'),
                html = splash:html()
              }
        end
        """

Поскольку я новичок в Splash и Lua, я не знаю, куда добавить этот код или откуда его вызвать.

Я создал страницу с подробными сведениями о тесте, здесь.

Мой текущий код:

myscraper.py

import json
import re

import scrapy
import time
from scrapy_splash import SplashRequest
from scrapy.selector import Selector
from scrapy.http import HtmlResponse
from myresults.items import MyResultItem


class Spider(scrapy.Spider):
    name = 'myscraper'
    allowed_domains = ['example.com']
    start_urls = ['https://www.example.com/results']

    def start_requests(self):
        # lua script for scroll to bottom while all objects appeared
        lua_script = """
        function main(splash, args)
          local object_count = 0
          local url = splash.args.url
          splash:go(url)
          splash:wait(0.5)
          local get_object_count = splash:jsfunc([[
            function (){
              var objects = document.getElementsByClassName("object-adres");
              return objects.length;
            }
            ]])
          temp_object_count = get_object_count()
          local retry = 3
          while object_count ~= temp_object_count do
            splash:evaljs('window.scrollTo(0, document.body.scrollHeight);')
            splash:wait(0.5)
            object_count = temp_object_count
            temp_object_count = get_object_count()
            
          end
          return splash:html()
        end
        """

        # yield first splash request with lua script and parse it from parse def
        yield SplashRequest(
            self.start_urls[0], self.parse,
            endpoint='execute',
            args={'lua_source': lua_script},
        )

    def parse(self, response):
        # get all properties from first page which was generated with lua script
        # get all adreslink from a tag
        object_links = response.css('a.adreslink::attr(href)').getall()
        for link in object_links:
            # send request with each link and parse it from parse_object def
            yield scrapy.Request(link, self.parse_object)

    def parse_object(self, response):
        # create new MyResultItem which will saved to json file
        item = MyResultItem()

        item['url'] = response.url # get url        
        


        yield item

items.py

import scrapy

class RentalItem(scrapy.Item):
    id = scrapy.Field()
    photos = scrapy.Field()
    url = scrapy.Field()

    pass

person Flo    schedule 20.12.2020    source источник


Ответы (1)


Скрипт Lua работает как скрипт Python. В Spider -> start_requests -> lua_script у вас уже есть сценарий Lua. Вы хотите выбрать первый #showphotos элемент и щелкнуть по нему; Кроме того, вы хотите добавить к результатам больше данных.

Таким образом, после выполнения уже существующего кода Lua мы хотим указать Splash выбрать первый элемент #showphotos:

btn = splash:select_all('#showphotos')[1]

Обратите внимание на индекс 1, а не на 0, потому что массив splash:select_all начинается с 1.

После этого щелкните по нему:

btn:mouse_click()

И в конце добавляем к результатам больше данных:

return {
    num = splash:select_all('#slider div.slider-inner')[1].node.outerHTML,
    html = splash:html()
}

Опять же, обратите внимание на индекс 1, а не на 0, потому что массив splash:select_all начинается с 1. Кроме того, я добавил .node.outerHTML, потому что splash:select_all() возвращает объект Lua, и нет способа сериализации по умолчанию это в JSON (ref)

В итоге у вас должно получиться что-то вроде этого:

function main(splash, args)
  local object_count = 0
  local url = splash.args.url
  splash:go(url)
  splash:wait(0.5)

  local get_object_count = splash:jsfunc([[
    function (){
      var objects = document.getElementsByClassName("object-adres");
      return objects.length;
    }
  ]])
  temp_object_count = get_object_count()
  local retry = 3
  while object_count ~= temp_object_count do
    splash:evaljs('window.scrollTo(0, document.body.scrollHeight);')
    splash:wait(0.5)
    object_count = temp_object_count
    temp_object_count = get_object_count()
  end

  btn = splash:select_all('#showphotos')[1]
  btn:mouse_click()
  assert(splash:wait(0.5))
  
  return {
    num = splash:select_all('#slider div.slider-inner')[1].node.outerHTML,
    html = splash:html()
  }
end
person SonnyStar    schedule 20.12.2020
comment
Отлично, спасибо! Я попробую это. Что я еще не понимаю в вашем решении, так это то, что первая часть сценария Lua актуальна для страницы обзора поиска, например. /allcompanies, чтобы прокрутить вниз, чтобы отобразить все результаты. Вторая часть сценария Lua с предложенным вами кодом для щелчка мышью по элементу #showphotos актуальна только для страниц с подробными сведениями, на которых есть карусель фотографий, например: _3 _, _ 4 _, _ 5_. Если я заключу все в одну функцию function main(splash, args), как это предлагается сейчас, я буду выполнять сценарий без надобности много раз? - person Flo; 20.12.2020
comment
пс. Я также обновил страницу с тестовым примером, чтобы показать, что я имею в виду. - person Flo; 20.12.2020