如何在Python中跟踪meta刷新

15
Python的urllib2会跟随3xx重定向以获取最终内容。是否有办法使urllib2(或其他库,如httplib2)也能跟随meta refreshes?还是我需要手动解析HTML以获取刷新元标记?
5个回答

12

这里是使用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.strip()[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  

'url=text[4:]' 应该改为 'url=text.strip()[4:]' 以去除前导空格。另外,有时我会看到 REFRESH 而不是 Refresh。 - Marc Van Daele
1
我同意。我按照你的建议修复了代码。 - asmaier

5
使用requests和lxml库的类似解决方案。还对被测试的内容进行了简单检查,确保其实际上是HTML(这是我实现中的要求)。此外,通过使用请求库的会话来捕获和使用cookie(如果重定向+ cookie被用作反爬机制,则有时是必要的)。
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)

有时元刷新重定向指向相对URL。例如,Facebook会使用<noscript><meta http-equiv="refresh" content="0; URL=/?_fb_noscript=1" /></noscript>。检测到相对URL并在前面加上方案和主机会很有用。 - Joe Mornin
@JosephMornin:已经改编了。但我意识到它仍然不支持循环重定向...总是有些问题。 - mlissner

1

好的,看起来没有库支持它,所以我一直在使用这段代码:

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

1

如果您不想使用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

-1
使用BeautifulSoup或lxml解析HTML。

使用HTML解析器仅仅提取meta refresh标签对于我的需求来说有些过度杀伤力。希望有一个能自动完成这个任务的Python HTTP库。 - hoju
嗯,“meta”确实是一个HTML标签,因此在HTTP库中找到这个功能的可能性很小。 - Otto Allmendinger

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