対象とする問題
pythonで書いたプログラムでスクレイピングする際に、以下の3つの問題があることが判明したので、ChatGPTに症状などを伝えて相談し、解決方法を見つけた。
- 使用環境のProxyサーバーがWindowsのユーザーアカウントを用いたWindows統合認証(NTLM)によるユーザー認証を要求するが、
requests
モジュールなど通常のpythonのライブラリーがこれに対応していない - pythonの標準ライブラリーでは、PAC(Proxy Auto-Config)ファイルに対応していない
-
https
で始まるURLにアクセスした場合に証明書に関するエラーが出る場合がある
NTLM認証とPACファイルの問題の解決方法
pythonのモジュールpx-proxyによるproxyサーバーを用い、元々のProxyサーバーの手前に入れたいわゆる多段串にすることで解決した。
px-proxyはNTLM認証に対応している。また、私の使用環境では下記の設定ファイルpx.ini
の様に、ユーザー名やパスワードを指定しないでも、px-proxyが認証情報を取得してくれた。この辺は環境によるらしい。
[proxy]
server =
pac = http://hogehoge.com/proxy.pac
pac_encoding = utf-8
port = 3128
listen = 127.0.0.1
gateway = 0
hostonly = 0
allow = 127.0.0.1
noproxy =
useragent =
username =
auth =
[client]
[settings]
workers = 2
threads = 32
idle = 30
socktimeout = 20.0
proxyreload = 60
foreground = 0
log = 0
私の使用環境では、proxyサーバーを自動設定する様になっていたので、pac =
の行に設定している。proxyサーバーのアドレスを指定する場合には、server =
の行に書けば良いと思われる。
px-proxyのインストール
単純に以下で良い。
pip install px-proxy
px-proxyによるproxyサーバーの起動
設定ファイルpx.ini
を適当なディレクトリーに起き、以下を実行する
python -m px -c hogehogedir/px.ini
python製プログラムでの対応
環境変数HTTP_PROXY
とHTTPS_PROXY
にpx.ini
のlisten
に指定したIPアドレスとport
に指定したポート番号を設定すれば良い。
px-proxyによるProxyサーバーが起動しているかも確認するならば、以下の様な関数を用意すると良い。
import socket
def set_proxy(port=3128, force_set_env = True):
try:
with socket.create_connection(("127.0.0.1", port), timeout=2) as sock:
print(f"[OK] px proxy はポート {port} で起動しています。")
except (socket.timeout, ConnectionRefusedError):
raise RuntimeError(
f"px proxy が 127.0.0.1:{port} で起動していません。\n"
f"px を起動してください(例: python -m px または px.exe)"
)
except Exception as e:
raise RuntimeError(f"px proxy チェック中に予期しないエラーが発生しました: {e}")
for x in ["HTTP_PROXY", "HTTPS_PROXY"]:
if force_set_env or (x not in os.environ):
os.environ[x] = f'http://127.0.0.1:{port}'
if True:
for x in ["HTTP_PROXY", "HTTPS_PROXY"]:
print(x, os.environ[x])
証明書に関する問題の解決方法
原因
https
で始まるURLにブラウザからはアクセス出来るのに、python製プログラムからではアクセス時にエラーや警告が出るのは、使用している「信頼済みCAのリスト(証明書ストア)」が異なるからだそうです。
Windows上ではブラウザは、Windowsの証明書ストアを使うのに対し、pythonの標準的なライブラリー(requests
, ssl
, urllib3
など)は、certifi
というPython向けの独立したCA証明書バンドルを使用するそうです。
certifi
はMozillaベースですが、更新タイミングが異なるため、最新CAが含まれていない場合があるそうで、この場合に問題が発生します。
解決方法
要はpython製プログラムにおいてもWindowsの証明書ストアを用いれば良いです。その方法はいくつかありますが、一番簡単なのはpythonのpip-system-certs
を使う方法です。
注意点
このモジュールをインストールしただけで、pipコマンドが使う証明書ストアもWindowsの証明書ストアを使う様に変更されます。
pip-system-certs
のインストール
単純に以下
pip install pip-system-certs
python製プログラムでの対応
下記例の様にrequests
モジュールなどより前にimportするだけです。
import pip_system_certs
import requests