Pass an empty mapping to ProxyHandler when one urllib opener should connect directly:

from urllib.request import ProxyHandler, build_opener

direct = build_opener(ProxyHandler({}))

with direct.open("https://example.com/", timeout=10) as response:
    print(response.status)

The empty dictionary supplies no proxy routes. Omitting the mapping lets the handler discover proxies from its environment and platform settings instead. Those two constructions express different policies.

The example uses Python’s standard library and retains normal HTTPS verification. The with block closes the response, including when an exception leaves the block.

Direct and proxied requests in the same application

Two openers can carry the two policies without changing environment variables between calls:

from urllib.request import ProxyHandler, build_opener

direct = build_opener(ProxyHandler({}))
proxied = build_opener(ProxyHandler({
    "http": "http://127.0.0.1:8080",
    "https": "http://127.0.0.1:8080",
}))

with proxied.open("https://example.com/", timeout=10) as response:
    print(response.status)

Replace the loopback address with your HTTP proxy, or choose a candidate from the HTTP proxy list for the proxied comparison. It can carry the HTTPS target through CONNECT when the service permits it. Match the listener’s protocol and authentication support to the handler.

Leave the default opener alone

Call direct.open or proxied.open through the object that owns the route. install_opener changes the process-wide default used by urllib’s convenience calls, so a library using it can affect unrelated code. It is unnecessary for the isolated opener above.

The timeout argument is an operation setting, not a complete wall-clock deadline for every transfer. Limit reading to the data the job needs and use a higher-level deadline if the job has a strict total budget.

These opener objects do not alter another library’s client, an existing process or a browser’s proxy settings.

Artihkkalat mat laktásit