【问题标题】:Scrapy CrawlSpider + Splash: how to follow links through linkextractor?Scrapy CrawlSpider + Splash:如何通过链接提取器跟踪链接?
【发布时间】:2019-11-20 16:30:12
【问题描述】:

我有以下部分工作的代码,

class ThreadSpider(CrawlSpider):
    name = 'thread'
    allowed_domains = ['bbs.example.com']
    start_urls = ['http://bbs.example.com/diy']

    rules = (
        Rule(LinkExtractor(
            allow=(),
            restrict_xpaths=("//a[contains(text(), 'Next Page')]")
        ),
            callback='parse_item',
            process_request='start_requests',
            follow=True),
    )

def start_requests(self):
    for url in self.start_urls:
        yield SplashRequest(url, self.parse_item, args={'wait': 0.5})

def parse_item(self, response):
    # item parser

代码将只为start_urls 运行,但不会遵循restricted_xpaths 中指定的链接,如果我注释掉start_requests() 方法和规则中的process_request='start_requests', 行,它将运行并遵循预期的链接,当然不用js渲染。

我已经阅读了两个相关的问题,CrawlSpider with Splash getting stuck after first URLCrawlSpider with Splash,并在start_requests() 方法中专门将scrapy.Request() 更改为SplashRequest(),但这似乎不起作用。我的代码有什么问题? 谢谢,

【问题讨论】:

    标签: python scrapy web-crawler scrapy-splash splash-js-render


    【解决方案1】:

    我遇到了一个类似的问题,似乎特定于将 Splash 与 Scrapy CrawlSpider 集成。它只会访问开始 url,然后关闭。我设法让它工作的唯一方法是不使用scrapy-splash插件,而是使用'process_links'方法将Splash http api url添加到所有scrapy收集的链接。然后我做了其他调整,以弥补这种方法产生的新问题。这是我所做的:

    如果您打算将其存储在某个地方,您需要这两个工具来组合初始 URL,然后将其拆开。

    from urllib.parse import urlencode, parse_qs
    

    在每个链接前面都添加了启动 URL,scrapy 会将它们全部过滤为“场外域请求”,因此我们将“localhost”设为允许的域。

    allowed_domains = ['localhost']
    start_urls = ['https://www.example.com/']
    

    但是,这会带来一个问题,因为当我们只想抓取一个网站时,我们最终可能会无休止地抓取网络。让我们用 LinkExtractor 规则来解决这个问题。通过只从我们想要的域中抓取链接,我们解决了异地请求问题。

    LinkExtractor(allow=r'(http(s)?://)?(.*\.)?{}.*'.format(r'example.com')),
    process_links='process_links',
    

    这里是 process_links 方法。 urlencode 方法中的字典是您放置所有启动参数的地方。

    def process_links(self, links):
        for link in links:
            if "http://localhost:8050/render.html?&" not in link.url:
                link.url = "http://localhost:8050/render.html?&" + urlencode({'url':link.url,
                                                                              'wait':2.0})
        return links
    

    最后,要将 url 从启动 url 中取出,请使用 parse_qs 方法。

    parse_qs(response.url)['url'][0] 
    

    关于这种方法的最后一点说明。您会注意到,我在开头的初始 URL 中有一个“&”。 (...render.html?&)。这使得无论您在使用 urlencode 方法时参数的顺序如何,解析启动 url 以取出实际 url 都是一致的。

    【讨论】:

      【解决方案2】:

      好像和https://github.com/scrapy-plugins/scrapy-splash/issues/92有关

      我个人使用 dont_process_response=True 所以响应是 HtmlResponse(这是 _request_to_follows 中的代码所要求的)。

      而且我还在我的 spyder 中重新定义了 _build_request 方法,如下所示:

      def _build_request(self, rule, link):
          r = SplashRequest(url=link.url, callback=self._response_downloaded, args={'wait': 0.5}, dont_process_response=True)
          r.meta.update(rule=rule, link_text=link.text)
          return r 
      

      在 github 问题中,一些用户只是在他们的类中重新定义了 _request_to_follow 方法。

      【讨论】:

        【解决方案3】:

        使用下面的代码 - 只需复制和粘贴

        restrict_xpaths=('//a[contains(text(), "Next Page")]')
        

        而不是

        restrict_xpaths=("//a[contains(text(), 'Next Page')]")
        

        【讨论】:

        • 这似乎没有帮助。如果我注释掉start_requests(),请记住restrict_xpaths=("//a[contains(text(), 'Next Page')]") 行工作得很好。我意识到这是一个未解决的问题,正如许多用户在这里报告的那样:github.com/scrapy-plugins/scrapy-splash/issues/92
        猜你喜欢
        • 2012-09-21
        • 1970-01-01
        • 1970-01-01
        • 2020-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多