Python程序中设置HTTP代理


Posted in Python onNovember 06, 2016

0x00 前言

大家对HTTP代理应该都非常熟悉,它在很多方面都有着极为广泛的应用。HTTP代理分为正向代理和反向代理两种,后者一般用于将防火墙后面的服务提供给用户访问或者进行负载均衡,典型的有Nginx、HAProxy等。本文所讨论的是正向代理。

HTTP代理最常见的用途是用于网络共享、网络加速和网络限制突破等。此外,HTTP代理也常用于Web应用调试、Android/IOS APP 中所调用的Web API监控和分析,目前的知名软件有Fiddler、Charles、Burp Suite和mitmproxy等。HTTP代理还可用于请求/响应内容修改,在不改变服务端的情况下为Web应用增加额外的功能或者改变应用行为等。

0x01 HTTP代理是什么

HTTP代理本质上是一个Web应用,它和其他普通Web应用没有根本区别。HTTP代理收到请求后,根据Header中Host字段的主机名和Get/POST请求地址综合判断目标主机,建立新的HTTP请求并转发请求数据,并将收到的响应数据转发给客户端。

如果请求地址是绝对地址,HTTP代理采用该地址中的Host,否则使用Header中的HOST字段。做一个简单测试,假设网络环境如下:

192.168.1.2 Web服务器
192.168.1.3 HTTP代理服务器

使用telnet进行测试

$ telnet 192.168.1.3
GET / HTTP/1.0
HOST: 192.168.1.2

注意最后需要连续两个回车,这是HTTP协议要求。完成后,可以收到 http://192.168.1.2/ 的页面内容。下面做一下调整,GET请求时带上绝对地址

$ telnet 192.168.1.3
GET http://httpbin.org/ip HTTP/1.0
HOST: 192.168.1.2

注意这里同样设置了HOST为192.168.1.2,但运行结果却返回了 http://httpbin.org/ip 页面的内容,也就是公网IP地址信息。

从上面的测试过程可以看出,HTTP代理并不是什么很复杂的东西,只要将原始请求发送到代理服务器即可。在无法设置HTTP代理的情况下,对于少量Host需要走HTTP代理的场景来说,最简单的方式就是将目标Host域名的IP指向代理服务器,可以采取修改hosts文件的方式来实现。

0x02 Python程序中设置HTTP代理

urllib2/urllib 代理设置

urllib2是Python标准库,功能很强大,只是使用起来稍微麻烦一点。在Python 3中,urllib2不再保留,迁移到了urllib模块中。urllib2中通过ProxyHandler来设置使用代理服务器。

proxy_handler = urllib2.ProxyHandler({'http': '121.193.143.249:80'})
opener = urllib2.build_opener(proxy_handler)
r = opener.open('http://httpbin.org/ip')
print(r.read())

也可以用install_opener将配置好的opener安装到全局环境中,这样所有的urllib2.urlopen都会自动使用代理。

urllib2.install_opener(opener)
r = urllib2.urlopen('http://httpbin.org/ip')
print(r.read())

在Python 3中,使用urllib。

proxy_handler = urllib.request.ProxyHandler({'http': 'http://121.193.143.249:80/'})
opener = urllib.request.build_opener(proxy_handler)
r = opener.open('http://httpbin.org/ip')
print(r.read())

requests 代理设置

requests是目前最优秀的HTTP库之一,也是我平时构造http请求时使用最多的库。它的API设计非常人性化,使用起来很容易上手。给requests设置代理很简单,只需要给proxies设置一个形如 {'http': 'x.x.x.x:8080', 'https': 'x.x.x.x:8080'} 的参数即可。其中http和https相互独立。

In [5]: requests.get('http://httpbin.org/ip', proxies={'http': '121.193.143.249:80'}).json()
Out[5]: {'origin': '121.193.143.249'}

可以直接设置session的proxies属性,省去每次请求都要带上proxies参数的麻烦。

s = requests.session()
s.proxies = {'http': '121.193.143.249:80'}
print(s.get('http://httpbin.org/ip').json())

0x03 HTTP_PROXY / HTTPS_PROXY 环境变量

urllib2 和 Requests 库都能识别 HTTP_PROXY 和 HTTPS_PROXY 环境变量,一旦检测到这些环境变量就会自动设置使用代理。这在用HTTP代理进行调试的时候非常有用,因为不用修改代码,可以随意根据环境变量来调整代理服务器的ip地址和端口。*nix中的大部分软件也都支持HTTP_PROXY环境变量识别,比如curl、wget、axel、aria2c等。

$ http_proxy=121.193.143.249:80 python -c 'import requests; print(requests.get("http://httpbin.org/ip").json())'
{u'origin': u'121.193.143.249'}

$ http_proxy=121.193.143.249:80 curl httpbin.org/ip
{
 "origin": "121.193.143.249"
}

在IPython交互环境中,可能经常需要临时性地调试HTTP请求,可以简单通过设置 os.environ['http_proxy'] 增加/取消HTTP代理来实现。

In [245]: os.environ['http_proxy'] = '121.193.143.249:80'
In [246]: requests.get("http://httpbin.org/ip").json()
Out[246]: {u'origin': u'121.193.143.249'}
In [249]: os.environ['http_proxy'] = ''
In [250]: requests.get("http://httpbin.org/ip").json()
Out[250]: {u'origin': u'x.x.x.x'}

0x04 MITM-Proxy

MITM 源于 Man-in-the-Middle Attack,指中间人攻击,一般在客户端和服务器之间的网络中拦截、监听和篡改数据。

