簡(jiǎn)介
aiohttp是Python3下的一個(gè)異步的HTTP庫,它可以作為客戶端請(qǐng)求數(shù)據(jù)也可以作為服務(wù)器使用。安裝很簡(jiǎn)單。。
pip3 install aiohttp
使用
作為客戶端的使用
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio
from aiohttp import ClientSession
url = "http://www.shuiyueyue.com"
headers = { "User-Agent": "Hi!Pretty!" }
async def darling(loop):
async with ClientSession(loop=loop) as session:
async with session.get(url, headers=headers) as resp:
print(await resp.text())
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(darling(loop))
作為服務(wù)器的使用
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio
from aiohttp import web
async def yoyoyo(request):
body = "<!DOCTYPE html><html><head><meta charset='utf-8'><title>Yo!Yo!Yo!</title></head><body><p>腦子有殼???</p></body></html>"
resp = web.Response(body=body.encode("utf-8"))
resp.content_type= "text/html; charset=utf-8"
return resp
async def init(loop):
app = web.Application(loop=loop)
app.router.add_get("/", yoyoyo)
return await loop.create_server(app.make_handler(), "", 8080)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()
代理
所謂代理,就是相當(dāng)于幫一對(duì)小情侶傳紙條的一個(gè)中間人的角色,當(dāng)小花想要將小紙條傳給小明的時(shí)候,卻發(fā)現(xiàn)中間站著個(gè)老師,這時(shí)最好的方法就是讓小花把小紙條給老師,然后讓老師把小紙條傳給小明。同理,當(dāng)一個(gè)客戶端想要通信一個(gè)連接不到的服務(wù)器的時(shí)候,簡(jiǎn)單的方法就是找一個(gè)能連接到那個(gè)服務(wù)器的機(jī)子作為代理服務(wù)器,過程一般如下:
- 客戶端發(fā)送請(qǐng)求
- 代理服務(wù)器接收來自客戶端的請(qǐng)求
- 代理服務(wù)器將客戶端的請(qǐng)求發(fā)送給服務(wù)器
- 服務(wù)器接收來自代理服務(wù)器的請(qǐng)求并響應(yīng)
- 代理服務(wù)器接收來自服務(wù)器的響應(yīng)
- 代理服務(wù)器將響應(yīng)發(fā)送回客戶端
- 客戶端接收響應(yīng)
一個(gè)簡(jiǎn)單的代理服務(wù)器所需要處理的就是四個(gè)步驟:
- 接收客戶端請(qǐng)求
- 發(fā)送客戶端請(qǐng)求至服務(wù)器
- 接收服務(wù)器響應(yīng)
- 發(fā)送服務(wù)器響應(yīng)至客戶端
實(shí)例
廢話說了一堆我終于要開始寫代碼了,首先了解一下請(qǐng)求頭和響應(yīng)頭中需要過濾的信息:
-
Content-Encoding,Accept-Encoding這兩個(gè)交給代理服務(wù)器自己去處理,省得出現(xiàn)什么壓縮錯(cuò)誤 -
Transfer-Encoding去掉分塊傳輸?shù)捻憫?yīng)頭,由代理服務(wù)器自身處理 -
Content-Length內(nèi)容字節(jié)數(shù)也交給服務(wù)器處理,否則如果修改了內(nèi)容會(huì)導(dǎo)致長(zhǎng)度不一 -
Proxy-Connection這個(gè)是使用代理服務(wù)器才會(huì)出現(xiàn)的,恩,怎么能讓人知道你在使用代理服務(wù)器呢 -
Connection替換掉Proxy-Connection并將值修改為close,否則如果使用keep-alive可能會(huì)導(dǎo)致分塊傳輸 -
Host嘗試過幾次因?yàn)闆]有去掉這個(gè)導(dǎo)致無法連接,所以讓代理服務(wù)器自己處理這個(gè)就好了
知道了要過濾內(nèi)容之后,可以開始編寫處理頭部信息的函數(shù)了:
bad_headers = ("accept-encoding", "content-encoding", "transfer-encoding", "content-length", "proxy-connection", "connection", "host")
async def fuckheaders(headers):
h = {}
for name, value in headers.items():
if name.lower() not in bad_headers:
h[name] = value
h['Connection'] = 'close'
return h
現(xiàn)在需要編寫一個(gè)接收客戶端請(qǐng)求的小型服務(wù)器,aiohttp.web通過middlewares來處理請(qǐng)求或響應(yīng)的信息,要編寫middlewares處理函數(shù)需要將處理函數(shù)封裝到一個(gè)函數(shù)中,如下:
async def factory(app, handler):
async def resp_handler(request):
# 做點(diǎn)啥...也可以不做...取決于你決定什么時(shí)候處理信息
results = await handler(request)
# 做點(diǎn)啥...也可以不做...取決于你決定什么時(shí)候處理信息
# 如果是作為最后的處理函數(shù)最后需要返回響應(yīng)數(shù)據(jù)
response = web.Response(body=results.text)
return response
return resp_handler
現(xiàn)在編寫這個(gè)小型服務(wù)器:
async def factory(app, handler):
async def fuck(request):
print("==> %s" % request.host)
method = request.method
url = str(request.url)
headers = await fuckheaders(request.headers)
data = await request.read()
return await getresp(app.loop, method, url, headers, data)
return fuck
上面的getresp函數(shù)其實(shí)就是代理服務(wù)器向服務(wù)器發(fā)送請(qǐng)求的處理函數(shù),現(xiàn)在開始編寫:
async def getresp(loop, method, url, headers, data):
async with ClientSession(loop=loop) as session:
async with session.request(method, url, headers=headers, data=data) as resp:
print("<== %s" % resp.host)
body = await resp.read()
headers = await fuckheaders(resp.headers)
response = web.Response(body=body, status=resp.status, reason=resp.reason, headers=headers)
return reponse
初始化函數(shù):
async def init(loop, port):
app = web.Application(loop=loop, middlewares=[factory])
return await loop.create_server(app.make_handler(), "", port)
完成啦,快夸夸自己!幾十行代碼就能完成一個(gè)簡(jiǎn)單的異步代理服務(wù)器,以下是完整代碼:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import asyncio
from aiohttp import web, ClientSession
bad_headers = ("accept-encoding", "content-encoding", "transfer-encoding", "content-length", "proxy-connection", "connection", "host")
async def fuckheaders(headers):
h = {}
for name, value in headers.items():
if name.lower() not in bad_headers:
h[name] = value
h['Connection'] = "close"
return h
async def factory(app, handler):
async def fuck(request):
print("==> %s" % request.host)
method = request.method
url = str(request.url)
headers = await fuckheaders(request.headers)
data = await request.read()
return await getresp(app.loop, method, url, headers, data)
return fuck
async def getresp(loop, method, url, headers={}, data={}):
async with ClientSession(loop=loop) as session:
async with session.request(method, url, headers=headers, data=data) as resp:
print("<== %s" % resp.host)
body = await resp.read()
headers = await fuckheaders(resp.headers)
response = web.Response(body=body, status=resp.status, reason=resp.reason, headers=headers)
return response
async def init(loop, port):
app = web.Application(loop=loop, middlewares=[factory])
return await loop.create_server(app.make_handler(), "", port)
def main():
if len(sys.argv) < 2:
print("Usage: %s <listen port>" % sys.argv[0])
sys.exit(1)
port = int(sys.argv[1])
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop, port))
loop.run_forever()
if __name__ == "__main__":
main()
完事兒
GitHub: https://github.com/maoyouxiao/AioProxy
Blog: http://www.shuiyueyue.com