私は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 の内容が表示されます。ただし、次のサイトではまったく機能しません。ボッテガヴェネタ私の質問は、これを行う最善の方法は何ですか? 現在私がこれを実装している方法は堅牢ですか、それともこの方法には恐ろしい落とし穴がありますか? それが正しい場合、私が指摘したような特定のサイトがまったく機能しないのはなぜですか?
ご協力いただければ幸いです。
ベストアンサー1
最近、似たような Web アプリケーションを作成しました。これは私が作成した方法であることに注意してください。皆さんもこのように作成すべきだと言っているのではありません。私が遭遇した落とし穴をいくつか紹介します。
属性値を相対値から絶対値に変更する
ページを取得してクライアントに表示するだけではありません。多くの場合、エラーなしで Web ページをプロキシすることはできません。
私が指摘したような特定のサイトがまったく機能しないのはなぜですか?
多くの Web ページでは、Web ページを適切にフォーマットして表示するために、リソースへの相対パスに依存しています。たとえば、次の画像タグ:
<img src="/header.png" />
クライアントは次のようなリクエストを実行します:
http://proxyurl/header.png
失敗します。ソース' 値は次のように変換されます:
http://anothersite.com/header.png.
したがって、HTML文書を次のように解析する必要があります。美しいスープ、ループオーバーすべてのタグ次のような属性を確認します。
'src', 'lowsrc', 'href'
そして価値観を変えるしたがって、タグは次のようになります。
<img src="http://anothersite.com/header.png" />
この方法は、画像タグだけでなく、他のタグにも適用されます。1つの、脚本、リンク、李そしてフレーム変更する必要があるものもいくつかあります。
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;" >
使用してアクセスしたり変更したりすることが難しいコードの例です美しいスープ。
最初の例では、相対URIへのCSS @Importがあります。2番目の例は、「url()' メソッドをインライン CSS ステートメントから使用します。
私の場合、これらの値を手動で変更するためにひどいコードを書くことになりました。これには正規表現を使用する必要があるかもしれませんが、よくわかりません。
リダイレクト
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に大きく依存しています。これらのサイトはプロキシするのが最も困難です。次のようなものを使用することを検討しています。ファントムJSまたはおばけWeb ページを取得して評価し、その結果をクライアントに提示します。
たぶん私のソースコードあなたを助けることができます。好きなように使うことができます。