python proxy tornado python-requests

Cómo escribir un proxy web en Python



tornado python-requests (6)

Aparentemente llegué bastante tarde respondiendo esto, pero lo encontré hace un tiempo. He estado escribiendo algo similar a tus requisitos yo mismo.

Es más un repetidor HTTP, pero la primera de sus tareas es el proxy en sí. Todavía no está totalmente completo y no hay ninguna lectura para mí por el momento, pero están en mi lista de tareas pendientes.

He usado mitmproxy para lograr esto. Puede que no sea la pieza de código más elegante que existe y he usado muchos hacks aquí y allá para lograr la funcionalidad del repetidor. Sé que mitmproxy de forma predeterminada tiene formas de lograr el efecto repetidor fácilmente, pero había ciertos requisitos en mi caso en los que no podía usar las funciones que ofrece mitmproxy.

Puede encontrar el proyecto en https://github.com/c0n71nu3/python_repeater/ El repositorio todavía está siendo actualizado por mí a medida que haya algún desarrollo.

Con suerte, podría servirle algo de ayuda.

Estoy tratando de escribir un proxy web en python. El objetivo es visitar una url como: http://proxyurl/http://anothersite.com/ y ver los contenidos de http://anothersite.com como lo haría normalmente. He llegado bastante lejos al abusar de la biblioteca de solicitudes, pero este no es realmente el uso previsto del marco de solicitudes. He escrito proxies con twisted anteriormente, pero no estoy seguro de cómo conectar esto con lo que estoy tratando de hacer. Aquí es donde estoy hasta ahora ...

import os import urlparse import requests import tornado.ioloop import tornado.web from tornado import template ROOT = os.path.dirname(os.path.abspath(__file__)) path = lambda *a: os.path.join(ROOT, *a) loader = template.Loader(path(ROOT, ''templates'')) class ProxyHandler(tornado.web.RequestHandler): def get(self, slug): if slug.startswith("http://") or slug.startswith("https://"): if self.get_argument("start", None) == "true": parsed = urlparse.urlparse(slug) self.set_cookie("scheme", value=parsed.scheme) self.set_cookie("netloc", value=parsed.netloc) self.set_cookie("urlpath", value=parsed.path) #external resource else: response = requests.get(slug) headers = response.headers if ''content-type'' in headers: self.set_header(''Content-type'', headers[''content-type'']) if ''length'' in headers: self.set_header(''length'', headers[''length'']) for block in response.iter_content(1024): self.write(block) self.finish() return else: #absolute if slug.startswith(''/''): slug = "{scheme}://{netloc}{original_slug}".format( scheme=self.get_cookie(''scheme''), netloc=self.get_cookie(''netloc''), original_slug=slug, ) #relative else: slug = "{scheme}://{netloc}{path}{original_slug}".format( scheme=self.get_cookie(''scheme''), netloc=self.get_cookie(''netloc''), path=self.get_cookie(''urlpath''), original_slug=slug, ) response = requests.get(slug) #get the headers headers = response.headers #get doctype doctype = None if ''<!doctype'' in response.content.lower()[:9]: doctype = response.content[:response.content.find(''>'')+1] if ''content-type'' in headers: self.set_header(''Content-type'', headers[''content-type'']) if ''length'' in headers: self.set_header(''length'', headers[''length'']) self.write(response.content) application = tornado.web.Application([ (r"/(.+)", ProxyHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()

Solo una nota, configuré una cookie para preservar el esquema, netloc y urlpath si there''s start = true en la cadena de consulta. De esta forma, cualquier enlace relativo o absoluto que luego llegue al proxy usa esa cookie para resolver la url completa.

Con este código, si va a http://localhost:8888/http://espn.com/?start=true , verá los contenidos de ESPN. Sin embargo, en el siguiente sitio no funciona en absoluto: http://www.bottegaveneta.com/us/shop/ . Mi pregunta es, ¿cuál es la mejor manera de hacer esto? ¿Es la forma actual en que estoy implementando esta robustez o hay algunas trampas terribles para hacerlo de esta manera? Si es correcto, ¿por qué ciertos sitios como el que señalé no funcionan en absoluto?

Gracias por cualquier ayuda.


Creo que no necesitas tu último bloque si. Esto parece funcionar para mí:

