Exploring SOAP Web Services – From Browser Console to Python
SOAP (Simple Object Access Protocol) might sound intimidating (or funny) but it is actually a straightforward way for systems to exchange structured messages using XML. In this article, I am introducing SOAP through YouTube video, where it is explored through 2 different angles – first in the Chrome browser console, then with Python and Jupyter Notebook.

Part 1 – Soap in the Chrome Browser Console
We start by sending SOAP requests directly from the browser’s JS console. This is a quick way to see the raw XML <soap> envelopes in action. Using a public integer calculator web service, we perform basic operations – additions, subtraction, multiplication, division – and observe how the requests and responses happen in real time!
For the browser, the entire SOAP journey looks like that:
Chrome Browser -> HTTP POST -> SOAP XML -> Server (http://www.dneonline.com/calculator.asmx?WSDL) -> SOAP XML -> Chrome Browser
function soapCalc(op, a, b) {
const xml = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<${op} xmlns="http://tempuri.org/">
<intA>${a}</intA>
<intB>${b}</intB>
</${op}>
</soap:Body>
</soap:Envelope>`;
return fetch("/calculator.asmx", {
method: "POST",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": `http://tempuri.org/${op}`
},
body: xml
})
.then(r => r.text())
.then(str => {
const doc = new DOMParser().parseFromString(str, "text/xml");
const resultTag = `${op}Result`;
const result = doc.getElementsByTagName(resultTag)[0]?.textContent;
console.log(`${op}(${a}, ${b}) → ${result}`);
return Number(result);
});
}
A simple way to call it is with constants, to avoid the strings:
const soapAdd = (a,b) => soapCalc("Add", a, b);
const soapSubtract = (a,b) => soapCalc("Subtract", a, b);
const soapMultiply = (a,b) => soapCalc("Multiply", a, b);
const soapDivide = (a,b) => soapCalc("Divide", a, b);
Like that:
soapAdd(5, 7); // 12 soapSubtract(10, 3); // 7 soapMultiply(6, 7); // 42 soapDivide(8, 2); // 4
Part 2 – Soap with Python and Jupyter Notebook
Here we jump into Python. With the help of libaries, we load the the WSDL (Web Services Description Language) file, inspect the available operations, and call the same calculator service programmatically.
from zeep import Client, Settings, helpers
from zeep.plugins import HistoryPlugin
from zeep.transports import Transport
from zeep.exceptions import Fault, TransportError
import requests
import requests_cache
from lxml import etree
session = requests.Session()
transport = Transport(session=session, timeout=20)
settings = Settings(strict=False, xml_huge_tree=True)
history = HistoryPlugin()
WSDL = "https://www.dneonline.com/calculator.asmx?WSDL"
client = None
MODE = None
def build_client_from(url: str):
return Client(wsdl=url, settings=settings, transport=transport, plugins=[history])
try:
session.verify = True
client = build_client_from(WSDL)
MODE = "https"
except Exception:
try:
session.verify = False
client = build_client_from(WSDL)
MODE = "https_verify_false"
except Exception:
try:
http_wsdl = WSDL.replace("https://", "http://")
print("http is replaced!")
session.verify = True
client = build_client_from(http_wsdl)
MODE = "http"
except Exception:
client = None
MODE = None
print("Client is none!")
def list_ops_robust(c, label):
print(f"-- {label} --")
for service in c.wsdl.services.values():
print("Service:", service.name)
for port in service.ports.values():
print(" Port:", port.name)
ops = getattr(port.binding, "_operations", {})
if isinstance(ops, dict) and ops:
for name in sorted(ops.keys()):
print(" Operation:", name)
else:
proxy = c.bind(service.name, port.name)
names = sorted(n for n in dir(proxy) if not n.startswith("_") and callable(getattr(proxy, n, None)))
for n in names:
print(" Operation:", n)
def safe_call(op, **kwargs):
try:
return getattr(client.service, op)(**kwargs)
except Fault as f:
print(f"{op} → SOAP Fault:", f)
except TransportError as te:
print(f"{op} → Transport error:", te)
except Exception as e:
print(f"{op} → Error:", type(e).__name__, e)
show_last_exchange(history)
----- SOAP REQUEST -----
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:Subtract xmlns:ns0="http://tempuri.org/">
<ns0:intA>360</ns0:intA>
<ns0:intB>2400</ns0:intB>
</ns0:Subtract>
</soap-env:Body>
</soap-env:Envelope>
----- SOAP RESPONSE -----
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<SubtractResponse xmlns="http://tempuri.org/">
<SubtractResult>-2040</SubtractResult>
</SubtractResponse>
</soap:Body>
</soap:Envelope>
safe_call("Divide", intA=100, intB=10)
>>10
Github code – https://github.com/Vitosh/Python_personal/tree/master/YouTube/038_Python-SOAP-Basics!
Enjoy it! 🙂