【问题标题】:Using Scrapy and Splash to Follow javascript pagination使用 Scrapy 和 Splash 跟踪 javascript 分页
【发布时间】:2019-08-24 21:40:58
【问题描述】:

我正在使用 Scrapy 和 splash 来提取数据。我正在寻找一种方法来跟踪由 javascript 提供支持的分页。 URL 不会改变,无论你在哪个页面,它总是一样的。

<li class="btn-next"><a href="javascript:ctrl.set_pageReload(2)">Next</a></li>

我尝试过使用 lua 脚本和 splash 来点击元素,但这不起作用:

    """function main(splash)
local url = splash.args.url
assert(splash:go(url))
assert(splash:wait(1))
assert(splash:runjs('document.getElementsByClassName("btn-next")[0].click()'))
assert(splash:wait(0.75))
-- return result as a JSON object
return {html = splash:html()}                
end """



def parse(self, response):
    section = response.css('li.li-result')
    for item in section:
        yield{
            'manufacturer' :  item.css('span.brand::text').extract_first(),
            'model' : item.css('span.sub-title::text').extract_first(),
            'engine_size' :  item.css('span.nowrap::text').extract_first(),
            'model_type' : item.css('span span.nowrap::text').extract_first(),
            'old_price' : item.css('li.li-result p.old-prix span::text').extract_first(),    
            'price' : item.css('li.li-result p.prix::text').extract_first(),
            'consumption' : item.css('li.li-result div.desc::text').extract_first(),
            'date' : item.css('p.btn-publication::text').extract_first(),
            'fuel_type' : item.css('div.bc-info div.upper::text').extract_first(),
            'mileage' : item.css('li.li-result div.bc-info ul div::text')[1].extract(),
            'year' : item.css('li.li-result div.bc-info ul div::text')[2].extract(),
            'transmission_type' : item.css('li.li-result div.bc-info ul div::text')[3].extract(),
            'add_number' : item.css('li.li-result div.bc-info ul div::text')[4].extract(),

        }

    next_page = response.css('li.btn-next').extract_first() #pagination    
    if next_page != 0:
        print(response)
        yield(SplashRequest(next_page, self.parse,
            endpoint='execute',
            cache_args=['lua_source'],
            args={'lua_source': script},  

        ))

甚至有可能以这种方式做到这一点吗?感谢帮助。

【问题讨论】:

  • 你能告诉我们是什么网站吗?你可能不需要渲染 JS。
  • @ThePyGuy 原来是我向here提出问题的同一个网站

标签: python scrapy scrapy-splash


【解决方案1】:

首先,Lua 脚本的两个问题:

  1. .btn-next 是一个非交互式 li 元素,因此单击它什么也不做。
    参考:https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
  2. 分页很慢,所以等待 0.75 秒太短了。

解决这个问题:

  1. 单击其a 子元素(参见下面的替代选项)。
  2. 等待 1.5 秒或更长时间。
function main(splash)
  local url = splash.args.url
  assert(splash:go(url))
  assert(splash:wait(1))
  -- assert(splash:runjs('document.getElementsByClassName("btn-next")[0].click()'))          -- Change this
  assert(splash:runjs('document.getElementsByClassName("btn-next")[0].children[0].click()')) -- to this
  -- assert(splash:wait(0.75)) -- Change this
  assert(splash:wait(1.5))     -- to this
  -- return result as a JSON object
  return {html = splash:html()}                
end

(上面显示 JavaScript 导航到第 2 页在 Splash 中工作,但我们需要更多工作来使用 Scrapy-Splash 抓取后续页面。)

接下来,parse 方法的两个问题:

  1. next_pageli 元素的 HTML 字符串,因此它不能作为 url 参数传递给 SplashRequest
  2. next_page 可能是 None,但绝不可能是 0

要解决这个问题,请参阅解决方案。

解决方案

  1. 传递response.urlresponse.text,然后在下一个请求中调用splash:set_content()恢复状态。
    • 传递 dontfilter=True 以跳过对 url 的重复检查。
    • 等待 2 秒,但有时仍然太短(请参阅下面的替代方法)。
  2. 检查next_page 不是None
script = """function main(splash)
  local url = splash.args.url
  local content = splash.args.content
  assert(splash:set_content(content, "text/html; charset=utf-8", url))
  assert(splash:runjs('document.getElementsByClassName("btn-next")[0].children[0].click()'))
  assert(splash:wait(2))
  return {html = splash:html()}
end"""
def parse(self, response, **kwargs):
    section = response.css('li.li-result')
    for item in section:
        yield {
            'manufacturer': item.css('span.brand::text').extract_first(),
            'model': item.css('span.sub-title::text').extract_first(),
            'engine_size': item.css('span.nowrap::text').extract_first(),
            'model_type': item.css('span span.nowrap::text').extract_first(),
            'old_price': item.css('li.li-result p.old-prix span::text').extract_first(),
            'price': item.css('li.li-result p.prix::text').extract_first(),
            'consumption': item.css('li.li-result div.desc::text').extract_first(),
            'date': item.css('p.btn-publication::text').extract_first(),
            'fuel_type': item.css('div.bc-info div.upper::text').extract_first(),
            'mileage': item.css('li.li-result div.bc-info ul div::text')[1].extract(),
            'year': item.css('li.li-result div.bc-info ul div::text')[2].extract(),
            'transmission_type': item.css('li.li-result div.bc-info ul div::text')[3].extract(),
            'add_number': item.css('li.li-result div.bc-info ul div::text')[4].extract(),
        }

    next_page = response.css('li.btn-next').extract_first()
    # print(next_page)
    if next_page is not None:
        yield SplashRequest(
            response.url,
            self.parse,
            endpoint='execute',
            args={
                'lua_source': script,
                'content': response.text,
            },
            cache_args=['lua_source'],
            dont_filter=True,
        )

(注释掉for item in section:块并取消注释print(next_page)以轻松验证解决方案。)


一些替代品

点击按钮的替代方法是直接调用函数:

-- assert(splash:runjs('document.getElementsByClassName("btn-next")[0].children[0].click()'))
assert(splash:runjs('ctrl.set_pageReload(ctrl.context.cur_page + 1)'))

硬编码可能不足的等待时间的另一种方法是定期检查设置的变量,然后等待 jQuery 就绪回调(可选地硬编码初始等待时间):

assert(splash:runjs('window.notReloaded = 1'))
-- assert(splash:wait(2)) -- Optional initial wait time
local exit = false
while (exit == false)
do
  result, error = splash:wait_for_resume([[
    function main(splash) {
      window.notReloaded ? splash.error() : splash.resume();
    }
  ]])
  if result then
    exit = true
  else
    splash:wait(0.2) -- Adjust resolution as desired
  end
end
assert(splash:wait_for_resume([[
  function main(splash) {
    $(() => splash.resume());
  }
]]))

【讨论】:

  • 解决方法如下:github.com/acjh/Scrapy-Splash_javascript-integration/commit/…。主要是,您没有将项目设置(最重要的是DOWNLOADER_MIDDLEWARES 中的SplashMiddleware)传递给CrawlerProcess,因此Lua 脚本甚至没有运行。现在您可以将splash:wait(10) 减少到 2 并看到它仍然有效。
猜你喜欢
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多