class ProxyHandler(tornado.web.RequestHandler): def get(self, slug): print ''get: '' + str(slug) if slug.startswith("http://") or slug.startswith("https://"): if self.get_argument("start", None) == "true": parsed = urlparse.urlparse(slug) self.set_cookie("scheme", value=parsed.scheme) self.set_cookie("netloc", value=parsed.netloc) self.set_cookie("urlpath", value=parsed.path) #external resource else: response = requests.get(slug) headers = response.headers if ''content-type'' in headers: self.set_header(''Content-type'', headers[''content-type'']) if ''length'' in headers: self.set_header(''length'', headers[''length'']) for block in response.iter_content(1024): self.write(block) self.finish() return else: slug = "{scheme}://{netloc}/{original_slug}".format( scheme=self.get_cookie(''scheme''), netloc=self.get_cookie(''netloc''), original_slug=slug, ) print self.get_cookie(''scheme'') print self.get_cookie(''netloc'') print self.get_cookie(''urlpath'') print slug response = requests.get(slug) #get the headers headers = response.headers #get doctype doctype = None if ''<!doctype'' in response.content.lower()[:9]: doctype = response.content[:response.content.find(''>'')+1] if ''content-type'' in headers: self.set_header(''Content-type'', headers[''content-type'']) if ''length'' in headers: self.set_header(''length'', headers[''length'']) self.write(response.content)



Recientemente escribí una aplicación web similar. Tenga en cuenta que esta es la forma en que lo hice. No digo que debas hacerlo así. Estas son algunas de las trampas que encontré:

Cambiar los valores de atributo de relativo a absoluto

Hay mucho más en juego que simplemente ir a buscar una página y presentarla al cliente. Muchas veces no puede proxy la página web sin ningún error.

¿Por qué ciertos sitios como el que señalé no funcionan en absoluto?

Muchas páginas web dependen de rutas de acceso relativas a los recursos para mostrar la página web de forma correcta. Por ejemplo, esta etiqueta de imagen:

<img src="/header.png" />

El resultado será que el cliente haga una solicitud para:

http://proxyurl/header.png

Lo cual falla El valor '' src '' debe convertirse a:

http://anothersite.com/header.png.

Por lo tanto, debe analizar el documento HTML con algo como BeautifulSoup , recorrer todas las etiquetas y verificar atributos como:

''src'', ''lowsrc'', ''href''

Y cambie sus valores en consecuencia para que la etiqueta se convierta en:

<img src="http://anothersite.com/header.png" />

Este método se aplica a más etiquetas que solo a la imagen. a , script , link , li y frame son algunos de los que debería cambiar también.

Travesuras HTML

El método anterior debería llevarlo lejos, pero aún no ha terminado.

Ambos

<style type="text/css" media="all">@import "/stylesheet.css?version=120215094129002";</style>

Y

<div style="position:absolute;right:8px;background-image:url(''/Portals/_default/Skins/BE/images/top_img.gif'');height:200px;width:427px;background-repeat:no-repeat;background-position:right top;" >

son ejemplos de código que es difícil de alcanzar y modificar con BeautifulSoup .

En el primer ejemplo, hay un css @Import para un uri relativo. El segundo se refiere al método '' url () '' de una declaración CSS en línea.

En mi situación, terminé escribiendo un código horrible para modificar estos valores manualmente. Es posible que desee utilizar Regex para esto, pero no estoy seguro.

Redirige

Con Python-Requests o Urllib2 puede seguir fácilmente las redirecciones automáticamente. Solo recuerde guardar lo que es el nuevo (base) uri; lo necesitará para la operación ''cambiar los valores de los atributos de relativa a absoluta''.

También necesita lidiar con redirecciones ''codificadas''. Tal como este:

<meta http-equiv="refresh" content="0;url=http://new-website.com/">

Necesita ser cambiado a:

<meta http-equiv="refresh" content="0;url=http://proxyurl/http://new-website.com/">

Etiqueta de base

La etiqueta base especifica la URL / destino base para todas las URL relativas en un documento. Probablemente quieras cambiar el valor.

¿Finalmente hecho?

Nop. Algunos sitios web dependen en gran medida de javascript para dibujar su contenido en la pantalla. Estos sitios son los más difíciles de usar. He estado pensando en usar algo como PhantomJS o Ghost para buscar y evaluar páginas web y presentar el resultado al cliente.

Tal vez mi código fuente pueda ayudarte. Puedes usarlo de la forma que quieras.



puedes usar el módulo de solicitudes.

import requests proxies = { "http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080", } requests.get("http://example.org", proxies=proxies)

solicitar documentos