如何在必要时给URL添加'http://'协议?

23
我需要解析一个URL。目前我正在使用urlparse.urlparse()和urlparse.urlsplit()。
问题是当没有方案(scheme)时,我无法从URL中获取“netloc”(主机)。 我的意思是,如果我有以下URL:
www.amazon.com/Programming-Python-Mark-Lutz/dp/0596158106/ref=sr_1_1?ie=UTF8&qid=1308060974&sr=8-1
我就无法获取netloc:www.amazon.com
根据Python文档的说明:
按照RFC 1808中的语法规范, urlparse只识别以“//”正确引入的netloc。 否则,输入被假定为相对URL, 因此以路径组件开头。
所以这是故意这样设计的。但我仍然不知道如何从该URL获取netloc。
我想我可以检查方案是否存在,如果不存在,则添加它,然后解析它。但这个解决方案似乎不是很好。
你有更好的建议吗?
编辑: 感谢所有答案。但是,我不能做Corey和其他人提出的“startswith”事情。因为,如果我得到其他协议/方案的URL,我会搞砸它。看:
如果我得到这个URL:
ftp://something.com

使用提出的代码,我会在开头添加"http://"并且会搞乱它。

我找到的解决方案

if not urlparse.urlparse(url).scheme:
   url = "http://"+url
return urlparse.urlparse(url)

需要注意的是:

我首先执行一些验证,如果没有提供方案(scheme),则将其视为 http://


这是因为URL的协议部分 - http:// - 缺失了吗? - ewall
是的,那就是原因。但是,如果方案仍然缺失,我该如何获得它呢? - santiagobasulto
在您的解决方案中,我仍然会检查前导的 //(可能只有 /),因为一个正确的 URL 会有这个(即使缺少方案)。 - SingleNegationElimination
@TokenMacGuy 我做了。它在“验证”部分。值得一提。请查看Steve的答案。 - santiagobasulto
现在,如果您提供了一个包含解决方案的自我回答,您也可能会因此获得一些赞同。 (或者您想让其他人发布您的答案,还是完全不同的事情?) - serv-inc
6个回答

13

看起来你需要指定协议才能获取netloc。

如果不存在,可以像这样添加:

import urlparse

url = 'www.amazon.com/Programming-Python-Mark-Lutz'
if '//' not in url:
    url = '%s%s' % ('http://', url)
p = urlparse.urlparse(url)
print p.netloc

更多有关该问题的信息:https://bugs.python.org/issue754016


6
文档中已经有了这个确切的例子,就在你粘贴的文本下面。如果没有“//”,则添加“//”将得到您想要的结果。如果您不知道它是否会有协议和“//”,您可以使用正则表达式(甚至只需查看它是否已包含“//”)来确定是否需要添加它。

您的另一个选择是使用split('/')并获取返回的列表的第一个元素,这仅适用于URL没有协议或'//'的情况。

编辑(添加给未来的读者):用于检测协议的正则表达式可能类似于re.match('(?:http|ftp|https)://', url)


我仍然存在不同的协议问题(请参见Bryan答案上的评论)。谢谢。 - santiagobasulto
然后你可以使用正则表达式 - 检查(?:http|ftp|etc):// - 或者只需检查字符串中是否存在 '://'。这取决于您希望它有多健壮; 完整的URL解析是复杂的。 - SteveMc
1
+1 你说得对,SteveMc。哪种方法更快?使用你发布的协议列表进行解析还是使用我提出的urlparse? - santiagobasulto
urlparse 可能(虽然我没有查看)使用正则表达式来进行解析(因为如我所说,这很复杂),但你的方法似乎非常合理,所以我建议保留你已经完成的工作。如果你好奇的话,可以对其进行性能分析。 - SteveMc
谢谢你的回答,Steve。我做了类似的事情。评论中的正则表达式非常好。你应该将它添加到答案中以供未来读者参考。 - santiagobasulto

6

如果协议始终为http,您只需使用一行代码:

return "http://" + url.split("://")[-1]

更好的选择是如果协议被传递,则使用该协议:
return url if "://" in url else "http://" + url

你的意思是 如果 url 中包含 "://",则返回 url,否则返回 "http://" + url 吗? - Robert Dodd
1
感谢Robert Dodd的错误报告。 - Ignacio Pérez

5

从文档中得知:

遵循RFC 1808中的语法规范,urlparse仅在以“//”正确引入netloc时才识别它。否则,输入被认为是相对URL,因此以路径组件开头。

所以你可以这样做:

In [1]: from urlparse import urlparse

In [2]: def get_netloc(u):
   ...:     if not u.startswith('http'):
   ...:         u = '//' + u
   ...:     return urlparse(u).netloc
   ...: 

In [3]: get_netloc('www.amazon.com/Programming-Python-Mark-Lutz/dp/0596158106/ref=sr_1_1?ie=UTF8&qid=1308060974&sr=8-1')
Out[3]: 'www.amazon.com'

In [4]: get_netloc('http://www.amazon.com/Programming-Python-Mark-Lutz/dp/0596158106/ref=sr_1_1?ie=UTF8&qid=1308060974&sr=8-1')
Out[4]: 'www.amazon.com'

In [5]: get_netloc('https://www.amazon.com/Programming-Python-Mark-Lutz/dp/0596158106/ref=sr_1_1?ie=UTF8&qid=1308060974&sr=8-1')
Out[5]: 'www.amazon.com'

2
你考虑过仅检查URL开头是否存在“http://”,如果不存在,则添加它吗?另一种解决方案是,假设第一部分确实是netloc而不是相对URL的一部分,只需获取第一个“/”之前的所有内容并将其用作netloc。

是的,这就是我现在正在做的。但并不是很喜欢。如果没有更好的选择,我会继续使用它。谢谢! - santiagobasulto
我还有一个问题。如果使用其他协议/方案怎么办?如果我在这个URL中检查http://,比如"ftp:// my.home.com",那么我会认为它不存在。如果我添加它,我会搞砸它。 - santiagobasulto

0
这个一行代码就可以实现。
netloc = urlparse('//' + ''.join(urlparse(url)[1:])).netloc

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