迫于种种原因,有时候我们不得不做一些流量转发的操作。
比如写爬虫。
写爬虫时,时常要与目标网站的“运维”、“程序员”斗智斗勇,而“代理”作为行走江湖必备的入门级杀手锏,自然是要逢场必上。
有匪君子,如切如磋,如琢如磨。 有匪君子,如切如磋,如琢如磨。
而这个“杀手锏”是不是那么好用却与代理的数量、质量息息相关。
我时常苦恼于“维护代理”和“切换代理”的麻烦,我堂堂一代“爬虫大王”,冉冉升起的“东方新星”,万千少女的。。。呃,好像有点扯远了。总之,怎能沉溺于区区“代理切换”这种微不足道的小事中。
那么,如果可以用一个二级代理来封装这些事情岂不美哉!
所谓空想不如实干,不仅要实干,更要撸起袖子加油干!于是抓起 Python 就撸了一个流量转发程序。
程序不长,去掉空行只有 45 行,但完整的实现了流量转发的功能,基于 ssclient 实现了二级代理,完整代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import select
from concurrent.futures import ThreadPoolExecutor
def process(conn, addr):
proxy_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxy_conn.connect(('127.0.0.1', 1080))
conn.setblocking(socket.MSG_DONTWAIT)
proxy_conn.setblocking(socket.MSG_DONTWAIT)
closed = False
while not closed:
rlist, _, _ = select.select([conn, proxy_conn], [], [])
for r in rlist:
w = proxy_conn if r is conn else conn
try:
d = r.recv(1024)
if not d:
closed = True
break
w.sendall(d)
except:
closed = True
break
try:
proxy_conn.shutdown(socket.SHUT_RDWR)
proxy_conn.close()
except:
pass
try:
conn.shutdown(socket.SHUT_RDWR)
conn.close()
except:
pass
def start():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 1991))
s.listen(10)
pool = ThreadPoolExecutor(128)
print('Server listen on 0.0.0.0:1991')
while True:
conn, addr = s.accept()
pool.submit(process, conn, addr)
if __name__ == '__main__':
start()
流量转发既已实现,剩下的事情就很好解决了,无非是维护一个可用的“代理池”,在转发流量的过程中随机选取一个可用代理,之后进行流量转发即可。
完整的功能将在我开发完成之后在博客中跟大家分享,并同步发布到我的微信公众号上。有兴趣的朋友可以关注我的公众号哟~
本帖同步更新在我的博客上,原文在此。
1
AngelCriss 2018-07-22 00:57:29 +08:00 via Android 1
互联网的冗余信息又多了点而已
|
2
429839446 2018-07-22 01:01:34 +08:00
居然不用 coroutine
|
3
AngelCriss 2018-07-22 01:06:35 +08:00 via Android
@429839446 很奇怪
|
5
nbndco 2018-07-22 08:14:05 +08:00 via iPhone
这代码连逻辑都不对……
|
6
lychnis 2018-07-22 09:26:17 +08:00 via Android
我不懂 python 的人乍一看这是同步的吧 这能用?
|
7
naiba 2018-07-22 09:54:00 +08:00 via Android
|
9
wwqgtxx 2018-07-22 10:27:23 +08:00 2
从性能上更推荐这个
https://github.com/wangyu-/tinyPortMapper 而且就算用 Python 也应该用 Selector 或者是 asyncio 吧,这种用 ThreadPoolExecutor 的不觉得有点效率太低了么 |
11
nozer OP @429839446 您这建议挺不错,您要不说,我还真不知道有这玩意儿。。。。当然这东西只是初步用来实现我的想法,一切从简单、易于理解的角度出发,在完善后续功能的过程中会考虑性能问题。
|
12
nozer OP @wwqgtxx 用 Python 主要是考虑到后面要增加自己爬取、维护共享代理的功能,全部用 Python 实现会更加方便一些。
|
13
nbndco 2018-07-22 11:02:55 +08:00
@nozer
你虽然在标题里强调简单(行数少),但简单的主要原因只是因为你没写最重要的东西。当前的实现基本上就是同步的,虽然看起来代码是异步的,但是 IO 几乎没有复用,和同步唯一的区别就是一个线程处理两个方向而不是一个方向。并发只有 128 实在是少了一点。 不过你就算自己做,不用 asyncio,完全 IO 复用,也不会长太多。 |
14
misaka19000 2018-07-22 11:06:38 +08:00
既然都用了 select 了,就不需要再用多线程了啊。。。
|
15
Cbdy 2018-07-22 11:10:07 +08:00
source.on('data', data => destination.write(data))
destination.on('data', data => source.write(data)) 流量转发不是两行代码的事情吗?至于这么麻烦 |
16
nozer OP @misaka19000 select 是用在单个请求的两端进行流量转发,线程是用来处理新的请求。
|
17
nozer OP @misaka19000 我明白您的意思了。
|
18
nozer OP @nbndco 兄弟回答的很中肯,一看就是仔细读过程序的,直接点出了要害。异步处理的却不够好,大家也提出了很多很有用的意见,不过兄弟能不能说下到底哪里连逻辑都不对,我好修改免得让别人看了误入歧途。。。
|
19
nozer OP @Cbdy 大神,不需要两行,一行就够了。
您看我帮您改写一下:s.on('data', data => d.write(data)) ;d.on('data', data => s.write(data)) ;。 ? java 怎能少了分号 |
20
aheadlead 2018-07-22 12:01:41 +08:00 via iPhone
流量转发我都用 shell 写的… nc 足够解决很多问题了
|
21
lance6716 2018-07-22 12:06:09 +08:00 via Android 1
@AngelCriss 经常有些半吊子水平的的人写博客,搞得我一搜东西中文博文基本没法看
|
22
congeec 2018-07-22 12:06:44 +08:00 via iPhone
|
23
nozer OP |
24
nozer OP 。。。上一篇回复 at 错了人,aheadlead 抱歉抱歉。
@aheadlead @congeec 咱们解决的不是同一个问题,我是要以此为基础随机选取代理,并不是仅仅做流量转发,流量转发只是前提,我在帖子里面也有描述(虽然比较啰嗦)。 关于态度问题,好好讨论问题的,我向来认真对待。在我的回复里面,凡是指出问题的、提出建议的,只要中肯我都接受,并确认后期采纳,其它的我也会给出解释。 其它的上来就奇奇怪怪的回答,我也只好回应一些奇奇怪怪的东西。 比如这位大神 @lance6716 上来就说一些与帖子本身无关的东西,我以为真的是 NB 哄哄的大神,结果点开帖子集一看,全是口水,半点干货没有,纯粹就是杠,这种人,我也只好他进行一番批判。 毕竟,今天是周末嘛。 |
25
AngelCriss 2018-07-22 12:34:06 +08:00 via Android
@nozer 其实你这帖子也没有干活。。
|
26
nozer OP |
27
AngelCriss 2018-07-22 12:44:16 +08:00 via Android
@nozer 我是看到标题进来的,一看很厉害的样子,然而看了代码,发现真是浪费时间,所以有了 1 楼的回复,说的比较隐晦而已。
至于代码,没啥好说的,即使不会 Python 的人稍微看点文档也能写出来。 你批评的那位只不过直白的说出来了而已。 |
28
lihongjie0209 2018-07-22 13:26:30 +08:00
@wwqgtxx #9 io 操作用 thread 没问题
|
29
lance6716 2018-07-22 18:09:37 +08:00 via Android
@nozer 为什么你会觉得干货体现在论坛上,应该是课程作业、论文和项目啊…最近写了一个期末作业是支持作业调度的 shell,前置知识是 apue 这本书和一点点编译原理。然而我也不觉得有什么值得发出来的,毕竟都有 bash 了,我这种小打小闹的也是给互联网填充辣鸡
|
30
Kilerd 2018-07-22 22:09:54 +08:00 2
现在的中文 IT 圈就充斥着这样大量的低级而且可能充满大量问题的博文,甚至还会用几十年前的技术来实现某一个功能。
更可笑的是,还存在着更大一群人乐于转载这些内容,对,不加任何分辨的那种。奇怪的是,他们在发布这些文章的时候,不会去 review 一下排版问题。 最荒诞的是,大量的人根本没有能力去区分这一类的文章。(我见过有人真的认认真真地把那些因为到处复制转载而导致代码无法正常显示的文章阅读了,认真地敲里面的代码。 |
31
nozer OP @Kilerd 阁下大才,所谓细微之处见真章,阁下既然指出“排版问题”至少是看过帖子内容的。我又细看了一下帖子,虽然排版实在算不上高明,但原本就是这样排的,只不过其中多出一句与本帖无关的话确系不应该。这是我的的失误,不过帖子没法编辑,也只能放任不管了。想必阁下必是做事细心之人,这点的确值得我辈学习,我会在今后注意这个问题。
不过再看阁下其它语句,又实在令人遗憾。 首先,第一句就显得很低级“现在的中文 IT 圈就充斥着这样大量的低级而且可能充满大量问题的博文,甚至还会用几十年前的技术来实现某一个功能。 ” 阁下以上帝视角站在“中文 IT 圈”之外,先把自己摘出来,再不管三七二十一把对方说成整个圈子的“低级”,我辈“米粒之辉”自然不敢同阁下这种“日月之光”比肩,想必阁下也实战过许多项目,绝不是那种做仅仅做过几个练习 Demo 就目空一切之辈,必然是胸中沟壑万千,从来不会写出这种“低级”博文,否则亦不敢说出如此狂妄之言。 阁下还说了“可能充满大量问题的博文”,就此暴露了阁下并非就事论事而是借题发挥。先不论博文到底是不是真的有问题,老是用这种“不确定性词汇”就足以让人觉得阁下办事不牢,因为阁下仅仅是凭借自己的臆想就直接得出结论,至于是不是真的“可能充满大量问题”谁关心呢,我只要发泄一下就好,这是不成熟的表现。 阁下又说“用几十年前的技术来实现某一个功能”,不觉让人发笑。Python2 在 2000 年发布至今不过 18 年,让 python 走向兴盛的 2.7 版也不过是 2010 年发布,距今不过 8 年。用几十年来形容似乎有点夸张过头了吧?况且,代码中用到的 socket 和线程池不过是最基础的手段,不管是 C、java 都在大量使用,你可以说它历史悠久没有异步编程那么优雅,但你要说这玩意儿就不应该再出现在人世间,恕我不敢苟同。 然后,阁下还有一句“最荒诞的是,大量的人根本没有能力去区分这一类的文章。(我见过有人真的认认真真地把那些因为到处复制转载而导致代码无法正常显示的文章阅读了,认真地敲里面的代码。” 这话看似没有任何问题,有理有据,现实中也的确不缺乏这样的人。但放在此处也不过是暴露阁下发泄情绪的目的,因为阁下这话的前提就是站不足脚的。阁下首先就把本贴列为“胡乱转载,导致代码无法正常展示”的“这一类文章”,请问阁下,本帖的代码是否无法正常运行,是否有缺行少字?阁下这才是最荒诞的。 最后,本帖大量无关回复已经带领本帖偏离初衷,失去了讨论的意义,恕我不再进行任何答复。有异议者可以 email 我:daoye.aim#outlook.com 最最后,所谓“论坛”其核心在“论”,恳请诸位不要戾气滔天,诚挚讨论,有一说一有二说二,共建和谐。 |
32
zeiyso 2018-08-13 23:21:46 +08:00
socket.sendall( ) 和 non-blocking I/O 不能一起用的
|