最新消息: 新版网站上线了!!!

Python 使用指定的网卡发送HTTP请求的实例

ÐèÇó: һ̨»úÆ÷ÉÏÓжà¸öÍø¿¨, ÈçºÎ·ÃÎÊÖ¸¶¨µÄ URL ʱʹÓÃÖ¸¶¨µÄÍø¿¨·¢ËÍÊý¾ÝÄØ?

$ curl --interface eth0 www.baidu.com # curl interface ¿ÉÒÔÖ¸¶¨Íø¿¨

ÔĶÁ urllib.py µÄÔ´Âë, ×·Êöµ½ open_http ¨C> httplib.HTTP ¨C> httplib.HTTP._connection_class = HTTPConnection

HTTPConnection ÔÚ´´½¨µÄʱºò»áÖ¸¶¨Ò»¸ö source_address.

HTTPConnection.connect ʱµ÷Óà HTTPConnection._create_connection = socket.create_connection

# ÏÈ¿´Ò»Ï±¾µØÍø¿¨ÐÅÏ¢
$ ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
  options=3<RXCSUM,TXCSUM>
  inet6 ::1 prefixlen 128 
  inet 127.0.0.1 netmask 0xff000000 
  inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
  nd6 options=1<PERFORMNUD>
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
  ether c8:e0:eb:17:3a:73 
  inet6 fe80::cae0:ebff:fe17:3a73%en0 prefixlen 64 scopeid 0x4 
  inet 192.168.20.2 netmask 0xffffff00 broadcast 192.168.20.255
  nd6 options=1<PERFORMNUD>
  media: autoselect
  status: active
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
  options=4<VLAN_MTU>
  ether 0c:5b:8f:27:9a:64 
  inet6 fe80::e5b:8fff:fe27:9a64%en8 prefixlen 64 scopeid 0xa 
  inet 192.168.8.100 netmask 0xffffff00 broadcast 192.168.8.255
  nd6 options=1<PERFORMNUD>
  media: autoselect (100baseTX <full-duplex>)
  status: active

¿ÉÒÔ¿´µ½en0ºÍen1, ÕâÁ½¿éÍø¿¨¶¼¿ÉÒÔ·ÃÎʹ«Íø. lo0ÊDZ¾µØ»Ø»·.

Ö±½ÓÐÞ¸Ä socket.py ×ö²âÊÔ.

def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
           source_address=None):
  """If *source_address* is set it must be a tuple of (host, port)
  for the socket to bind as a source address before making the connection.
  An host of '' or port 0 tells the OS to use the default.
  source_address Èç¹ûÉèÖÃ, ±ØÐëÊÇ´«µÝÔª×é (host, port), ĬÈÏÊÇ ("", 0) 
  """

  host, port = address
  err = None
  for res in getaddrinfo(host, port, 0, SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    sock = None
    try:
      sock = socket(af, socktype, proto)
      # sock.bind(("192.168.20.2", 0)) # en0
      # sock.bind(("192.168.8.100", 0)) # en1
      # sock.bind(("127.0.0.1", 0)) # lo0
      if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
        sock.settimeout(timeout)
      if source_address:
        print "socket bind source_address: %s" % source_address
        sock.bind(source_address)
      sock.connect(sa)
      return sock

    except error as _:
      err = _
      if sock is not None:
        sock.close()
  if err is not None:
    raise err
  else:
    raise error("getaddrinfo returns an empty list")

²Î¿¼ËµÃ÷Îĵµ, Ö±½Ó·ÖÈý´Î°ó¶¨²»Í¨Íø¿¨µÄ IP µØÖ·, ¶Ë¿ÚÉèÖÃΪ0.

# ²âÊÔ en0
$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'
.148.245.16

# ²âÊÔ en1
$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'
.94.115.227

# ²âÊÔ lo0
$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 87, in urlopen
  return opener.open(url)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 213, in open
  return getattr(self, name)(url)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 350, in open_http
  h.endheaders(data)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1049, in endheaders
  self._send_output(message_body)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 893, in _send_output
  self.send(msg)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 855, in send
  self.connect()
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 832, in connect
  self.timeout, self.source_address)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 578, in create_connection
  raise err
