使用Scrapy和Splash进行JavaScript递归爬取同一页面

3
我正在爬取一个网站,这个网站使用JavaScript进行下一页的跳转。我使用Splash在第一页上执行我的JavaScript代码,我能够跳转到第二页,但是我无法跳转到第三页、第四页、第五页等等。只有一页后,爬取就停止了。
我正在爬取的链接为:http://59.180.234.21:8788/user/viewallrecord.aspx 我的代码如下:
import scrapy
from scrapy_splash import SplashRequest
from time import sleep


class MSEDCLSpider(scrapy.Spider):
    name = "msedcl_spider"
    scope_path = 'body > table:nth-child(11) tr > td.content_area > table:nth-child(4) tr:not(:first-child)'
    ref_no_path = "td:nth-child(1) ::text"
    title_path = "td:nth-child(2) ::text"
    end_date_path = "td:nth-child(5) ::text"
    fee_path = "td:nth-child(6) ::text"
    start_urls = ["http://59.180.234.21:8788/user/viewallrecord.aspx"]

    lua_src = """function main(splash)
        local url = splash.args.url
        splash:go(url)
        splash:wait(2.0)
        splash:runjs("document.querySelectorAll('#lnkNext')[0].click()")
        splash:wait(4.0)
        return {
            splash:html(),
        }
        end
        """

    def start_requests(self):
        for url in self.start_urls:
            yield SplashRequest(
                url,
                self.parse,
                endpoint='execute',
                method='POST',
                dont_filter=True,
                args={
                    'wait': 1.0,
                    'lua_source': self.lua_src,
                },
            )


    def parse(self, response):
        print response.status
        scopes = response.css('#page-info').extract()[0]
        print(response.url)
        print(scopes)

我对scrapy和splash都是新手,请温柔一点。谢谢。


主要代码没有缩进问题。但是当我粘贴代码时,它会被改变。 - REDDY PRASAD
我认为你在混合使用空格和制表符(至少在粘贴的代码中是这样)。尝试使用所有空格(每个制表符4个空格)来格式化你问题中的代码。 - paul trmbrth
问题不在于缩进。无论如何,我已经编辑了帖子并进行了修改。 - REDDY PRASAD
1个回答

4
我能看到两个问题:
  1. 您没有发出这些请求。在start_requests中只有一个请求,响应在self.parse方法中解析,但是对于第三方和其他页面的请求从未发送。为此,您需要从.parse方法中发送一些请求。

  2. 如果您解决了(1),那么您可能会遇到下一个问题:Splash在请求之间不保留页面状态。将每个请求视为打开一个新的无痕模式浏览器窗口并执行某些操作;这是按设计来的。但是这个网站的问题在于URL在页面之间不会改变,因此您不能仅从第三页开始并单击“下一页”。

但我认为有方法可以解决(2)。也许您可以在单击后获取页面HTML,然后使用splash:set_content将其加载到浏览器中;您还可以保留Cookie——在scrapy-splash README中有一个示例;尽管似乎这个网站不依赖于Cookie进行分页。

另一种方法是编写一个脚本,加载所有页面,而不仅仅是下一页,然后将所有页面的内容返回给客户端。类似这样(未经测试):

function main(splash) 
    splash:go(splash.args.url)
    local pages = {splash:html()}
    for i = 2,100 do             
        splash:runjs("document.querySelectorAll('#lnkNext')[0].click()")            
        splash:wait(4)
        pages[i] = splash:html()
    end
    return pages
end

为了让这个工作正常运行,您需要设置更长的超时时间;您可能还需要使用更大的--max-timeout选项启动Splash。

谢谢你的回答。第二种方法有没有什么缺点?比如性能、内存使用等方面,因为有一些其他网站需要爬取超过200页。 - REDDY PRASAD
@REDDYPRASAD 第二种方法更难监控和调试,如果出现错误,你无法获得部分结果并继续(除非你已经以一种处理该问题的方式编写了你的脚本)。 - Mikhail Korobov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接