我一直在尝试使用requests
库但使用不同的网络接口来向网站提出请求。以下是我尝试使用但无法使用的答案列表。
此answer描述了如何实现我想要的功能,但是它使用了pycurl
。我可以使用pycurl
,但已经了解了有关猴子修补的知识,并想尝试一下。
另外answer似乎一开始就起作用了,因为它不会引发任何错误。但是,我使用Wireshark监视了网络流量,并且数据包是从默认界面发送的。我试图在答案的作者定义的函数set_src_addr
中打印消息,但消息未显示。因此,我认为它正在修补一个从未调用过的函数。我收到HTTP 200响应,因为我已经将套接字绑定到127.0.0.1,所以不会发生。
import socket
real_create_conn = socket.create_connection
def set_src_addr(*args):
address, timeout = args[0], args[1]
source_address = ('127.0.0.1', 0)
return real_create_conn(address, timeout, source_address)
socket.create_connection = set_src_addr
import requests
r = requests.get('http://www.google.com')
r
<Response [200]>
我也尝试过此answer。使用这种方法,我会得到两种错误:
import socket
true_socket = socket.socket
def bound_socket(*a, **k):
sock = true_socket(*a, **k)
sock.bind(('127.0.0.1', 0))
return sock
socket.socket = bound_socket
import requests
这将不允许我创建套接字并引发此error。我还尝试过修改此答案,看起来像这样:
import requests
import socket
true_socket = socket.socket
def bound_socket(*a, **k):
sock = true_socket(*a, **k)
sock.bind(('192.168.0.10', 0))
print(sock)
return sock
socket.socket = bound_socket
r = requests.get('https://www.google.com')
这也不起作用,并引发此error。
我遇到以下问题:我希望每个进程都通过特定的网络接口发送请求。我认为,由于线程共享全局内存(包括库),因此我应该更改代码以使用进程。现在,我想在某个地方应用猴子补丁解决方案,以使每个进程可以使用不同的接口进行通信。我想念什么吗?这是解决此问题的最佳方法吗?
编辑: 我也想知道不同的程序是否可能具有相同库的不同版本。如果它们是共享的,我如何在Python中拥有不同版本的库(每个进程一个)?
答案 0 :(得分:1)
这似乎适用于python3:
In [1]: import urllib3
In [2]: real_create_conn = urllib3.util.connection.create_connection
In [3]: def set_src_addr(address, timeout, *args, **kw):
...: source_address = ('127.0.0.1', 0)
...: return real_create_conn(address, timeout=timeout, source_address=source_address)
...:
...: urllib3.util.connection.create_connection = set_src_addr
...:
...: import requests
...: r = requests.get('http://httpbin.org')
它失败,但出现以下异常:
ConnectionError: HTTPConnectionPool(host='httpbin.org', port=80): Max retries exceeded with url: / (Caused by NewConnectionError("<urllib3.connection.HTTPConnection object at 0x10c4b89b0>: Failed to establish a new connection: [Errno 49] Can't assign requested address",))
答案 1 :(得分:0)
我将记录找到的解决方案,并列出过程中遇到的一些问题。
salparadise正确。它与我发现的第一个answer非常相似。我假设requests
模块导入urllib3
,而后者具有自己的socket
库版本。因此,requests
模块很有可能永远不会直接调用socket
库,但是其功能将由urllib3
模块提供。
我没有首先注意到它,但是我在问题中遇到的第三个片段正在起作用。我拥有ConnectionError
的问题是因为我试图通过无线物理接口使用macvlan虚拟接口(如果我理解正确,则在MAC地址不匹配的情况下会丢弃数据包)。因此,以下解决方案确实有效:
import requests
from socket import socket as backup
import socket
def socket_custom_src_ip(src_ip):
original_socket = backup
def bound_socket(*args, **kwargs):
sock = original_socket(*args, **kwargs)
sock.bind((src_ip, 0))
print(sock)
return sock
return bound_socket
在我的问题中,我将需要多次更改套接字的IP地址。我遇到的问题之一是,如果不对套接字函数进行备份,则对其进行多次更改将导致错误RecursionError: maximum recursion depth exceeded
。发生这种情况是因为在第二次更改中,socket.socket
函数将不是原始函数。因此,我上面的解决方案创建了原始套接字功能的副本,以用作备份其他IP的进一步绑定。
最后,以下是如何使用不同的库实现多个过程的概念证明。有了这个想法,我可以在进程中导入和猴子修补每个套接字,使其具有不同的猴子修补版本。
import importlib
import multiprocessing
class MyProcess(multiprocessing.Process):
def __init__(self, module):
super().__init__()
self.module = module
def run(self):
i = importlib.import_module(f'{self.module}')
print(f'{i}')
p1 = MyProcess('os')
p2 = MyProcess('sys')
p1.start()
<module 'os' from '/usr/lib/python3.7/os.py'>
p2.start()
<module 'sys' (built-in)>
这也可以使用import
语句和global
关键字在以下所有函数内部提供透明访问
import multiprocessing
def fun(self):
import os
global os
os.var = f'{repr(self)}'
fun2()
def fun2():
print(os.system(f'echo "{os.var}"'))
class MyProcess(multiprocessing.Process):
def __init__(self):
super().__init__()
def run(self):
if 'os' in dir():
print('os already imported')
fun(self)
p1 = MyProcess()
p2 = MyProcess()
p2.start()
<MyProcess(MyProcess-2, started)>
p1.start()
<MyProcess(MyProcess-1, started)>
答案 2 :(得分:0)
我遇到了类似的问题,我想让一些本地主机流量不是源自127.0.0.1(我正在通过本地主机测试https连接)
这就是我使用python核心库ssl
和http.client
(参见docs)的方法,因为它似乎比我在网上使用{{1} }库。
requests