如何展开一个URL缩短链接?

21
我想能够输入一个缩短的或非缩短的URL,并返回其未缩短的形式。如何编写一个Python程序来实现这一点?
额外说明:
  • 情况1:缩短 --> 未缩短
  • 情况2:未缩短 --> 未缩短
例如,输入数组中的bit.ly/silly 应该在输出数组中为google.com
例如,输入数组中的google.com 应该在输出数组中为google.com

2
你是在谈论特定的URL缩短服务吗?这个服务是否有API可以检索信息? - JAL
如果你很着急,你也可以使用这个 API https://rapidapi.com/logicione/api/url-expander1 - Watt
10个回答

40

向URL发送HTTP HEAD请求并查看响应码。如果代码是30x,则查看Location头以获取未缩短的URL。否则,如果代码是20x,则该URL没有重定向;您可能还想以某种方式处理错误代码(4xx和5xx)。例如:

# This is for Py2k.  For Py3k, use http.client and urllib.parse instead, and
# use // instead of / for the division
import httplib
import urlparse

def unshorten_url(url):
    parsed = urlparse.urlparse(url)
    h = httplib.HTTPConnection(parsed.netloc)
    h.request('HEAD', parsed.path)
    response = h.getresponse()
    if response.status/100 == 3 and response.getheader('Location'):
        return response.getheader('Location')
    else:
        return url

忽略URL查询,更好的版本在这里: https://dev59.com/cmw05IYBdhLWcg3wmCwo#7153185 - DmitrySandalov
5
使用上述代码时请注意,如果您想获取实际的URL,则不会递归地还原缩短的URL。请尝试在http://t.co/hAplNMmSTg上测试。如需递归,请执行return unshorten_url(response.getheader('Location')) - Andrei-Niculae Petre
1
可能还要在一个集合中跟踪先前的URL,以防止循环递归。 - Herbert

34

使用 requests 库:

import requests

session = requests.Session()  # so connections are recycled
resp = session.head(url, allow_redirects=True)
print(resp.url)

4
我喜欢这个方案,它可以自动跟随多个重定向。 - neuhaus
我不得不设置 verify=False,因为Requests无法验证证书。 - Joe
有没有办法让请求显示每个重定向的URL? - newcool

5

Unshorten.me提供了一个API,可以发送JSON或XML请求,并返回完整的URL。


5
如果您使用的是Python 3.5+版本,则可以使用Unshortenit模块,这会使此过程变得非常简单。
from unshortenit import UnshortenIt
unshortener = UnshortenIt()
uri = unshortener.unshorten('https://href.li/?https://example.com')

4
为了还原链接,您可以使用requests。这是一个简单的解决方案,适用于我。
import requests
url = "http://foo.com"

site = requests.get(url)
print(site.url)

4

打开URL并查看其解析结果:

>>> import urllib2
>>> a = urllib2.urlopen('http://bit.ly/cXEInp')
>>> print a.url
http://www.flickr.com/photos/26432908@N00/346615997/sizes/l/
>>> a = urllib2.urlopen('http://google.com')
>>> print a.url
http://www.google.com/

3
这段话的意思是:这会获取整个页面。如果该页面不是重定向,并且恰好非常大,那么你将浪费大量带宽来确定它不是重定向。使用 HEAD 请求要好得多。 - Adam Rosenfield
1
@Adam Rosenfeld:对于刚开始学习Python的人来说,这可能是一个适当的副业项目的答案。但我不建议Google或Yahoo这样的蜘蛛程序通过这种方式来查找真实的URL。 - hughdbrown
这样做不是一个好主意。你会浪费很多带宽。正如@user387049所建议的那样,只需使用http://unshort.me api更好、更快。 - Cory

1

不幸的是,这只支持Python 2,并且为什么在2012年的Python代码中会写无括号的print语句呢 :( - Herbert

1
这里有一份源代码,它考虑了几乎所有有用的边角情况:
  • 设置自定义超时时间。
  • 设置自定义用户代理。
  • 检查是否需要使用 http 或 https 连接。
  • 递归解析输入的 URL,并防止陷入循环。

源代码在 Github 上:https://github.com/amirkrifa/UnShortenUrl

欢迎留下评论...

import logging
logging.basicConfig(level=logging.DEBUG)

TIMEOUT = 10
class UnShortenUrl:
    def process(self, url, previous_url=None):
        logging.info('Init url: %s'%url)
        import urlparse
        import httplib
        try:
            parsed = urlparse.urlparse(url)
            if parsed.scheme == 'https':
                h = httplib.HTTPSConnection(parsed.netloc, timeout=TIMEOUT)
            else:
                h = httplib.HTTPConnection(parsed.netloc, timeout=TIMEOUT)
            resource = parsed.path
            if parsed.query != "": 
                resource += "?" + parsed.query
            try:
                h.request('HEAD', 
                          resource, 
                          headers={'User-Agent': 'curl/7.38.0'}
                                   }
                          )
                response = h.getresponse()
            except:
                import traceback
                traceback.print_exec()
                return url

            logging.info('Response status: %d'%response.status)
            if response.status/100 == 3 and response.getheader('Location'):
                red_url = response.getheader('Location')
                logging.info('Red, previous: %s, %s'%(red_url, previous_url))
                if red_url == previous_url:
                    return red_url
                return self.process(red_url, previous_url=url) 
            else:
                return url 
        except:
            import traceback
            traceback.print_exc()
            return None

如果我正确理解了您的流程,您可能希望对您能够容忍的重定向数量进行限制。 - Foon
在某些情况下,重定向指向相同的先前URL,因此为了防止无限循环的陷阱,我在递归调用中传播先前的URL,如果最终得到red_url == previous_url,则停止并返回该URL。否则,在正常情况下,在某个迭代中,响应状态将不再等于重定向状态,因此我们返回检索到的URL。 - Amir Krifa
@AmirKrifa 能够处理指向 link.bar 的 link.foo,而 link.bar 又指回 link.foo 吗?(我不知道 httplib 是否有跟随重定向的选项,在这种情况下,这种链接在你调用递归调用之前会抛出异常) - Foon

1
你可以使用 geturl()
from urllib.request import urlopen
url = "bit.ly/silly"
unshortened_url = urlopen(url).geturl()
print(unshortened_url)
# google.com

0

这是非常简单的任务,你只需要添加4行代码就可以了 :)

import requests
url = input('Enter url : ')
site = requests.get(url)
print(site.url)

只需运行此代码,您就可以成功还原URL。


这与此答案相同:https://dev59.com/9W855IYBdhLWcg3wrGdY#43712633 - Gino Mempin

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