¿Cómo configuro la IP/interfaz de origen con Python y urllib2?Interfaz de origen con Python y urllib2
Respuesta
Desafortunadamente la pila de módulos de biblioteca estándar en uso (urllib2, httplib, socket) es algo mal diseñado para el propósito - en el punto clave en el funcionamiento, HTTPConnection.connect
(en httplib) delegados a socket.create_connection
, que a su vez da no se "engancha" en absoluto entre la creación de la instancia de socket sock
y la llamada sock.connect
, para que inserte el sock.bind
justo antes de sock.connect
que es lo que necesita para configurar el IP de origen (estoy evangelizando ampliamente por NO diseñar abstracciones en tales una forma hermética, excesivamente encapsulada - Voy a estar hablando de eso en OSCON este jueves bajo el título "Zen y el arte del mantenimiento de la abstracción" - pero aquí su problema es cómo lidiar con una pila de abstracciones que fueron diseñadas de esta manera, suspiro).
Cuando enfrenta estos problemas, solo tiene dos soluciones no tan buenas: copiar, pegar y editar el código mal diseñado en el que necesita colocar un "gancho" que el diseñador original no atendió ; o, "mono-parche" ese código. Ninguno de los dos es BUENO, pero ambos pueden funcionar, así que al menos agradezcamos que tengamos esas opciones (usando un lenguaje de código abierto y dinámico). En este caso, creo que me gustaría ir para el mono-parches (que es malo, pero copiar y pegar codificación es aún peor) - un fragmento de código como:
import socket
true_socket = socket.socket
def bound_socket(*a, **k):
sock = true_socket(*a, **k)
sock.bind((sourceIP, 0))
return sock
socket.socket = bound_socket
En función de sus necesidades específicas (ver necesita todos los enchufes para estar vinculados a la misma fuente IP, o ...?) simplemente puede ejecutar esto antes de usar urllib2
normalmente, o (en formas más complejas, por supuesto) ejecutarlo en caso necesario solo para los enchufes salientes que necesita para enlazar de cierta manera (luego, cada vez restaure socket.socket = true_socket
para salir del camino para futuras tomas que aún no se han creado). La segunda alternativa agrega sus propias complicaciones para orquestar correctamente, por lo que espero que aclare si necesita complicaciones antes de explicarlas todas.
La buena respuesta de AKX es una variante de la alternativa "copiar/pegar/editar", así que no necesito ampliar mucho sobre eso - tenga en cuenta que no reproduce exactamente socket.create_connection
en su método connect
, vea el fuente here (al final de la página) y decida qué otra funcionalidad de la función create_connection
puede incluir en su versión copiada/pegada/editada si decide seguir esa ruta.
no solo una respuesta completa, sino, tal vez, el primer ejemplo de buen uso de parches de mono que he visto –
Tx @Roberto - es más un "mal menor", pero, sí, cuando se enfrenta a una abstracción eso está sellado en contra de sus necesidades (faltando los ganchos/"fugas" requeridos), el parcheo puede ser "bueno" (en el mismo sentido que tirar un diente enfermo sin remedio es "bueno" ;-). –
@Alex Martelli: Gracias. ¡El parche de mono funcionó bien! :) – jonasl
Esto parece funcionar.
import urllib2, httplib, socket
class BindableHTTPConnection(httplib.HTTPConnection):
def connect(self):
"""Connect to the host and port specified in __init__."""
self.sock = socket.socket()
self.sock.bind((self.source_ip, 0))
if isinstance(self.timeout, float):
self.sock.settimeout(self.timeout)
self.sock.connect((self.host,self.port))
def BindableHTTPConnectionFactory(source_ip):
def _get(host, port=None, strict=None, timeout=0):
bhc=BindableHTTPConnection(host, port=port, strict=strict, timeout=timeout)
bhc.source_ip=source_ip
return bhc
return _get
class BindableHTTPHandler(urllib2.HTTPHandler):
def http_open(self, req):
return self.do_open(BindableHTTPConnectionFactory('127.0.0.1'), req)
opener = urllib2.build_opener(BindableHTTPHandler)
opener.open("http://google.com/").read() # Will fail, 127.0.0.1 can't reach google.com.
Sin embargo, tendrá que encontrar la manera de parametrizar "127.0.0.1".
¡Esto funciona perfectamente! Muchas gracias. –
@DaveRawks: ¿En qué sistema obtuviste el éxito? No puedo vincular la interfaz de red en Windows 7 –
El código funciona para mí en Linux y OSX. Nunca he escrito código de socket en Windows, pero sospecho que la falta de tomas sin procesar en el espacio de usuario de windows podría causar problemas. –
Pensé en seguir con una versión ligeramente mejor del parche de mono. Si necesita poder establecer diferentes opciones de puerto en algunos de los sockets o está usando algo como SSL, el código siguiente funciona un poco mejor.
_ip_address = None
def bind_outgoing_sockets_to_ip(ip_address):
"""This binds all python sockets to the passed in ip address"""
global _ip_address
_ip_address = ip_address
import socket
from socket import socket as s
class bound_socket(s):
def connect(self, *args, **kwargs):
if self.family == socket.AF_INET:
if self.getsockname()[0] == "0.0.0.0" and _ip_address:
self.bind((_ip_address, 0))
s.connect(self, *args, **kwargs)
socket.socket = bound_socket
Sólo tienes que obligar a la toma de conexión si es necesario ejecutar algo así como un servidor web en el mismo proceso que necesita para unirse a una dirección IP diferente.
Razonamiento esté de mí mono-parche en el nivel más alto disponible, aquí es una alternativa a la respuesta de Alex qué parches httplib
en lugar de socket
, aprovechando argumento source_address
palabra clave httplib.HTTPSConnection.__init__()
's (que no está expuesta por urllib2
, AFAICT). Probado y trabajando en Python 2.7.2.
import httplib
HTTPSConnection_real = httplib.HTTPSConnection
class HTTPSConnection_monkey(HTTPSConnection_real):
def __init__(*a, **kw):
HTTPSConnection_real.__init__(*a, source_address=(SOURCE_IP, 0), **kw)
httplib.HTTPSConnection = HTTPSConnection_monkey
Aquí hay un refinamiento adicional que hace uso de HTTPConnection's source_address argument (introducido en Python 2.7):
import functools
import httplib
import urllib2
class BoundHTTPHandler(urllib2.HTTPHandler):
def __init__(self, source_address=None, debuglevel=0):
urllib2.HTTPHandler.__init__(self, debuglevel)
self.http_class = functools.partial(httplib.HTTPConnection,
source_address=source_address)
def http_open(self, req):
return self.do_open(self.http_class, req)
Esto nos da una costumbre urllib2.HTTPHandler aplicación que es source_address conscientes. Podemos añadir a una nueva urllib2.OpenerDirector e instalarlo como el abridor por defecto (para futuras llamadas urlopen()) con el siguiente código:
handler = BoundHTTPHandler(source_address=("192.168.1.10", 0))
opener = urllib2.build_opener(handler)
urllib2.install_opener(opener)
+1. Este, creo, debería ser el método recomendado con el módulo 'httplib' actualizado de Python 2.7. Por cierto, la cadena de direcciones IPv4 debe ponerse entre comillas. Intenté editar tu publicación, pero un cambio tan pequeño no pudo pasar el filtro de trivialidad de stackoverflow. –
A partir de Python 2.7 httplib.HTTPConnection tenía source_address agregado a él, que le permite ofrecer un par de puertos IP para enlazar.
Ver: http://docs.python.org/2/library/httplib.html#httplib.HTTPConnection
- 1. Autenticación de Windows con Python y urllib2
- 2. Python: Urllib2 y OpenCV
- 3. Python - urllib2 y cookielib
- 4. Python urllib2 con keep alive
- 5. Python: ¿Procesamiento de Javascript con urllib2?
- 6. RSS Manejo de redirecciones con Python/urllib2
- 7. Python urllib2 HTTPS y proxy autenticación NTLM
- 8. pitón: urllib2 utilizando diferentes interfaz de red
- 9. cuelga detecta con Python urllib2.urlopen
- 10. Ambos usando cookies y un proxy en Python con urllib2
- 11. Python URLLib/URLLib2 POST
- 12. Python urllib2, autenticación HTTP básica y tr.im
- 13. Python urllib2 Encabezado de respuesta
- 14. urllib2 con cookies
- 15. Python: urllib/urllib2/httplib confusion
- 16. Python urllib2 autenticación básica Problema
- 17. Python urllib2 llenado y recuperación de formularios automáticos
- 18. Python urllib y urllib2 no abren URL de localhost?
- 19. python: urllib2 cómo enviar cookies con la solicitud de urlopen
- 20. urllib2 y json
- 21. python urllib2: restablecimiento de conexión por pares
- 22. Proceso de Python bloqueado por urllib2
- 23. Proxy con urllib2
- 24. ¿Por qué obtengo urllib2.HTTPError con urllib2 y no hay errores con urllib?
- 25. Python - error de tipo irresistible en urllib2
- 26. Python urllib2 URLError Código de estado HTTP.
- 27. Disminución con urllib2
- 28. Establecer host-header al usar Python y urllib2
- 29. Autenticación HTTP básica no parece funcionar con urllib2 en python
- 30. HTTP POST autenticado con carga útil XML usando Python urllib2
Es razonable utilizar "peticiones" biblioteca o pycurl. Siempre tropiezas con el mal diseño de urllib2, si lo usas para tareas no triviales. – HighCat