【问题标题】:Parsing the urls in sitemap with different url format using sitemap spider in scrapy, python使用scrapy,python中的站点地图蜘蛛解析具有不同url格式的站点地图中的url
【发布时间】:2015-02-01 21:55:36
【问题描述】:

我在scrapy,python中使用站点地图蜘蛛。 站点地图似乎有不寻常的格式,网址前有“//”:

<url>
    <loc>//www.example.com/10/20-baby-names</loc>
</url>
<url>
    <loc>//www.example.com/elizabeth/christmas</loc>
 </url>

myspider.py

from scrapy.contrib.spiders import SitemapSpider
from myspider.items import *

class MySpider(SitemapSpider):
    name = "myspider"
    sitemap_urls = ["http://www.example.com/robots.txt"]

    def parse(self, response):
        item = PostItem()           
        item['url'] = response.url
        item['title'] = response.xpath('//title/text()').extract()

        return item

我收到此错误:

raise ValueError('Missing scheme in request url: %s' % self._url)
    exceptions.ValueError: Missing scheme in request url: //www.example.com/10/20-baby-names

如何使用站点地图蜘蛛手动解析网址?

【问题讨论】:

    标签: python web-scraping scrapy sitemap scrapy-spider


    【解决方案1】:

    如果我没看错,您可以(为了快速解决)覆盖SitemapSpider_parse_sitemap 的默认实现。这不好,因为您将不得不复制大量代码,但应该可以。 您必须添加一个方法来生成带有方案的 URL。

    """if the URL starts with // take the current website scheme and make an absolute
    URL with the same scheme"""
    def _fix_url_bug(url, current_url):
        if url.startswith('//'):
               ':'.join((urlparse.urlsplit(current_url).scheme, url))
           else:
               yield url
    
    def _parse_sitemap(self, response):
        if response.url.endswith('/robots.txt'):
            for url in sitemap_urls_from_robots(response.body)
                yield Request(url, callback=self._parse_sitemap)
        else:
            body = self._get_sitemap_body(response)
            if body is None:
                log.msg(format="Ignoring invalid sitemap: %(response)s",
                        level=log.WARNING, spider=self, response=response)
                return
    
            s = Sitemap(body)
            if s.type == 'sitemapindex':
                for loc in iterloc(s):
                    # added it before follow-test, to allow test to return true
                    # if it includes the scheme (yet do not know if this is the better solution)
                    loc = _fix_url_bug(loc, response.url)
                    if any(x.search(loc) for x in self._follow):
                        yield Request(loc, callback=self._parse_sitemap)
            elif s.type == 'urlset':
                for loc in iterloc(s):
                    loc = _fix_url_bug(loc, response.url) # same here
                    for r, c in self._cbs:
                        if r.search(loc):
                            yield Request(loc, callback=c)
                            break
    

    这只是一个普遍的想法,未经测试。所以它要么完全不起作用,要么可能存在语法错误。请通过 cmets 回复,以便我改进答案。

    您尝试解析的站点地图似乎是错误的。来自 RFC 缺少方案 is perfectly fine,但站点地图 require URLs to begin with a scheme

    【讨论】:

    • 不行,iterlocSitemapSpider 是同一个模块,像这样重新定义它不会改变任何东西。我正在研究这个 - mock 可能会有所帮助。
    • 你能详细说明一下吗?如果他有一个实例MySpider在运行,不应该调用MySpideriterloc方法吗?我感觉你是指我的想法中的多态性问题?
    • 问题是iterloc 不是SitemapSpider 中的方法——它是类外的单独函数,但在同一个模块中。
    • 顺便说一句,可能会覆盖_parse_sitemap() 内部方法并在发送请求之前修复 url 是一个更好的选择..检查一下。谢谢。
    • @alecxe 你能举一些例子来覆盖 _parse_sitemap() 在发送请求之前修复 url 吗?
    【解决方案2】:

    我使用@alecxe 的技巧来解析蜘蛛中的网址。我让它工作,但不确定这是否是最好的方法。

    from urlparse import urlparse
    import re 
    from scrapy.spider import BaseSpider
    from scrapy.http import Request
    from scrapy.utils.response import body_or_str
    from example.items import *
    
    class ExampleSpider(BaseSpider):
        name = "example"
        start_urls = ["http://www.example.com/sitemap.xml"]
    
        def parse(self,response):
            nodename = 'loc'
            text = body_or_str(response)
            r = re.compile(r"(<%s[\s>])(.*?)(</%s>)" % (nodename, nodename), re.DOTALL)
            for match in r.finditer(text):
                url = match.group(2)
                if url.startswith('//'):
                    url = 'http:'+url
                    yield Request(url, callback=self.parse_page)
    
        def parse_page(self, response):
            # print response.url
            item = PostItem()   
    
            item['url'] = response.url
            item['title'] = response.xpath('//title/text()').extract()
            return item
    

    【讨论】:

      【解决方案3】:

      我认为最好和最干净的解决方案是添加一个下载器中间件,它会在蜘蛛没有注意到的情况下更改恶意 URL。

      import re
      import urlparse
      from scrapy.http import XmlResponse
      from scrapy.utils.gz import gunzip, is_gzipped
      from scrapy.contrib.spiders import SitemapSpider
      
      # downloader middleware
      class SitemapWithoutSchemeMiddleware(object):
          def process_response(self, request, response, spider):
              if isinstance(spider, SitemapSpider):
                  body = self._get_sitemap_body(response)
      
                  if body:
                      scheme = urlparse.urlsplit(response.url).scheme
                      body = re.sub(r'<loc>\/\/(.+)<\/loc>', r'<loc>%s://\1</loc>' % scheme, body)    
                      return response.replace(body=body)
      
              return response
      
          # this is from scrapy's Sitemap class, but sitemap is
          # only for internal use and it's api can change without
          # notice
          def _get_sitemap_body(self, response):
              """Return the sitemap body contained in the given response, or None if the
              response is not a sitemap.
              """
              if isinstance(response, XmlResponse):
                  return response.body
              elif is_gzipped(response):
                  return gunzip(response.body)
              elif response.url.endswith('.xml'):
                  return response.body
              elif response.url.endswith('.xml.gz'):
                  return gunzip(response.body)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-18
        • 1970-01-01
        相关资源
        最近更新 更多