概述
python通过代理访问网络的简单直接方法:
在程序开头插入以下代码
import socket
import socks
socks.set_default_proxy(socks.SOCKS5, "代理服务器IP", 代理服务器端口)
socket.socket = socks.socksocket
其原理是改重写了socket.socket这个类,使任何通过socket.socket访问网络的对象都通过socks.socksocket来访问网络,
这里要注意,使用socks时要根据代理类型设置参数,类型支持3种:socks.SOCKS5 / socks.SOCKS4 / socks.HTTP
例如以下代码显示实际IP。
from urllib import request
response = request.urlopen('http://httpbin.org/ip')
str1 = response.read().decode()
print(str1)
以下代码则显示代理IP:(request对象是通过socket.socket访问网络的)
from urllib import request
import socket
import socks
socks.set_default_proxy(socks.SOCKS5, "222.129.38.21", 57114)
socket.socket = socks.socksocket
response = request.urlopen('http://httpbin.org/ip')
str1 = response.read().decode()
print(str1)
但我不想全局使用代理,只想imaplib使用代理。那就要找到imaplib中创建网络连接的那段代码,并重写即可。
IMAP4_SSL中创建网络链接的源代码如下:
class IMAP4_SSL(IMAP4):
def _create_socket(self):
host = None if not self.host else self.host
sys.audit("imaplib.open", self, self.host, self.port)
sock = socket.create_connection((host, self.port))
return self.ssl_context.wrap_socket(sock,
server_hostname=self.host)
创建一个IMAP4_SSL的子类,并重写其_create_socket方法:
class SocksIMAP4SSL(IMAP4_SSL):
def _create_socket(self):
p_addr = '222.129.38.21'
p_port = 57114
sock = socks.create_connection((self.host, self.port), proxy_type=PROXY_TYPE_HTTP, proxy_addr=p_addr, proxy_port=p_port)
return self.ssl_context.wrap_socket(sock, server_hostname=self.host)
#
# 关于 return self.ssl_context.wrap_socket(sock, server_hostname=self.host) 见到一些例子加入 ssl.HAS_SNI 判断,这里需要吗?暂时未清楚。如下:
# server_hostname = self.host if ssl.HAS_SNI else None
# return self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
-----------------------------------------
知道以上原理之前,我的摸索过程是这样的:在网上找了一轮代码,试过可行的是这段:
https://stackoverflow.com/questions/51543067/python-imap-proxy-connection (CSDN上也有人贴出 https://blog.csdn.net/alakers/article/details/103891300)
import ssl, time
from socks import create_connection
from socks import PROXY_TYPE_SOCKS4
from socks import PROXY_TYPE_SOCKS5
from socks import PROXY_TYPE_HTTP
from imaplib import IMAP4
from imaplib import IMAP4_PORT
from imaplib import IMAP4_SSL_PORT
from filter import get_user_pass
__author__ = "sstevan"
__license__ = "GPLv3"
__version__ = "0.1"
class SocksIMAP4(IMAP4):
"""
IMAP service trough SOCKS proxy. PySocks module required.
"""
PROXY_TYPES = {"socks4": PROXY_TYPE_SOCKS4,
"socks5": PROXY_TYPE_SOCKS5,
"http": PROXY_TYPE_HTTP}
def __init__(self, host, port=IMAP4_PORT, proxy_addr=None, proxy_port=None,
rdns=True, username=None, password=None, proxy_type="socks5"):
self.proxy_addr = proxy_addr
self.proxy_port = proxy_port
self.rdns = rdns
self.username = username
self.password = password
self.proxy_type = SocksIMAP4.PROXY_TYPES[proxy_type.lower()]
IMAP4.__init__(self, host, port)
def _create_socket(self):
return create_connection((self.host, self.port), proxy_type=self.proxy_type, proxy_addr=self.proxy_addr,
proxy_port=self.proxy_port, proxy_rdns=self.rdns, proxy_username=self.username,
proxy_password=self.password)
class SocksIMAP4SSL(SocksIMAP4):
def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None, proxy_addr=None,
proxy_port=None, rdns=True, username=None, password=None, proxy_type="socks5"):
if ssl_context is not None and keyfile is not None:
raise ValueError("ssl_context and keyfile arguments are mutually "
"exclusive")
if ssl_context is not None and certfile is not None:
raise ValueError("ssl_context and certfile arguments are mutually "
"exclusive")
self.keyfile = keyfile
self.certfile = certfile
if ssl_context is None:
ssl_context = ssl._create_stdlib_context(certfile=certfile,
keyfile=keyfile)
self.ssl_context = ssl_context
SocksIMAP4.__init__(self, host, port, proxy_addr=proxy_addr, proxy_port=proxy_port,
rdns=rdns, username=username, password=password, proxy_type=proxy_type)
def _create_socket(self):
sock = SocksIMAP4._create_socket(self)
server_hostname = self.host if ssl.HAS_SNI else None
return self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
def open(self, host='', port=IMAP4_PORT):
SocksIMAP4.open(self, host, port)
def connect_proxy(imap_server, imap_port, proxy_addr, proxy_port, proxy_type, email, password):
mailbox = SocksIMAP4SSL(host=imap_server, port=imap_port,
proxy_addr=proxy_addr, proxy_port=proxy_port, proxy_type=proxy_type)
try:
mailbox.login(email, password)
print("We are here")
print("OK ",)
except Exception as e:
print(e)
return False
print(mailbox.state)
mailbox.logout()
return True
if __name__ == "__main__":
imap_server = "imap.rambler.ru"
imap_port = 993
proxy_addr = "188.120.224.172"
proxy_port = 59923
proxy_type = "socks5"
email, password = get_user_pass("pm@mail11.rambler.ru:11")
if email is not None:
resp = connect_proxy(imap_server, imap_port, proxy_addr, proxy_port, proxy_type, email, password)
#resp = connect(email, password, "smtp.rambler.ru")
time.sleep(1)
但感觉这段有点把问题搞复杂了(我仅用到IMAP4_SSL,非SSL的不需要)。有没有直接对IMAP4_SSL设置代理的呢?以下这段似乎更合适:https://stackoverflow.com/questions/3386724/how-can-i-fetch-emails-via-pop-or-imap-through-a-proxy/3387230#3387230 (下面称其为B方案)
import ssl
class SocksIMAP4SSL(IMAP4_SSL):
def open(self, host, port=IMAP4_SSL_PORT):
self.host = host
self.port = port
#actual privoxy default setting, but as said, you may want to parameterize it
self.sock = create_connection((host, port), PROXY_TYPE_HTTP, "127.0.0.1", 8118)
self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
self.file = self.sslobj.makefile('rb')
但调试了很久都不成功。
既然B方案是 IMAP4_SSL的子类,只修改了其中的open方法,那么追踪一下原生IMAP4_SSL的open方法的源代码,看看A方案、B方案、原生代码三者之间差别吧,
查看IMAP4_SSL的源代码及查看官方文档得知,IMAP4_SSL是IMAP4的子类,(文档链接)This is a subclass derived from IMAP4
that connects over an SSL encrypted socket。
在官方源代码上发现,A方案实际上是把IMAP4_SSL的原生源代码拷贝了出来,怪不得A方案没有调用IMAP4_SSL啦。
进一步对比跟踪代码,看到A方案也仅仅是修改了 IMAP4 的 _create_socket() 方法 [ 在open()中调用了_create_socket() ]。而_create_socket()中使用了 socket.create_connection,发现A方案跟IMAP4原生代码最核心的区别就是socket.create_connection()传入的参数不一样。
同样是调用socket.create_connection(),原生IMAP4只传入了目标地(host+port)这个参数,A方案传入的参数加了代理ip、代理端口两个参数,
原生:
class IMAP4:
def _create_socket(self):
host = None if not self.host else self.host
sys.audit("imaplib.open", self, self.host, self.port)
return socket.create_connection((host, self.port))
def open(self, host='', port=IMAP4_PORT):
"""Setup connection to remote server on "host:port"
(default: localhost:standard IMAP4 port).
This connection will be used by the routines:
read, readline, send, shutdown.
"""
self.host = host
self.port = port
self.sock = self._create_socket()
self.file = self.sock.makefile('rb')
class IMAP4_SSL(IMAP4):
def _create_socket(self):
sock = IMAP4._create_socket(self)
return self.ssl_context.wrap_socket(sock,
server_hostname=self.host)
def open(self, host='', port=IMAP4_SSL_PORT):
IMAP4.open(self, host, port)
#注意,父类的self.port默认值是IMAP4_PORT(143),这里调用一下父类,self.port默认值就变成IMAP4_SSL_PORT了(993),所以重写open()这段不能删除
A方案:
class SocksIMAP4(IMAP4):
def _create_socket(self):
return create_connection((self.host, self.port), proxy_type=self.proxy_type, proxy_addr=self.proxy_addr,
proxy_port=self.proxy_port, proxy_rdns=self.rdns, proxy_username=self.username,
proxy_password=self.password)
class SocksIMAP4SSL(SocksIMAP4):
def _create_socket(self):
sock = SocksIMAP4._create_socket(self)
server_hostname = self.host if ssl.HAS_SNI else None
return self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
def open(self, host='', port=IMAP4_PORT):
SocksIMAP4.open(self, host, port)
那原生的socket.create_connection() 是怎么用的呢,有些神马玩法呢?
官方文档如下:https://docs.python.org/zh-cn/3.9/library/socket.html?highlight=create_connection#socket.create_connection
这里有一些示例 :Python socket.create_connection() Examples
https://www.programcreek.com/python/example/4161/socket.create_connection
发现官方的 socket.create_connection 并不支持那么多参数啊,再仔细跟踪,IMAP4使用的是socket.create_connection,而A方案使用的是另外一个类,socks.create_connection
一个是socket,一个是socks,一个字母的差别!!
恩,再查一下socks怎么玩吧
socks的说明文档:http://socksipy.sourceforge.net/readme.txt
最后发现三者差异为:
#方案A用 和IMAP4_SSL都是用 ssl._create_stdlib_context(certfile=certfile,keyfile=keyfile).wrap_socket(sock, server_hostname=server_hostname) ,方案B用 ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
他们相同吗?不知道,不想继续折腾了,放弃调试方案B,参考方案A直接重写IMAP4_SSL的_create_socket(),运行登录成功!
最后
以上就是粗心芹菜为你收集整理的通过代理使用imaplib读取邮件的全部内容,希望文章能够帮你解决通过代理使用imaplib读取邮件所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复