2010-02-10 25 views
13

Tôi muốn nói urllib2.urlopen (hoặc công cụ tùy chỉnh) để sử dụng 127.0.0.1 (hoặc ::1) để giải quyết địa chỉ. Tuy nhiên, tôi sẽ không thay đổi số /etc/resolv.conf của mình.Yêu cầu urllib2 sử dụng DNS tùy chỉnh

Một giải pháp có thể là sử dụng công cụ như dnspython để truy vấn địa chỉ và httplib để tạo công cụ mở url tùy chỉnh. Tôi muốn nói với urlopen để sử dụng máy chủ định danh tùy chỉnh. Bất kỳ đề xuất?

Trả lời

20

Dường như độ phân giải tên cuối cùng được xử lý bởi socket.create_connection.

-> urllib2.urlopen 
-> httplib.HTTPConnection 
-> socket.create_connection 

Mặc dù một khi tiêu đề "Máy chủ lưu trữ:" đã được đặt, bạn có thể giải quyết máy chủ và chuyển địa chỉ IP qua trình mở.

Tôi khuyên bạn nên phân loại httplib.HTTPConnection và quấn phương thức connect để sửa đổi self.host trước khi chuyển đến socket.create_connection.

Sau đó phân lớp HTTPHandler (và HTTPSHandler) để thay thế cho phương pháp http_open với một mà đi của bạn HTTPConnection thay vì httplib của riêng để do_open.

Như thế này:

import urllib2 
import httplib 
import socket 

def MyResolver(host): 
    if host == 'news.bbc.co.uk': 
    return '66.102.9.104' # Google IP 
    else: 
    return host 

class MyHTTPConnection(httplib.HTTPConnection): 
    def connect(self): 
    self.sock = socket.create_connection((MyResolver(self.host),self.port),self.timeout) 
class MyHTTPSConnection(httplib.HTTPSConnection): 
    def connect(self): 
    sock = socket.create_connection((MyResolver(self.host), self.port), self.timeout) 
    self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) 

class MyHTTPHandler(urllib2.HTTPHandler): 
    def http_open(self,req): 
    return self.do_open(MyHTTPConnection,req) 

class MyHTTPSHandler(urllib2.HTTPSHandler): 
    def https_open(self,req): 
    return self.do_open(MyHTTPSConnection,req) 

opener = urllib2.build_opener(MyHTTPHandler,MyHTTPSHandler) 
urllib2.install_opener(opener) 

f = urllib2.urlopen('http://news.bbc.co.uk') 
data = f.read() 
from lxml import etree 
doc = etree.HTML(data) 

>>> print doc.xpath('//title/text()') 
['Google'] 

Rõ ràng có những vấn đề chứng chỉ nếu bạn sử dụng HTTPS, và bạn sẽ cần phải điền MyResolver ...

+0

Tôi không nghĩ mình sẽ cần HTTPS ngay bây giờ, vì vậy điều này sẽ hoàn toàn đủ! Cảm ơn nhiều! –

+0

Cũng có thể ghi đè lên 'HTTPConnection._create_connection', có sẵn từ Python 2.7.7 và 3.5 do http://bugs.python.org/issue7776. –

0

Bạn sẽ cần phải thực hiện tra cứu dns của riêng bạn khách hàng (hoặc sử dụng dnspython như bạn đã nói). Thủ tục tra cứu tên trong glibc khá phức tạp để đảm bảo khả năng tương thích với các hệ thống tên khác không phải dns. Ví dụ, không có cách nào để chỉ định một máy chủ DNS cụ thể trong thư viện glibc.

16

Cách khác (bẩn) là vá khỉ socket.getaddrinfo.

Ví dụ: mã này thêm bộ nhớ cache (không giới hạn) để tìm kiếm dns.

import socket 
prv_getaddrinfo = socket.getaddrinfo 
dns_cache = {} # or a weakref.WeakValueDictionary() 
def new_getaddrinfo(*args): 
    try: 
     return dns_cache[args] 
    except KeyError: 
     res = prv_getaddrinfo(*args) 
     dns_cache[args] = res 
     return res 
socket.getaddrinfo = new_getaddrinfo 
+2

Một lợi thế của việc hack này cũng chặn đứng gần như tất cả các tra cứu dns trong python, không chỉ thông qua 'urlopen' –

+0

đây là một giải pháp tốt hơn nếu các máy chủ có phạm vi nhỏ. tôi có tốc độ 10X với nó. :) –

Các vấn đề liên quan