2010-02-23 13 views
9

Urllib2 của Python theo sau chuyển hướng 3xx để nhận nội dung cuối cùng. Có cách nào để tạo urllib2 (hoặc một số thư viện khác chẳng hạn như httplib2) cũng theo dõi meta refreshes? Hoặc tôi có cần phải phân tích cú pháp HTML theo cách thủ công cho các thẻ meta làm mới không?cách theo dõi làm mới meta trong Python

Trả lời

1

OK, dường như không có thư viện hỗ trợ nó vì vậy tôi đã được sử dụng mã này:

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

Điều này hoạt động thực sự tốt, cảm ơn bạn đã đăng nó. – Yehonatan

-1

Sử dụng BeautifulSoup hoặc lxml để phân tích cú pháp HTML.

+0

sử dụng một phân tích cú pháp HTML để chỉ trích xuất các thẻ meta refresh là overkill , ít nhất là vì mục đích của tôi. Đã hy vọng có một thư viện HTTP Python đã thực hiện điều này một cách tự động. – hoju

+0

Vâng 'meta' it * là * một thẻ html, do đó bạn không thể tìm thấy chức năng này trong thư viện http. –

8

Dưới đây là một giải pháp sử dụng BeautifulSoup và httplib2 (và giấy chứng nhận xác thực dựa):

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 
3

Một tương tự giải pháp bằng cách sử dụng các yêu cầu và thư viện lxml. Cũng thực hiện một kiểm tra đơn giản rằng điều đang được thử nghiệm thực sự là HTML (một yêu cầu trong việc thực hiện của tôi). Cũng có thể nắm bắt và sử dụng cookie bằng cách sử dụng các phiên của thư viện yêu cầu (đôi khi cần thiết nếu chuyển hướng + cookie đang được sử dụng như một cơ chế chống cào).

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 

Cách sử dụng:

s = requests.session() 
r = s.get(url) 
# test for and follow meta redirects 
r = follow_redirections(r, s) 
+0

Đôi khi chuyển hướng làm mới meta trỏ đến URL tương đối. Ví dụ: Facebook thực hiện ' '. Nó sẽ là tốt để phát hiện các URL tương đối và thêm vào kế hoạch và máy chủ. –

+0

@JosephMornin: Được chỉnh sửa. Tôi nhận ra nó vẫn không hỗ trợ chuyển hướng vòng tròn mặc dù ... luôn luôn một cái gì đó. – mlissner

0

Nếu bạn không muốn sử dụng BS4, bạn có thể sử dụng lxml như thế này:

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