IOError: [Errno socket error] [Errno 49] Can't assign requested address

²âÊÔͨ¹ý, ˵Ã÷ÔÚ¶àÍø¿¨Çé¿öÏÂ, ´´½¨ socket ʱ°ó¶¨Ä³¿éÍø¿¨µÄ IP ¾Í¿ÉÒÔ, ¶Ë¿ÚÐèÒªÉèÖÃΪ0. Èç¹û¶Ë¿Ú²»ÉèÖÃΪ0, µÚ¶þ´ÎÇëÇóʱ, ¿ÉÒÔ¿´µ½Å×Òì³£, ¶Ë¿Ú±»Õ¼ÓÃ.

Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 87, in urlopen
  return opener.open(url)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 213, in open
  return getattr(self, name)(url)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 350, in open_http
  h.endheaders(data)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1049, in endheaders
  self._send_output(message_body)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 893, in _send_output
  self.send(msg)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 855, in send
  self.connect()
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 832, in connect
  self.timeout, self.source_address)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 577, in create_connection
  raise err
IOError: [Errno socket error] [Errno 48] Address already in use

Èç¹ûÊÇÔÚÏîÄ¿ÖÐ, Ö»ÐèÒª°Ñ socket.create_connection Õâ¸öº¯ÊýµÄÐβΠsource_address ÉèÖÃΪ¶ÔÓ¦Íø¿¨µÄ (IP, 0) ¾Í¿ÉÒÔ.

# test-interface_urllib.py
import socket
import urllib, urllib2

_create_socket = socket.create_connection

SOURCE_ADDRESS = ("127.0.0.1", 0)
#SOURCE_ADDRESS = ("172.28.153.121", 0)
#SOURCE_ADDRESS = ("172.16.30.41", 0)

def create_connection(*args, **kwargs):
  in_args = False
  if len(args) >=3:
    args = list(args)
    args[2] = SOURCE_ADDRESS
    args = tuple(args)
    in_args = True
  if not in_args:
    kwargs["source_address"] = SOURCE_ADDRESS
  print "args", args
  print "kwargs", str(kwargs)
  return _create_socket(*args, **kwargs)

socket.create_connection = create_connection

print urllib.urlopen("http://ip.haschek.at").read()

ͨ¹ý²âÊÔ, ¿ÉÒÔ·¢ÏÖÒѾ­¿ÉÒÔͨ¹ýÖƶ¨µÄÍø¿¨·¢ËÍÊý¾Ý, ²¢ÇÒ IP µØÖ·¶ÔÓ¦Íø¿¨·ÖÅäµÄ IP.

ÎÊÌâ, ÅÀ³æ¾­³£Ê¹Óà requests, requests ÊÇ·ñÖ§³ÖÄØ. ͨ¹ý²âÊÔ, ¿ÉÒÔ·¢ÏÖ, requests ²¢Ã»ÓÐʹÓà python ÄÚÖÃµÄ socket Ä£¿é.

¿´Ô´Âë, requests ÊÇÈç¹û´´½¨µÄ socket Á¬½ÓÄØ. ·½·¨ºÍ²é¿´ urllib ´´½¨socket µÄ·½Ê½Ò»Ñù. ¾ßÌå¾Í²»Ð´ÁË.

ÒòΪÎÒÓõÄÊÇ python 2.7, ËùÒÔ¿ÉÒÔ¶¨Î»µ½ requests ʹÓÃµÄ socket Ä£¿éÊÇ urllib3.utils.connection µÄ.

Ð޸ķ½·¨ºÍ urllib Ïà²î²»´ó.

import urllib3.connection
_create_socket = urllib3.connection.connection.create_connection
# pass

urllib3.connection.connection.create_connection = create_connection
# pass

ÔËÐкó, ¿ÉÄÜ»áÅ׳öÒì³£. requests.exceptions.ConnectionError: Max retries exceeded with .. Invalid argument

Õâ¸öÒì³£²»ÊÇÿ´Î³öÏÖ, ¸ú IP ¶ÎÓйØϵ, ÌøתµÝ¹é²ãÊýÌ«¶àµ¼ÖÂ, Ö»ÐèÒª½« kwargs ÖÐµÄ socket_optionsÈ¥µô¼´¿É. 127.0.0.1¿Ï¶¨»á³öÒì³£.

