【问题标题】:Changing hostname in a url更改 url 中的主机名
【发布时间】:2014-03-04 22:19:13
【问题描述】:

我正在尝试使用 python 更改 url 中的主机名,并且一直在使用 urlparse 模块一段时间,但没有找到令人满意的解决方案。例如,考虑 url:

https://www.google.dk:80/barbaz

我想将“www.google.dk”替换为例如“www.foo.dk”,所以我得到以下网址:

https://www.foo.dk:80/barbaz

所以我要替换的部分是 urlparse.urlsplit 所指的主机名。我曾希望 urlsplit 的结果能让我做出改变,但结果类型 ParseResult 不允许我这样做。如果没有别的,我当然可以通过将所有部分与 + 一起附加来重建新的 url,但这会给我留下一些非常丑陋的代码,其中包含很多条件来在正确的地方获得“://”和“:” .

【问题讨论】:

  • 我试图避免使用任何 if 语句,因为基本 url 是否具有端口号可能会有所不同。不过,根据您的回答,我似乎无法避免它:-)。感谢您的帮助。

标签: python url


【解决方案1】:

您可以使用urllib.parse.urlparse 函数和ParseResult._replace 方法(Python 3):

>>> import urllib.parse
>>> parsed = urllib.parse.urlparse("https://www.google.dk:80/barbaz")
>>> replaced = parsed._replace(netloc="www.foo.dk:80")
>>> print(replaced)
ParseResult(scheme='https', netloc='www.foo.dk:80', path='/barbaz', params='', query='', fragment='')

如果您使用的是 Python 2,请将 urllib.parse 替换为 urlparse

ParseResultnamedtuple 的子类,_replacenamedtuple 方法:

返回命名元组的新实例替换指定字段 新价值观

更新

正如@2rs2ts 在评论中所说的netloc 属性包括一个端口号。

好消息:ParseResult 具有 hostnameport 属性。 坏消息:hostnameport 不是namedtuple 的成员,它们是动态属性,你不能这样做parsed._replace(hostname="www.foo.dk")。它会抛出异常。

如果您不想在: 上拆分,并且您的网址始终有一个端口号并且没有usernamepassword(即“https://username:password@www.google.dk:80/barbaz”之类的网址),您可以这样做:

parsed._replace(netloc="{}:{}".format(parsed.hostname, parsed.port))

【讨论】:

  • 请注意,主机名称为netloc,它包括任何端口号。这个答案表明了这一点,但没有明确说明。
  • 使用私有方法_replace 感觉不对。
  • _replacenamedtuple 公共 API 的一部分。它只是以下划线开头,以避免与字段名称冲突。
  • 提醒 - netloc 还包括用户名和密码。如果您解析'https://user:hunter2@example.com:444/path' 之类的内容,您的netloc 将是'user:hunter2@example.com:444'
  • urlparse 不是 pip 中的可导入库,因此,这不起作用,因为“import urlparse”不起作用。
【解决方案2】:

你总是可以做到这一点:

>>> p = parse.urlparse("https://stackoverflow.com/questions/21628852/changing-hostname-in-a-url")
>>> parse.ParseResult(**dict(p._asdict(), netloc='perrito.com.ar')).geturl()
'https://perrito.com.ar/questions/21628852/changing-hostname-in-a-url'

【讨论】:

    【解决方案3】:

    我也建议像@linkyndy 的回答一样使用urlspliturlunsplit,但是对于Python3,它会是:

    >>> from urllib.parse import urlsplit, urlunsplit
    >>> url = list(urlsplit('https://www.google.dk:80/barbaz'))
    >>> url
    ['https', 'www.google.dk:80', '/barbaz', '', '']
    >>> url[1] = 'www.foo.dk:80'
    >>> new_url = urlunsplit(url)
    >>> new_url
    'https://www.foo.dk:80/barbaz'
    

    【讨论】:

      【解决方案4】:

      netloc 中主机的简单字符串替换也适用于大多数情况:

      >>> p = urlparse.urlparse('https://www.google.dk:80/barbaz')
      >>> p._replace(netloc=p.netloc.replace(p.hostname, 'www.foo.dk')).geturl()
      'https://www.foo.dk:80/barbaz'
      

      如果用户名或密码与主机名匹配,这将不起作用。您不能限制 str.replace 仅替换最后一次出现,因此我们可以使用 split 和 join:

      >>> p = urlparse.urlparse('https://www.google.dk:www.google.dk@www.google.dk:80/barbaz')
      >>> new_netloc = 'www.foo.dk'.join(p.netloc.rsplit(p.hostname, 1))
      >>> p._replace(netloc=new_netloc).geturl()
      'https://www.google.dk:www.google.dk@www.foo.dk:80/barbaz'
      

      【讨论】:

      • _replace 是私有的,不应被客户端代码使用。
      • 比接受的答案更好,尤其是第二个选项。
      • @gb:_replace 在 NamedTuple 中不是私有的。它是 API 的一部分:docs.python.org/2/library/…
      • 是的,_replace 不是私人的。引用v3 doc为了防止与字段名称发生冲突,方法和属性名称以下划线开头。比在其他答案中使用列表索引更好的选择。
      • 虽然,_replace 只是故事的一部分,因为它返回一个新元组而不是改变旧元组。所以newurl = urlunsplit(urlsplit(url)._replace(netloc="<new netloc>"))p 上的_replace 无效
      【解决方案5】:

      使用urlparse模块的urlparseurlunparse方法:

      import urlparse
      
      old_url = 'https://www.google.dk:80/barbaz'
      url_lst = list(urlparse.urlparse(old_url))
      # Now url_lst is ['https', 'www.google.dk:80', '/barbaz', '', '', '']
      url_lst[1] = 'www.foo.dk:80'
      # Now url_lst is ['https', 'www.foo.dk:80', '/barbaz', '', '', '']
      new_url = urlparse.urlunparse(url_lst)
      
      print(old_url)
      print(new_url)
      

      输出:

      https://www.google.dk:80/barbaz
      https://www.foo.dk:80/barbaz
      

      【讨论】:

        【解决方案6】:

        要替换主机而不接触正在使用的端口(如果有),请使用:

        import re, urlparse
        
        p = list(urlparse.urlsplit('https://www.google.dk:80/barbaz'))
        p[1] = re.sub('^[^:]*', 'www.foo.dk', p[1])
        print urlparse.urlunsplit(p)
        

        打印

        https://www.foo.dk:80/barbaz
        

        如果您没有提供任何端口,这也可以正常工作。

        如果您更喜欢 Nigel 指出的 _replace 方式,您可以改用它:

        p = urlparse.urlsplit('https://www.google.dk:80/barbaz')
        p = p._replace(netloc=re.sub('^[^:]*', 'www.foo.dk', p.netloc))
        print urlparse.urlunsplit(p)
        

        【讨论】:

        • @Downvoter:想提一下你不喜欢什么?没有理由(不明显)的反对票根本没有帮助。如果可能的话,我想改进我的答案。
        【解决方案7】:

        您可以利用 Python 的 urlparse 中的 urlspliturlunsplit

        >>> from urlparse import urlsplit, urlunsplit
        >>> url = list(urlsplit('https://www.google.dk:80/barbaz'))
        >>> url
        ['https', 'www.google.dk:80', '/barbaz', '', '']
        >>> url[1] = 'www.foo.dk:80'
        >>> new_url = urlunsplit(url)
        >>> new_url
        'https://www.foo.dk:80/barbaz'
        

        正如文档所述,传递给urlunsplit() 的参数“可以是任何五项可迭代”,因此上述代码按预期工作。

        【讨论】:

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