Как написать веб-прокси в Python

Я пытаюсь написать веб-прокси в python. Цель состоит в том, чтобы посетить URL-адрес, например: http://proxyurl/http://anothersite.com/ и посмотреть его содержимое http://anothersite.com как обычно. Я получил прилично далеко, злоупотребляя библиотекой запросов, но на самом деле это не намеренное использование структуры запросов. Я уже писал прокси с скрученными , но я не уверен, как связать это с тем, что я пытаюсь сделать. Вот где я нахожусь …

 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() 

Просто примечание, я установил cookie для сохранения схемы, netloc и urlpath, если в querystring есть начало = true. Таким образом, любая относительная или абсолютная ссылка, которая затем попадает в прокси-сервер, использует этот файл cookie для разрешения полного URL-адреса.

С помощью этого кода, если вы перейдете к http://localhost:8888/http://espn.com/?start=true вы увидите содержимое ESPN. Однако на следующем сайте он вообще не работает: http://www.bottegaveneta.com/us/shop/ . Мой вопрос: какой лучший способ сделать это? Является ли текущий способ, которым я реализую эту уверенность, или есть какие-то ужасные подводные камни для этого? Если это правильно, почему некоторые сайты, как я указал, не работают вообще?

Спасибо за любую помощь.

6 Solutions collect form web for “Как написать веб-прокси в Python”

Недавно я написал аналогичное веб-приложение. Заметьте, что так я и сделал. Я не говорю, что вы должны это делать так. Это некоторые подводные камни, с которыми я столкнулся:

Изменение значений атрибутов относительно абсолютного

Существует гораздо более активное участие, чем просто выбор страницы и представление ее клиенту. Много раз вы не можете проксировать веб-страницу без каких-либо ошибок.

Почему некоторые сайты, как я указал, не работают вообще?

Многие веб-страницы полагаются на относительные пути к ресурсам, чтобы отображать веб-страницу в хорошо отформатированном виде. Например, этот тег изображения:

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

В результате клиент выполнит запрос:

 http://proxyurl/header.png 

Что не удается. Значение ' src ' должно быть преобразовано в:

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

Итак, вам нужно проанализировать HTML-документ с помощью чего-то вроде BeautifulSoup , перебрать все теги и проверить атрибуты, такие как:

 'src', 'lowsrc', 'href' 

И измените их значения так, чтобы тег стал:

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

Этот метод применяется для большего количества тегов, чем только для одного изображения. a , script , link , li и frame – это несколько, которые вы также должны изменить.

Шенафиганы HTML

Предыдущий метод должен дать вам далеко, но вы еще не закончили.

И то и другое

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

А также

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

являются примерами кода, который трудно достичь и изменить с помощью BeautifulSoup .

В первом примере есть css @Import для относительного uri. Второй относится к методу « url () » из встроенного оператора CSS.

В моей ситуации я закончил писать ужасный код, чтобы вручную изменить эти значения. Вы можете использовать Regex для этого, но я не уверен.

Перенаправление

С помощью Python-Requests или Urllib2 вы можете легко выполнять переадресацию автоматически. Просто не забудьте сохранить новый (базовый) uri; вам понадобится это для «изменения значений атрибутов относительно абсолютной».

Вам также нужно иметь дело с «жестко закодированными» переадресациями. Например, такой:

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

Необходимо изменить на:

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

Базовый тег

Базовый тег указывает базовый URL / цель для всех относительных URL-адресов в документе. Вероятно, вы хотите изменить значение.

Наконец, сделали?

Неа. Некоторые веб-сайты в значительной степени полагаются на javascript, чтобы отображать их содержимое на экране. Эти сайты наиболее сложны для прокси. Я думал об использовании чего-то вроде PhantomJS или Ghost для извлечения и оценки веб-страниц и представления результата клиенту.

Возможно, мой исходный код может вам помочь. Вы можете использовать его любым способом.

Если вы хотите создать реальный прокси-сервер, вы можете использовать:

Торнадо-прокси

или

простой прокси-сервер на основе Twisted

Но я думаю, что не сложно будет их адаптировать для вашего дела.

Я думаю, что вам не нужен ваш последний блок. Кажется, это работает для меня:

 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) 

Вы можете использовать модуль сокета в стандартной библиотеке, и если вы используете Linux epoll.

Вы можете увидеть пример кода простого асинхронного сервера здесь: https://github.com/aychedee/octopus/blob/master/octopus/server.py

Видимо, я довольно поздно отвечаю на это, но наткнулся на него некоторое время назад. Я сам пишу что-то похожее на ваши требования.

Это скорее HTTP-повторитель, но первая из его задач – это сам прокси. Он еще не полностью завершен, и на данный момент я не читал его, но это список моих дел.

Я использовал mitmproxy для достижения этого. Возможно, это не самый элегантный фрагмент кода, и я использовал множество хаков здесь и там, чтобы добиться функциональности ретранслятора. Я знаю, что у mitmproxy по умолчанию есть способы легко получить репитер, но в моем случае было какое-то определенное требование, когда я не мог использовать те функции, которые предлагал mitmproxy.

Проект можно найти на странице https://github.com/c0n71nu3/python_repeater/ . Репо все еще обновляется мной, когда и когда происходят какие-либо события.

Надеюсь, он сможет вам помочь.

вы можете запросить модуль пользователя.

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

запрос документов

Python - лучший язык программирования в мире.