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.

The SOAP Exchange Mechanism uses requests and response.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
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) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
——– 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 |
https://www.youtube.com/watch?v=rr0r1GmiyZg
Github code – https://github.com/Vitosh/Python_personal/tree/master/YouTube/038_Python-SOAP-Basics!
Enjoy it! 🙂