Q如何在python中跟踪元刷新

python的urllib2遵循3xx重定向以获取最终内容。是否有办法使urllib2(或其他库,如httplib2)也遵循元刷新(meta refreshes)?还是需要手动解析html以获取刷新元标记?

2010-02-23 13:31:41  hoju

A回答

  • 1

    下面是一个使用beautifulsoup和httplib2(以及基于证书的身份验证)的解决方案:

    import BeautifulSoup
    import httplib2
    
    def meta_redirect(content):
        soup  = BeautifulSoup.BeautifulSoup(content)
    
        result=soup.find("meta",attrs={"http-equiv":"Refresh"})
        if result:
            wait,text=result["content"].split(";")
            if text.strip().lower().startswith("url="):
                url=text[4:]
                return url
        return None
    
    def get_content(url, key, cert):
    
        h=httplib2.Http(".cache")
        h.add_certificate(key,cert,"")
    
        resp, content = h.request(url,"GET")
    
        # follow the chain of redirects
        while meta_redirect(content):
            resp, content = h.request(meta_redirect(content),"GET") 
    
        return content  
    
    2016-06-30 15:53:16  Caumons asmaier
  • 2

    使用请求和lxml库的类似解决方案。还要做一个简单的检查,确认被测试的东西实际上是html(在我的实现中是一个要求)。还可以通过使用请求库的会话捕获和使用cookies(如果重定向+cookies用作防刮机制,则有时是必需的)。

    import magic
    import mimetypes
    import requests
    from lxml import html 
    from urlparse import urljoin
    
    def test_for_meta_redirections(r):
        mime = magic.from_buffer(r.content, mime=True)
        extension = mimetypes.guess_extension(mime)
        if extension == '.html':
            html_tree = html.fromstring(r.text)
            attr = html_tree.xpath("//meta[translate(@http-equiv, 'REFSH', 'refsh') = 'refresh']/@content")[0]
            wait, text = attr.split(";")
            if text.lower().startswith("url="):
                url = text[4:]
                if not url.startswith('http'):
                    # Relative URL, adapt
                    url = urljoin(r.url, url)
                return True, url
        return False, None
    
    
    def follow_redirections(r, s):
        """
        Recursive function that follows meta refresh redirections if they exist.
        """
        redirected, url = test_for_meta_redirections(r)
        if redirected:
            r = follow_redirections(s.get(url), s)
        return r
    

    用法:

    s = requests.session()
    r = s.get(url)
    # test for and follow meta redirects
    r = follow_redirections(r, s)
    
    2016-06-01 20:47:59  mlissner
    • 有时元刷新重定向指向相对URL。例如,facebook做<noscript><meta http-equiv="refresh" content="0; URL=/?_fb_noscript=1" /></noscript>。最好是检测相关的URL并预先设置方案和主机。 – Joe Mornin Jul 18 '13 at 5:31
    • @约瑟夫莫宁:改编。我意识到它仍然不支持循环重定向,尽管…总是有些东西。 – mlissner Jul 18 '13 at 16:53
  • 3

    好吧,似乎没有库支持它,所以我一直在使用此代码:

    import urllib2
    import urlparse
    import re
    
    def get_hops(url):
        redirect_re = re.compile('<meta[^>]*?url=(.*?)["\']', re.IGNORECASE)
        hops = []
        while url:
            if url in hops:
                url = None
            else:
                hops.insert(0, url)
                response = urllib2.urlopen(url)
                if response.geturl() != url:
                    hops.insert(0, response.geturl())
                # check for redirect meta tag
                match = redirect_re.search(response.read())
                if match:
                    url = urlparse.urljoin(url, match.groups()[0].strip())
                else:
                    url = None
        return hops
    
    2012-09-28 13:24:07  Onilton Maciel hoju
    • 1
      这本很好用,谢谢你贴出来。 – Yehonatan Mar 16 '16 at 11:19
    • 另请参见:stackoverflow.com/a/1732454/2468469“>stackoverflow.com/a/1732454/2468469 – Adrian17 Oct 31 '18 at 14:19
  • 4

    如果您不想使用bs4,可以使用如下lxml:

    from lxml.html import soupparser
    
    def meta_redirect(content):
        root = soupparser.fromstring(content)
        result_url = root.xpath('//meta[@http-equiv="refresh"]/@content')
        if result_url:
            result_url = str(result_url[0])
            urls = result_url.split('URL=') if len(result_url.split('url=')) < 2    else result_url.split('url=')
            url = urls[1] if len(urls) >= 2 else None
        else:
            return None
        return url
    
    2017-02-13 07:42:59  chroming
  • 5

    使用beautifulsoup或lxml解析html。

    2010-02-23 13:39:04  Ignacio Vazquez-Abrams
    • 使用html解析器来提取meta-refresh标记太过分了,至少在我看来是这样。希望有一个python http库可以自动执行此操作。 – hoju Feb 24 '10 at 1:05
    • 好吧meta它是一个html标记,所以您不太可能在http库中找到这个功能。 – Otto Allmendinger Feb 24 '10 at 12:14