mitmproxy 是一款Python语言开发的开源中间人代理神器,支持SSL,支持透明代理、反向代理,支持流量录制回放,支持自定义脚本等。功能上同Windows中的 Fiddler 有些类似,但mitmproxy是一款console程序,没有GUI界面,不过用起来还算方便。使用mitmproxy可以很方便的过滤、拦截、修改任意经过代理的HTTP请求/响应数据包,甚至可以利用它的scripting API,编写脚本达到自动拦截修改HTTP数据的目的。

# test.py
def response(flow):
  flow.response.headers["BOOM"] = "boom!boom!boom!"

上面的脚本会在所有经过代理的Http响应包头里面加上一个名为BOOM的header。用 mitmproxy -s 'test.py' 命令启动mitmproxy,curl验证结果发现的确多了一个BOOM头。

$ http_proxy=localhost:8080 curl -I 'httpbin.org/get'
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 03 Nov 2016 09:02:04 GMT
Content-Type: application/json
Content-Length: 186
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
BOOM: boom!boom!boom!
...

显然mitmproxy脚本能做的事情远不止这些,结合Python强大的功能,可以衍生出很多应用途径。除此之外,mitmproxy还提供了强大的API,在这些API的基础上,完全可以自己定制一个实现了特殊功能的专属代理服务器。

经过性能测试,发现mitmproxy的效率并不是特别高。如果只是用于调试目的那还好,但如果要用到生产环境,有大量并发请求通过代理的时候,性能还是稍微差点。我用twisted实现了一个简单的proxy,用于给公司内部网站增加功能、改善用户体验,以后有机会再和大家分享。

Python 相关文章推荐
解决pyqt中ui编译成窗体.py中文乱码的问题
Dec 23 Python
python实现括号匹配的思路详解
Aug 23 Python
Python调用C++,通过Pybind11制作Python接口
Oct 16 Python
详解Django-auth-ldap 配置方法
Dec 10 Python
python 文本单词提取和词频统计的实例
Dec 22 Python
Centos部署django服务nginx+uwsgi的方法
Jan 02 Python
python爬虫 爬取58同城上所有城市的租房信息详解
Jul 30 Python
tensorflow 只恢复部分模型参数的实例
Jan 06 Python
Python实现点云投影到平面显示
Jan 18 Python
tensorflow ckpt模型和pb模型获取节点名称,及ckpt转pb模型实例
Jan 21 Python
Python使用QQ邮箱发送邮件实例与QQ邮箱设置详解
Feb 18 Python
教你如何使用Python开发一个钉钉群应答机器人
Jun 21 Python
Python 搭建Web站点之Web服务器网关接口
Nov 06 #Python
Python 搭建Web站点之Web服务器与Web框架
Nov 06 #Python
读写json中文ASCII乱码问题的解决方法
Nov 05 #Python
django1.8使用表单上传文件的实现方法
Nov 04 #Python
Python+MongoDB自增键值的简单实现
Nov 04 #Python
基于Python的接口测试框架实例
Nov 04 #Python
浅谈Python爬取网页的编码处理
Nov 04 #Python
You might like
根德YB400的电路分析
2021/03/02 无线电
用PHP为SHOPEX增加日志功能代码
2010/07/02 PHP
js利用数组length属性清空和截短数组的小例子
2014/01/15 Javascript
JavaScript通过select动态更换图片的方法
2015/03/23 Javascript
jQuery实现的动态伸缩导航菜单实例
2015/05/07 Javascript
jQuery实现的精美平滑二级下拉菜单效果代码
2016/03/28 Javascript
jQuery animate和CSS3相结合实现缓动追逐效果附源码下载
2016/04/18 Javascript
Vue.JS入门教程之列表渲染
2016/12/01 Javascript
angular 基于ng-messages的表单验证实例
2017/05/04 Javascript
react-router实现跳转传值的方法示例
2017/05/27 Javascript
JavaScript实现获取用户单击body中所有A标签内容的方法
2017/06/05 Javascript
jquery之基本选择器practice(实例讲解)
2017/09/30 jQuery
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
2017/11/14 jQuery
vue.js分页中单击页码更换页面内容的方法(配合spring springmvc)
2018/02/10 Javascript
详解vuex结合localstorage动态监听storage的变化
2018/05/03 Javascript
[00:32]2016完美“圣”典风云人物:Maybe宣传片
2016/12/05 DOTA
[01:13:59]LGD vs Mineski Supermajor 胜者组 BO3 第三场 6.5
2018/06/06 DOTA
Python中datetime模块参考手册
2017/01/13 Python
对pandas的dataframe绘图并保存的实现方法
2017/08/05 Python
python放大图片和画方格实现算法
2018/03/30 Python
一些Centos Python 生产环境的部署命令(推荐)
2018/05/07 Python
8段用于数据清洗Python代码(小结)
2019/10/31 Python
aws 通过boto3 python脚本打pach的实现方法
2020/05/10 Python
python动态规划算法实例详解
2020/11/22 Python
python中doctest库实例用法
2020/12/31 Python
爱尔兰电脑、家电和家具购物网站:Buy It Direct
2019/07/09 全球购物
英国哈罗德园艺:Harrod Horticultural
2020/03/31 全球购物
如何提高MySql的安全性
2014/06/19 面试题
如何撰写岗位职责
2014/02/01 职场文书
终止劳动合同证明书样本
2014/11/19 职场文书
销售经理岗位职责
2015/01/31 职场文书
党校培训学习心得体会
2016/01/06 职场文书
职场新人知识:如何制定一份合理的工作计划?
2019/09/11 职场文书
Go 通过结构struct实现接口interface的问题
2021/10/05 Golang
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
Python实现提取PDF简历信息并存入Excel
2022/04/02 Python