TorをPythonスクレイピングに流用しやすくするためのモジュールを作成しました。Torをスクレイピングに流用することによって、IPによる制限を回避することが容易になります。
PythonのスクレイピングでTorを利用する方法はこちらの記事を参考にしてください。
複数のIPアドレスを同時に使い分けたり、reCaptchaが出現した時のリトライ、匿名性の維持などにも役立つため、是非導入していただければと思います。
ソースはnoteで公開しています。
https://note.mu/katsuwo315/n/nc75c0772c27a
事前準備:Torエキスパートバンドル版のインストール
Tor本体のダウンロード
Torのエキスパートバンドル版を公式サイトからダウンロード
リンクはwin32版のエキスパートバンドルへの直リンクです。
ダウンロードしたTor本体を解凍
解凍した状態でのフォルダの構造はこんな感じです。
Tor ├─ libeay32.dll ├─ libevent-2-1-6.dll ├─ libevent_core-2-1-6.dll ├─ libevent_extra-2-1-6.dll ├─ libgcc_s_sjlj-1.dll ├─ libssp-0.dll ├─ libwinpthread-1.dll ├─ ssleay32.dll ├─ tor-gencert.exe ├─ tor.exe └─ zlib1.dll
tor.exeがTorの本体です。
解凍して、任意のディレクトリTorフォルダを設置してください。
こだわりがなければ以下の例に沿って設置していただければ問題は起きにくいと思います。
TorのディレクトリにPathを通す
例えば以下のフォルダに上で解凍したTorフォルダを設置したとします。
C: └─ProgramData └─Tor ├─ libeay32.dll ├─ libevent-2-1-6.dll ├─ libevent_core-2-1-6.dll ├─ libevent_extra-2-1-6.dll ├─ libgcc_s_sjlj-1.dll ├─ libssp-0.dll ├─ libwinpthread-1.dll ├─ ssleay32.dll ├─ tor-gencert.exe ├─ tor.exe └─ zlib1.dll
すると、Pathを通すディレクトリは以下のようになります。
C:ProgramDataTor
システム環境変数を検索して開きます
システム環境変数を選択
Pathを選択
新規を選択して出てきた項目に先ほどのPathをコピペ
C:ProgramDataTor
これを追加してOKを押します。
Pathが通ったか確認
コマンドプロンプトを立ち上げる
どのディレクトリでもいいので”tor”と入力する
プログラムが立ち上がればOK
‘tor’ は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
このメッセージが出る場合は設定が間違っていますので、確認してください。
Bootstrapped 100% (done)というメッセージまで進むようであれば、前提のTor本体の設定は完了です。
モジュールの依存関係のライブラリをインストールする
標準モジュール以外で依存関係にあるライブラリは以下です。
全てpipでインストール可能です。
pip install stem pip install attrdict
このTor操作モジュールでできる事と基本操作
TorをPythonから扱いやすくするためのモジュールです。
Python3系、Windowsを前提として作成しました。
ここでは、基本的な操作方法とできる事を紹介します。
基本的な使い方:インスタンスの生成
※インポートするにはモジュールのファイルをパスに配置する必要があります。
# インポート from tor_control import TOR # インスタンスの生成 tor = TOR()
モジュール、Tor本体共にPathが通っている場合はこれでインスタンスが作成されます。
そうでない場合は、インスタンス生成時に各種Pathを引き渡す必要があります。
設定用のtorrcファイルをがある場合も、インスタンスの生成時に指定できます。
tor_path = "C:ProgramDataTor" rc_path = "C:UsersOwnerAppDataRoamingtortorrc" tor = TOR(tor_path=tor_path, rc_path=rc_path)
torrcを設定しない場合はディフォルトで起動されますので、なくてもインスタンスの生成は可能です。
Torを起動させるコマンド
生成したインスタンスに対して、色々なコマンドを出すことが出来ます。
モジュールにはいくつかのTorを起動するためのコマンドがあります。
少しずつ役割が違いますので、紹介します。
runメソッド:通常起動
tor.run() Tor starting port: 9050 Out:0
runメソッドは通常起動用のメソッドです。
起動されたポート番号が標準出力されます。
0がreturnされてきている場合は、起動成功を意味しています。
何らかのエラーで起動のコマンドが通らなかった場合は0以外の数字が返ってきます。
runメソッド:ポートを指定しての起動
runメソッドに引数を渡さない場合は、通常のTor起動と同じ9050番のポートを開放して起動します。
tor.run(9060) Tor starting port: 9060 Out:0
int(整数型)の数値を引数としてrunメソッドに渡すとそのポート番号でTorが起動します。
multiメソッド:複数のTorを起動
複数のTorを起動させ、同時に複数のIPアドレスを利用してインターネットにアクセスすることが出来ます。もちろん、スクレイピングなどにも利用できます。
tor.multi() Out: Tor starting port: 9070 Tor starting port: 9080 Tor starting port: 9090
何も引数に指定しなければ3つのTorを起動させます。
利用するportは極力、衝突を回避するように設計しています。
tor.multi(5) Out: Tor starting port: 9050 Tor starting port: 9060 Tor starting port: 9070 Tor starting port: 9080 Tor starting port: 9090
整数を引数にして渡すと、その数だけTorを起動させます。
Torを終了させるコマンド
Torを終了させるコマンドは二つあります。
killメソッド:portを指定して終了させる
portを整数で指定して、Torを終了させます。killした結果、残っているportをリスト型で返却します。
tor.kill(9160) Out:[9050, 9060, 9070, 9080, 9090, 9100, 9110, 9120, 9130, 9140, 9150]
force_killメソッド:複数のTorをリストで指定して終了させる
tor.force_kill([9120, 9130, 9140, 9150])
返却はありません。
force_killメソッド: 全てのTorを終了させる
引数を渡さないと、全てのTorを終了させます。
tor.force_kill()
プロキシの情報を返却するコマンド
現状起動しているTorの情報を返却するメソッドと、アトリビュートを紹介します。
socksメソッド:socksポートを指定するための文字列を返却
他のライブラリでsocks形式でプロキシを指定するための文字列を返却するメソッドです。
tor.socks() Out:'socks5://127.0.0.1:9050'
引数を渡さないと、現状起動されているTorが使っているportの中から最小のものを返却します。
socksメソッド:requestsにプロキシを指定する
socksメソッドを利用して、このような形でrequestsにプロキシの指定をすることが可能です。
import requests # proxies変数を作成 proxies = { 'https': tor.socks() } url = "https://ipinfo.io" # getメソッドproxies引数に渡す requests.get(url, proxies=proxies).json() Out:{'ip': '23.129.64.192', 'hostname': '192.emeraldonion.org', 'city': 'Seattle', 'region': 'Washington', 'country': 'US', 'loc': '47.6036,-122.3256', 'org': 'AS396507 Emerald Onion', 'postal': '98104', 'timezone': 'America/Los_Angeles', 'readme': 'https://ipinfo.io/missingauth'}
socksメソッド:selenium(Chrome)にプロキシを指定する
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() # optionsで指定する options.add_argument(f'--proxy-server={tor.socks()}') # 指定したoptionsでchromeを起動 driver = webdriver.Chrome(options=options) driver.get('https://ipinfo.io')
seleniumでアクセスした結果がこちら
socksメソッド:利用するsocksポートを指定
利用するsocksを整数型で指定することが可能です。
起動されているTorがそのportを開放していない場合は起動を伴って文字列を返却します。
# 指定したportでTorが起動されていなければ、そのportでTorを起動します。 # 更に、socksプロキシ指定用の文字列を返却します。 tor.socks(9060) Out:'socks5://127.0.0.1:9060'
socksメソッド:requestsのproxies引数に直接渡せる形で返却する
socksメソッドのfor_req引数にTrueを渡すと、requestsのproxies引数に直接渡せるdict型のデータを返却します。
# for_req引数にTrueを渡す tor.socks(for_req=True) # proxies引数に直接渡せる形の辞書データが返ってくる。 Out:{'http': 'socks5://127.0.0.1:9050', 'https': 'socks5://127.0.0.1:9050'} # 実際に直接引き渡してみる url = "https://ipinfo.io" requests.get(url, proxies=tor.socks(for_req=True)).json() Out:{'ip': '173.244.209.5', 'hostname': 'slc-exit.privateinternetaccess.com', 'city': 'New York', 'region': 'New York', 'country': 'US', 'loc': '40.7145,-74.0029', 'org': 'AS29854 WestHost, Inc.', 'postal': '10118', 'timezone': 'America/New_York', 'readme': 'https://ipinfo.io/missingauth'}
socksメソッド: portをランダムで返却する
Torを複数起動している場合、rand引数にTrueを渡すと、現在オープンしているportの中からランダムに選択した文字列を返却します。
これは複数のIPを入れ替わり立ち代わり、ランダムに利用できることを意味します。for_req引数との組み合わせでも動作します。
# 呼び出される度に起動中のTorが使っているportをランダムに取得して返却します tor.socks(rand=True, for_req=True) Out:{'http': 'socks5://127.0.0.1:9060', 'https': 'socks5://127.0.0.1:9060'} tor.socks(rand=True, for_req=True) Out:{'http': 'socks5://127.0.0.1:9070', 'https': 'socks5://127.0.0.1:9070'} tor.socks(rand=True, for_req=True) Out:{'http': 'socks5://127.0.0.1:9050', 'https': 'socks5://127.0.0.1:9050'}
実用的に使うと、このようなコードになります。
# 10回アクセスしてIPを調べる。 # ipはtor.socks(rand=True, for_req=True)で毎回ランダムに取得される # multiメソッドなどで複数のTorが立ち上がっていることが条件です。 url = "https://ipinfo.io" for i in range(10): res = requests.get(url, proxies=tor.socks(rand=True, for_req=True)) print(res.json()['ip']) Out:199.249.230.122 Out:199.249.230.66 Out:199.249.230.75 Out:162.247.74.27 Out:64.113.32.29 Out:162.247.74.201 Out:162.247.74.27 Out:173.244.209.5 Out:199.249.230.66 Out:162.247.74.201
繰り返しアクセスしても、ランダムに別のTorサーキットを使うのでIPが毎回変わっています。
reqメソッド:requestsのproxies引数に直接渡せる形で返却する
socksメソッドにfor_req=Trueを渡した時と同じ動作をします。基本的には短縮形です。
tor.req() Out:{'http': 'socks5://127.0.0.1:9050', 'https': 'socks5://127.0.0.1:9050'}
portを指定することも可能。
tor.req(9080) Out:{'http': 'socks5://127.0.0.1:9080', 'https': 'socks5://127.0.0.1:9080'}
rand引数も使えます。
tor.req(rand=True) Out:{'http': 'socks5://127.0.0.1:9110', 'https': 'socks5://127.0.0.1:9110'}
少しsocksメソッドと違うのは、reqメソッドでportを指定した場合、ディフォルトでは、そのportが実際にTorが使っているかの確認は取りません。
つまり、実際に使われていないportを指定しても結果を返します。
tor.req(999999) Out:{'http': 'socks5://127.0.0.1:999999', 'https': 'socks5://127.0.0.1:999999'} # そのportの起動確認を取りたい場合、run引数を使います。 tor.req(10000, run=True) Tor starting port: 10000 Out:{'http': 'socks5://127.0.0.1:10000', 'https': 'socks5://127.0.0.1:10000'}
Torの起動状況を確認するコマンド
Torの起動状況を確認するためのアトリビュートを紹介します。
Torが利用しているportの確認
現在起動しているTorが使っているportの中で最小のport番号を返却します。
tor.port Out: 9050
Torが利用している全てのportの確認
tor.ports Out: [9050, 9060, 9070, 9080, 9090, 9100, 9110, 9120, 9130]
Torが利用しているtorrcのファイルpathの確認
tor.torrc Out: WindowsPath('C:/Users/Owner/AppData/Roaming/tor/torrc') type(tor.torrc) Out: pathlib.WindowsPath str(tor.torrc) Out:'C:UsersOwnerAppDataRoamingtortorrc'
Pathはpathlibのオブジェクト形式で返却されます。
Torのconfig関係のコマンド
Torのconfigをこのモジュールを通して設定することも可能です。
各設定項目はこちらで確認できます。
setアトリビュート:現在のtorrcの設定を参照
インスタンス生成時に、torrcファイルにある設定を自動的に読み込みます。
torrcで設定した項目に追加して、よく使いそうな項目をディフォルトでセットしています。以下のコマンドで設定を確認できます。
tor.set Out: AttrDict({'CircuitBuildTimeout': '', 'NewCircuitPeriod': '', 'MaxCircuitDirtiness': '30', 'NumEntryGuards': '100', 'ExcludeExitNodes': '200.9.255.32', 'ExcludeNodes': 'SlowServer,200.9.255.32', 'EntryNodes': '', 'ExitNodes': '', 'StrictNodes': '1'})
tor起動時はこの設定を元に起動されます。
setアトリビュート:設定項目を追加/書き換える
tor.setに設定項目名をアトリビュートで続けて参照/代入できます。
設定を書き換えると、新たにTorを起動するコマンドを使った時にその設定を使って起動します。
tor.set.MaxCircuitDirtiness = 60 tor.set.MaxCircuitDirtiness Out: 60
ディフォルトで表示されない項目名でもここで追加設定することが可能です。
アトリビュートで項目名にアクセスして、直接代入します。
項目名が間違えていると、Tor起動を拒否される場合があります。
tor.set.ClientOnly = 1
apply_settingメソッド:既に起動済みのTorに現在の設定を反映させる
設定を書き換えただけでは、現在すでに起動しているTorに対してその設定は反映されません。
設定を起動済みのTorに反映させるためにはapply_settingメソッドを使います。
# portを指定して起動済みのTorに現在の設定を反映させる tor.apply_setting(9050) # portをリストで指定することも可能 tor.apply_setting([9050, 9060, 9070]) # portを指定しなければ、起動済みの全てのTorに対して設定を適用 tor.apply_setting()
write_rcメソッド:setアトリビュートの内容をテキストにしてtorrcに出力
現状のsetアトリビュートに登録されている設定内容を元に、新規にtorrcファイルとして出力します。出力先のファイルパスはtorrcアトリビュートで確認できます。
# 出力先のファイルパスを確認できます。 tor.torrc Out: WindowsPath('C:/Users/Owner/AppData/Roaming/tor/torrc') tor.write_rc()
出力されたtorrcファイルは以前のtorrcファイルに上書きはされません。
古いtorrcファイルは “torrc.orig.x”として別に保存されます。
stemモジュールのコマンドを使う
より、上級者向けの操作として、tor_controlモジュールは、stemモジュールのラッパー的な役割も果たします。
実行しているTor全てに対してcontrol portを設定しています。control port を通して、起動済みのTorを自在に操作できる、stem.control.Controllerオブジェトを内部的に生成しています。
ctrlアトリビュート:Controllerオブジェクトへのアクセス
# port番号とオブジェトが辞書型で確認できる tor.ctrl Out: {9050: <stem.control.Controller at 0x2c9249eae48>} # これでControllerオブジェクトへアクセスできる。 tor.ctrl[9050] Out: <stem.control.Controller at 0x2c9249eae48> # Controllerオブジェクトで使えるコマンドを続ける事が出来る。
stemモジュールのcontrol.Controller オブジェクトをインスタンス内に格納しています。Controllerオブジェクトに対して有効なコマンドは、基本的に全て使うことが出来ます。
以下に実行可能なコマンド一覧を記載します。(stemドキュメントより日本語訳しています。リンクは下部)
Controller – General controller class intended for direct use
詳細は:https://stem.torproject.org/api/control.html
|-from_port-ポート接続に基づいてコントローラーを提供します。
+-from_socket_file-ソケットファイル接続に基づいてコントローラーを提供します。
|-authenticate-torでこのコントローラーを認証します
|-reconnect-再接続してソケットに認証します
|-get_info-パラメーターのGETINFOクエリを発行します
|-get_version-torバージョンを提供します
|-get_exit_policy-終了ポリシーを提供します
|-get_ports-torが接続をリッスンしているローカルポートを提供します
|-get_listeners-torが接続をリッスンしているアドレスとポートを提供します
|-get_accounting_stats-中継制限に関連する統計を提供します
|-get_protocolinfo-コントローラーインターフェースに関する情報
|-get_user-torが実行されているユーザーを提供します
|-get_pid-torプロセスのpidを提供します
|-get_start_time-torプロセスが開始したときのタイムスタンプ
|-get_uptime-持続時間torが実行されています
|-is_user_traffic_allowed-直接のユーザートラフィックを送受信するかどうかを確認します
|-get_microdescriptor-リレーのマイクロディスクリプタを照会する
|-get_microdescriptors-現在利用可能なすべてのマイクロディスクリプタを提供します
|-get_server_descriptor-リレーのサーバー記述子を照会する
|-get_server_descriptors-現在利用可能なすべてのサーバー記述子を提供します
|-get_network_status-リレーのルーターステータスエントリのクエリ
|-get_network_statuses-現在利用可能なすべてのルーターステータスエントリを提供します
|-get_hidden_service_descriptor-指定された隠しサービス記述子を照会します
|-get_conf-構成オプションの値を取得
|-get_conf_map-複数の構成オプションの値を取得します
|-is_set-オプションがデフォルトと異なるかどうかを決定します
|-set_conf-構成オプションの値を設定します
|-reset_conf-構成オプションをデフォルト値に戻します
|-set_options-複数の構成オプションの値を設定またはリセットします
|-get_hidden_service_conf-非表示のサービス構成を提供します
|-set_hidden_service_conf-非表示のサービス構成を設定します
|-create_hidden_service-新しい非表示サービスを作成するか、新しいポートを追加します
|-remove_hidden_service-非表示のサービスを削除するか、ポートをドロップします
|-list_ephemeral_hidden_services-一時的な隠されたサービスのリスト
|-create_ephemeral_hidden_service-新しい一時的な非表示サービスを作成する
|-remove_ephemeral_hidden_service-一時的な非表示サービスを削除します
|-add_event_listener-torイベントの通知を受けるイベントリスナーをアタッチします
|-remove_event_listener-リスナーを削除して、以降のイベントが通知されないようにします
|-is_caching_enabled-コントローラーがキャッシュを有効にしている場合はtrue
|-set_caching-キャッシュを有効または無効にする
|-clear_cache-キャッシュされた結果をクリアします
|-load_conf-設定情報をtorrcにあるかのようにロードします
|-save_conf-設定情報をtorrcに保存します
|-is_feature_enabled-特定のコントローラー機能が有効かどうかを確認します
|-enable_feature-デフォルトで無効になっているコントローラー機能を有効にします
|-get_circuit-アクティブな回路を提供します
|-get_circuits-アクティブな回路のリストを提供します
|-new_circuit-新しい回路を作成する
|-extend_circuit-新しい回路を作成し、既存の回路を拡張します
|-repurpose_circuit-回路の目的を変更する
|-close_circuit-回路を閉じる
|-get_streams-アクティブなストリームのリストを提供します
|-attach_stream-ストリームを回線に接続します
|-close_stream-ストリームを閉じる
|-signal-torクライアントにシグナルを送信します
|-is_newnym_available-torが現在NEWNYMシグナルを受け入れる場合はtrue
|-get_newnym_wait-torがNEWNYMシグナルを受け入れるまでの秒数
|-get_effective_rate-効果的な中継レート制限を提供します
|-is_geoip_unavailable-geoip dbが利用できないことがわかった場合はtrue
|-map_address-あるアドレスを別のアドレスにマップして、元のアドレスへの接続を別のアドレスに置き換えます
+-drop_guards-ガードリレーのセットをドロップし、新しいセットを選択します
例えば、設定値をディフォルトに戻したい場合は以下のように入力するとControllerを利用できます。
tor.ctrl[9050].reset_conf('ExitNodes')
ソースコードはnoteで公開しています。
コメント
コメント一覧 (2件)
購入を検討していますが、Win環境ではなくMacまたはLinuxでの使用を想定しています。
こちらのプログラムを流用することは可能でしょうか?
また可能な場合はどの程度プログラムの修正は必要でしょうか?
お手数おかけしますが回答よろしくお願い致します。
こんにちは。ご検討いただけているとの件、コメントいただきありがとうございます。
今ソース見たところLinux共通で対応しようとした痕跡はありましたが、最後までやった記憶はないので、
おそらくLinuxでの利用は、そのままでは動作しないと思います。もちろんWin系ならそのまま動きます。
大部分は共通して動くような書き方をしてはいますが、
subprocessモジュール(標準)でTorを起動時させたり、killする時に引き渡すコマンドの修正、
ファイルパス関係の修正などは必要だと思います。
Linux対応しようとした痕跡、その他の動作解説のコメントもそのまま公開しています。
ソースは1枚ファイルの550行ほどです。