import socket
import urllib
import urllib2
import urllib3.connection

import requests as req

_default_create_socket = socket.create_connection
_urllib3_create_socket = urllib3.connection.connection.create_connection


SOURCE_ADDRESS = ("127.0.0.1", 0)
#SOURCE_ADDRESS = ("172.28.153.121", 0)
#SOURCE_ADDRESS = ("172.16.30.41", 0)

def default_create_connection(*args, **kwargs):
  try:
    del kwargs["socket_options"]
  except:
    pass
  in_args = False
  if len(args) >=3:
    args = list(args)
    args[2] = SOURCE_ADDRESS
    args = tuple(args)
    in_args = True
  if not in_args:
    kwargs["source_address"] = SOURCE_ADDRESS
  print "args", args
  print "kwargs", str(kwargs)
  return _default_create_socket(*args, **kwargs)

def urllib3_create_connection(*args, **kwargs):
  in_args = False
  if len(args) >=3:
    args = list(args)
    args[2] = SOURCE_ADDRESS
    in_args = True
    args = tuple(args)
  if not in_args:
    kwargs["source_address"] = SOURCE_ADDRESS
  print "args", args
  print "kwargs", str(kwargs)
  return _urllib3_create_socket(*args, **kwargs)

socket.create_connection = default_create_connection
# ÒòΪż¶û»á³öÎÊÌâ, ËùÒÔʹÓÃĬÈ쵀 socket.create_connection
# urllib3.connection.connection.create_connection = urllib3_create_connection
urllib3.connection.connection.create_connection = default_create_connection

print " *** test requests: " + req.get("http://ip.haschek.at").content
print " *** test urllib: " + urllib.urlopen("http://ip.haschek.at").read()
print " *** test urllib2: " + urllib2.urlopen("http://ip.haschek.at").read()

×¢Òâ: ʹÓà urllib3.utils.connection ºÃÏñ²»Æð×÷ÓÃ

ÉÔ΢ÔÙÍêÉÆÒ»ÏÂ, ¾ÍÊǰѸù¾ÝÍø¿¨Ãû×Ô¶¯»ñÈ¡ IP.

import subprocess

def get_all_net_devices():
  sub = subprocess.Popen("ls /sys/class/net", shell=True, stdout=subprocess.PIPE)
  sub.wait()
  net_devices = sub.stdout.read().strip().splitlines()
  # ['eth0', 'eth1', 'lo']
  # ÕâÀï¼òµ¥¹ýÂËÒ»ÏÂÍø¿¨Ãû×Ö, ¸ù¾ÝÐèÇó¸Ä¶¯
  net_devices = [i for i in net_devices if "ppp" in i]
  return net_devices
ALL_DEVICES = get_all_net_devices()

def get_local_ip(device_name):
  sub = subprocess.Popen("/sbin/ifconfig en0 | grep '%s ' | awk '{print $2}'" % device_name, shell=True, stdout=subprocess.PIPE)
  sub.wait()
  ip = sub.stdout.read().strip()
  return ip

def random_local_ip():
  return get_local_ip(random.choice(ALL_DEVICES))

# code ...

Ö»ÐèÒª°Ñ args[2] = SOURCE_ADDRESS ºÍ kwargs["source_address"] = SOURCE_ADDRESS¸Ä³É random_local_ip() »òÕß get_local_ip("eth0")

ÖÁÓÚÓÐʲôÓÃ;, ¾ÍȫƾÏëÏóÁË.

ÒÔÉÏÕâƪPython ʹÓÃÖ¸¶¨µÄÍø¿¨·¢ËÍHTTPÇëÇóµÄʵÀý¾ÍÊÇС±à·ÖÏí¸ø´ó¼ÒµÄÈ«²¿ÄÚÈÝÁË£¬Ï£ÍûÄܸø´ó¼ÒÒ»¸ö²Î¿¼£¬Ò²Ï£Íû´ó¼Ò¶à¶àÖ§³Ö½Å±¾Ö®¼Ò¡£

转载请注明:谷谷点程序 » Python 使用指定的网卡发送HTTP请求的实例