21

Python で Web プロキシを作成しようとしています。目標は、次のような 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()

クエリ文字列に start=true がある場合、スキーム、netloc、および urlpath を保持するために Cookie を設定します。そうすれば、プロキシにヒットする相対リンクまたは絶対リンクは、その Cookie を使用して完全な URL を解決します。

このコードで にアクセスするhttp://localhost:8888/http://espn.com/?start=trueと、ESPN のコンテンツが表示されます。ただし、次のサイトではまったく機能しません: http://www.bottegaveneta.com/us/shop/ . 私の質問は、これを行う最善の方法は何ですか? 私がこれを堅牢に実装している現在の方法ですか、それともこの方法で行うにはいくつかのひどい落とし穴がありますか? もしそれが正しければ、私が指摘したような特定のサイトがまったく機能しないのはなぜですか?

助けてくれてありがとう。

4

5 に答える 5

8

私は最近、同様の Web アプリケーションを作成しました。これは私が行った方法であることに注意してください。このようにすべきだと言っているのではありません。これらは私が遭遇したいくつかの落とし穴です:

相対値から絶対値への属性値の変更

ページを取得してクライアントに提示するだけでなく、さらに多くの作業が必要です。エラーなしで Web ページをプロキシできないことがよくあります。

私が指摘したような特定のサイトがまったく機能しないのはなぜですか?

多くの Web ページは、適切にフォーマットされた方法で Web ページを表示するために、リソースへの相対パスに依存しています。たとえば、このイメージ タグ:

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

クライアントは次の要求を行います。

http://proxyurl/header.png

これは失敗します。' src ' 値は次のように変換する必要があります。

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

そのため、 BeautifulSoupなどで HTML ドキュメントを解析し、すべてのタグをループして、次のような属性を確認する必要があります。

'src', 'lowsrc', 'href'

タグが次のようになるように、それに応じて値を変更します。

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

この方法は、画像タグだけでなく、より多くのタグに適用されます。ascriptlinkli、および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/BE/images/top_img.gif');height:200px;width:427px;background-repeat:no-repeat;background-position:right top;" >

BeautifulSoupを使用して到達および変更するのが難しいコードの例です。

最初の例では、相対 uri への css @Import があります。2 つ目は、インライン CSS ステートメントからの' url() ' メソッドに関するものです。

私の状況では、これらの値を手動で変更する恐ろしいコードを書くことになりました。これには正規表現を使用したいかもしれませんが、よくわかりません。

リダイレクト

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/ターゲットを指定します。おそらく値を変更したいと思うでしょう。

ついにやった?

いいえ。一部の Web サイトでは、コンテンツを画面に描画するために JavaScript に大きく依存しています。これらのサイトは、プロキシが最も困難です。PhantomJSGhostなどを使用してWeb ページを取得して評価し、結果をクライアントに提示することを考えていました。

多分私のソースコードがあなたを助けることができます. 好きなように使用できます。

于 2013-11-01T15:10:50.580 に答える
0

標準ライブラリの socket モジュールを使用できます。Linux epoll を使用している場合も同様です。

ここで単純な非同期サーバーのコード例を見ることができます: https://github.com/aychedee/octopus/blob/master/octopus/server.py

于 2013-08-03T18:14:56.987 に答える
0

どうやら私はこれに答えるのがかなり遅れていますが、しばらく前に偶然見つけました。私はあなたの要件に似たものを自分で書いています。

これはどちらかというと HTTP リピーターですが、最初のタスクはプロキシ自体です。それはまだ完全ではなく、今のところそれを読むことはありませんが、それらは私の todo リストにあります。

これを達成するためにmitmproxyを使用しました。これは、最も洗練されたコードではないかもしれません。リピーター機能を実現するために、あちこちで多くのハックを使用しました。デフォルトで mitmproxy にはリピーターを簡単に実現する方法があることは知っていますが、私の場合、mitmproxy が提供するこれらの機能を使用できないという特定の要件がありました。

https://github.com/c0n71nu3/python_repeater/でプロジェクトを見つけることができます 。レポは、開発があれば、私によってまだ更新されています。

うまくいけば、それはあなたに役立つことができるでしょう。

于 2015-09-01T11:28:59.640 に答える