GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing zetcode.com — Python Requests - accessing web resources via HTTP Python Requestslast modified May 9, 2026 In this article, we explore practical techniques for working with the Python Requests module, one of the most widely used libraries for interacting with web resources. Step by step, we demonstrate how to retrieve data from remote servers, submit form data, upload JSON payloads, stream large responses efficiently, and establish secure HTTPS connections. Each concept is illustrated with clear, real-world examples. To make the examples concrete, we interact with several types of back-end services: a public online API, an Nginx server configured for local testing, Python's built-in HTTP server, and a small Flask application. This variety shows how Requests behaves across different environments and how to adapt your code to each scenario. The Hypertext Transfer Protocol (HTTP) is the underlying application protocol that enables communication across the Web. It defines how clients and servers exchange messages, how resources are addressed, and how data is transferred in a reliable, extensible way. Because HTTP is the foundation of nearly all modern web services, understanding how to work with it programmatically is essential for tasks such as automation, data collection, testing, and system integration.In this article, we explore practical techniques for working with the Python Requests module, one of the most widely used libraries for interacting with web resources. Step by step, we demonstrate how to retrieve data from remote servers, submit form data, upload JSON payloads, stream large responses efficiently, and establish secure HTTPS connections. Each concept is illustrated with clear, real-world examples. The Hypertext Transfer Protocol (HTTP) is the underlying application protocol that enables communication across the Web. It defines how clients and servers exchange messages, how resources are addressed, and how data is transferred in a reliable, extensible way. Because HTTP is the foundation of nearly all modern web services, understanding how to work with it programmatically is essential for tasks such as automation, data collection, testing, and system integration. Python requests
Library versionThe first program prints the version of the Requests library. version.py
#!/usr/bin/python import requests print(requests.__version__) print(requests.__copyright__) The program prints the version and copyright of Requests. $ ./version.py 2.33.1 Copyright Kenneth Reitz Reading a web page
The read_webpage.py
#!/usr/bin/python import requests url = "https://example.com" resp = requests.get(url) print(resp.text)
The Resource management
When read_webpage_with.py
#!/usr/bin/python
import requests
with requests.get("https://example.com") as resp:
resp.raise_for_status()
print(resp.text)
The Stripping HTML tagsThe following program gets a small web page and strips its HTML tags. strip_tags.py
#!/usr/bin/python
import requests
import re
url = "https://example.com"
with requests.get(url) as resp:
resp.raise_for_status()
content = resp.text
stripped = re.sub('<[^<]+?>', '', content)
print(stripped)
The script strips the HTML tags of the stripped = re.sub('<[^<]+?>', '', content)
A simple regular expression is used to strip the HTML tags. For more complex HTML documents, consider using a library like Beautiful Soup instead of regular expressions for more robust parsing. Getting status
The get_status.py
#!/usr/bin/python
import requests
url = "https://example.com"
with requests.get(url) as resp:
print(resp.status_code)
url = "https://example.com/news"
with requests.get(url) as resp:
print(resp.status_code)
We perform two HTTP requests with the $ ./get_status.py 200 404 200 is a standard response for successful HTTP requests and 404 tells that the requested resource could not be found. The Response object
Every request method —
The following example exercises several of these attributes against
response_object.py
#!/usr/bin/python
import requests
url = "https://httpbin.org/get"
try:
with requests.get(url, params={"lang": "python"}, timeout=(5, 10)) as resp:
resp.raise_for_status()
print(f"Status code: {resp.status_code}")
print(f"OK: {resp.ok}")
print(f"Final URL: {resp.url}")
print(f"Redirects: {len(resp.history)}")
print(f"Elapsed: {resp.elapsed.total_seconds():.3f} s")
print(f"Encoding: {resp.encoding}")
print(f"Content-Type: {resp.headers.get('content-type', 'n/a')}")
print(f"Sent headers: {dict(resp.request.headers)}")
print()
print("Body (JSON):")
data = resp.json()
for key, value in data.items():
print(f" {key}: {value}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
The program sends a GET request to $ ./response_object.py
Status code: 200
OK: True
Final URL: https://httpbin.org/get?lang=python
Redirects: 0
Elapsed: 0.621 s
Encoding: utf-8
Content-Type: application/json
Sent headers: {'User-Agent': 'python-requests/2.33.1', 'Accept-Encoding':
'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive'}
Body (JSON):
args: {'lang': 'python'}
...
Python requests raise_for_status
The
While raise_for_status.py
#!/usr/bin/python
import requests
urls = [
"https://example.com", # 200 OK — succeeds
"https://httpbin.org/status/404", # 404 Not Found — client error
"https://httpbin.org/status/500", # 500 Internal Server Error — server error
]
for url in urls:
print(f"GET {url}")
try:
resp = requests.get(url)
resp.raise_for_status()
print(f" Status code: {resp.status_code} — OK\n")
except requests.exceptions.HTTPError as e:
print(f" HTTP error: {e}\n")
The $ ./raise_for_status.py GET https://example.com Status code: 200 — OK GET https://httpbin.org/status/404 HTTP error: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404 GET https://httpbin.org/status/500 HTTP error: 500 Server Error: INTERNAL SERVER ERROR for url: https://httpbin.org/status/500
RequestException
Every exception the The hierarchy below shows the most commonly encountered exceptions and their inheritance relationships. RequestException ├── ConnectionError │ ├── ProxyError │ └── SSLError ├── HTTPError # raised by resp.raise_for_status() ├── URLRequired ├── TooManyRedirects ├── Timeout │ ├── ConnectTimeout │ └── ReadTimeout └── InvalidURL The most specific exceptions are at the bottom of the tree, and the most general at the top. This means that when you want to handle different failure modes separately, the specific exceptions must be caught before the more general ones — otherwise the base class will catch every error and make the specific branches unreachable. exceptions.py
#!/usr/bin/python
import requests
URLS = [
"https://httpbin.org/status/404", # HTTPError
"https://httpbin.org/delay/10", # ReadTimeout
"https://httpbin.org/get", # success
"https://invalid.invalid", # ConnectionError
"http://", # InvalidURL
]
for url in URLS:
print(f"GET {url}")
try:
with requests.get(url, timeout=(5, 3)) as resp:
resp.raise_for_status()
print(f" OK — status {resp.status_code}\n")
except requests.exceptions.ConnectTimeout:
print(" ConnectTimeout — server did not accept the connection in time\n")
except requests.exceptions.ReadTimeout:
print(" ReadTimeout — server connected but stalled mid-response\n")
except requests.exceptions.HTTPError as e:
print(f" HTTPError — {e}\n")
except requests.exceptions.SSLError as e:
print(f" SSLError — certificate or handshake failure: {e}\n")
except requests.exceptions.ProxyError as e:
print(f" ProxyError — could not connect through proxy: {e}\n")
except requests.exceptions.ConnectionError as e:
print(f" ConnectionError — DNS failure or refused connection: {e}\n")
except requests.exceptions.TooManyRedirects:
print(" TooManyRedirects — redirect loop detected\n")
except requests.exceptions.InvalidURL as e:
print(f" InvalidURL — malformed URL: {e}\n")
except requests.exceptions.RequestException as e:
print(f" Unexpected error: {e}\n")
The exceptions are ordered from most specific to most general, which is how
Python resolves $ ./exceptions.py GET https://httpbin.org/status/404 HTTPError — 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404 GET https://httpbin.org/delay/10 ReadTimeout — server connected but stalled mid-response GET https://httpbin.org/get OK — status 200 GET https://invalid.invalid ConnectionError — DNS failure or refused connection: ... GET http:// InvalidURL — malformed URL: ... When to catch the base class
In code where the reaction to any failure is the same — log the error, return a
default value, show a generic message to the user — catching
def fetch(url):
try:
with requests.get(url, timeout=(5, 10)) as resp:
resp.raise_for_status()
return resp.json()
except requests.exceptions.RequestException as e:
print(f"Could not fetch {url}: {e}")
return None
This pattern is appropriate for helper functions whose callers do not care about the specific cause of failure — only whether a result was returned. When the caller needs to distinguish between a server being down and a server returning a bad response, the specific subclasses should be caught and re-raised or handled individually instead. HTTPError and raise_for_status
HTTP request methods
An HTTP request is a message sent from a client to a server asking it
to perform a specific action. The action is identified by a
method — the most common being head_request.py
#!/usr/bin/python
import requests
url = "https://example.com"
with requests.head(url, timeout=(5, 10)) as resp:
resp.raise_for_status()
print("Server: ", resp.headers.get("server", "n/a"))
print("Last modified: ", resp.headers.get("last-modified", "n/a"))
print("Content type: ", resp.headers.get("content-type", "n/a"))
A $ ./head_request.py Server: cloudflare Last modified: Wed, 06 May 2026 14:17:14 GMT Content type: text/html Python requests get method
The
The get_request.py
#!/usr/bin/python
import requests
url = "https://httpbin.org/get?name=Peter"
with requests.get(url) as resp:
resp.raise_for_status()
print(resp.text)
The script sends a variable with a value to the $ ./mget.py
{
"args": {
"name": "Peter"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, zstd",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.33.1",
"X-Amzn-Trace-Id": "Root=1-69ff0896-272a70fa29b81c2556b6fffb"
},
...
"url": "https://httpbin.org/get?name=Peter"
}
get_request2.py
#!/usr/bin/python
import requests
payload = {'name': 'Peter', 'age': 23}
url = "https://httpbin.org/get"
with requests.get(url, params=payload) as resp:
resp.raise_for_status()
print(resp.url)
print(resp.text)
The payload = {'name': 'Peter', 'age': 23}
The data is sent in a Python dictionary. resp = req.get("https://httpbin.org/get", params=payload)
We send a GET request to the print(resp.url) print(resp.text) We print the URL and the response content to the console. Python requests timeout attribute
The
The timeout applies to two distinct phases: the connection phase
(establishing the TCP handshake) and the read phase (waiting
for the server to send data). You can control both independently by passing a
tuple timeout_request.py
#!/usr/bin/python
import requests
TIMEOUT = (3, 5)
urls = [
"https://example.com", # responds immediately
"https://httpbin.org/delay/10", # deliberately delays 10 s — will time out
]
for url in urls:
print(f"GET {url}")
try:
with requests.get(url, timeout=TIMEOUT) as resp:
resp.raise_for_status()
print(f" Status code: {resp.status_code}\n")
except requests.exceptions.ConnectTimeout:
print(" Connection timed out — server not reachable\n")
except requests.exceptions.ReadTimeout:
print(" Read timed out — server connected but did not respond in time\n")
except requests.exceptions.Timeout:
print(" Request timed out\n")
The first URL responds normally within the allowed window. The second points to
TIMEOUT = (3, 5) The timeout is set to 3 seconds for the connection phase and 5 seconds for the read phase. $ ./timeout_request.py GET https://example.com Status code: 200 GET https://httpbin.org/delay/10 Read timed out — server connected but did not respond in time
Both Python requests redirectionRedirection is a process of forwarding one URL to a different URL. The HTTP response status code 301 Moved Permanently is used for permanent URL redirection; 302 Found for a temporary redirection. redirect.py
#!/usr/bin/python
import requests
url = "https://httpbin.org/redirect-to?url=/"
with requests.get(url) as resp:
resp.raise_for_status()
print(resp.status_code)
print(resp.history)
print(resp.url)
In the example, we issue a GET request to the
$ ./redirect.py 200 [<Response [302]>] https://httpbin.org/
A GET request to In the second example, we do not follow a redirect. redirect2.py
#!/usr/bin/python
import requests
url = "https://httpbin.org/redirect-to?url=/"
with requests.get(url, allow_redirects=False) as resp:
resp.raise_for_status()
print(resp.status_code)
print(resp.url)
The $ ./redirect2.py 302 https://httpbin.org/redirect-to?url=/ Redirect with nginxIn the next example, we show how to set up a page redirect in nginx server. location = /oldpage.html {
return 301 /newpage.html;
}
Add these lines to the nginx configuration file, which is located at
$ sudo service nginx restart After the file has been edited, we must restart nginx to apply the changes. oldpage.html
<!DOCTYPE html> <html> <head> <title>Old page</title> </head> <body> <p> This is old page </p> </body> </html>
This is the newpage.html
<!DOCTYPE html> <html> <head> <title>New page</title> </head> <body> <p> This is a new page </p> </body> </html>
This is the redirect3.py
#!/usr/bin/python
import requests
url = "http://localhost/oldpage.html"
with requests.get(url) as resp:
resp.raise_for_status()
print(resp.status_code)
print(resp.history)
print(resp.url)
print(resp.text)
This script accesses the old page and follows the redirect. As we already mentioned, Requests follows redirects by default. $ ./redirect3.py 200 (<Response [301]>,) http://localhost/files/newpage.html <!DOCTYPE html> <html> <head> <title>New page</title> </head> <body> <p> This is a new page </p> </body> </html> $ sudo tail -2 /var/log/nginx/access.log 127.0.0.1 - - [21/Jul/2019:07:41:27 -0400] "GET /oldpage.html HTTP/1.1" 301 184 "-" "python-requests/2.4.3 CPython/3.4.2 Linux/3.16.0-4-amd64" 127.0.0.1 - - [21/Jul/2019:07:41:27 -0400] "GET /newpage.html HTTP/1.1" 200 109 "-" "python-requests/2.4.3 CPython/3.4.2 Linux/3.16.0-4-amd64"
As we can see from the User agent
A user agent is a short identification string that an HTTP client
sends to a server in the When we write our own Python HTTP server, we can choose any user-agent name we like. This makes it easy to distinguish our requests from browsers, automated tools, or other scripts in server logs. It also helps when debugging, because the server can immediately see that the request came from our custom client rather than from Chrome, Firefox, or a bot. http_server.py
#!/usr/bin/python
from http.server import BaseHTTPRequestHandler, HTTPServer
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
message = "Hello there"
self.send_response(200)
if self.path == "/agent":
message = self.headers["user-agent"]
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes(message, "utf8"))
return
if __name__ == "__main__":
with HTTPServer(("127.0.0.1", 8081), MyHandler) as server:
print("Listening on http://127.0.0.1:8081")
server.serve_forever()
We have a simple Python HTTP server. if self.path == '/agent':
message = self.headers['user-agent']
If the path contains user_agent.py
#!/usr/bin/python
import requests
headers = {'user-agent': 'Python script'}
url = "http://localhost:8081/agent"
with requests.get(url, headers=headers) as resp:
resp.raise_for_status()
print(resp.text)
This script creates a simple GET request to our Python HTTP server.
To add HTTP headers to a request, we pass in a dictionary to the
headers = {'user-agent': 'Python script'}
The header values are placed in a Python dictionary. resp = requests.get("http://localhost:8081/agent", headers=headers)
The values are passed to the $ ./http_server.py starting server on port 8081... First, we start the server. $ ./user_agent.py Python script Then we run the script. The server responded with the name of the agent that we have sent with the request. Python requests post value
The post_value.py
#!/usr/bin/python
import requests
data = {'name': 'Peter'}
url = "https://httpbin.org/post"
with requests.post(url, data=data) as resp:
resp.raise_for_status()
print(resp.text)
The script sends a request with a $ ./post_value.py
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "Peter"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "10",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.21.0"
},
"json": null,
...
}
This is the output of the Python requests upload imageIn the following example, we are going to upload an image. We create a web application with Flask. app.py
#!/usr/bin/python
import os
from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def home():
return 'This is home page'
@app.route("/upload", methods=['POST'])
def handleFileUpload():
msg = 'failed to upload image'
if 'image' in request.files:
photo = request.files['image']
if photo.filename != '':
photo.save(os.path.join('.', photo.filename))
msg = 'image uploaded successfully'
return msg
if __name__ == '__main__':
app.run()
This is a simple application with two endpoints. The upload_file.py
#!/usr/bin/python
import requests
url = 'http://localhost:5000/upload'
image_file = 'data/sid.jpg'
with open(image_file, 'rb') as f:
files = {'image': f}
with requests.post(url, files=files) as resp:
resp.raise_for_status()
print(resp.text)
We send the image to the Flask application. The file is specified
in the JSON
JSON is a
lightweight text format for exchanging structured data. It maps directly onto
Python dictionaries and lists, making it the natural choice for HTTP APIs:
the Reading JSON from a server
When the server sets http_server_json.py
#!/usr/bin/python
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
payload = json.dumps({"name": "Jane", "age": 17}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
if __name__ == "__main__":
with HTTPServer(("127.0.0.1", 8000), Handler) as server:
print("Listening on http://127.0.0.1:8000")
server.serve_forever()
This is a simple HTTP server that returns a JSON response to any GET request.
The read_json.py
#!/usr/bin/python
import requests
url = "http://127.0.0.1:8000"
try:
with requests.get(url, timeout=(5, 10)) as resp:
resp.raise_for_status()
data = resp.json()
print(f"Name: {data['name']}, age: {data['age']}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
except ValueError as e:
print(f"Failed to decode JSON: {e}")
$ ./read_json.py Name: Jane, age: 17 Sending JSON to a server
Passing a dictionary to the http_server_json2.py
#!/usr/bin/python
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
try:
data = json.loads(self.rfile.read(length))
except json.JSONDecodeError:
self.send_response(400)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(b"400 Bad Request: invalid JSON\n")
return
lines = [f"{key}: {value}" for key, value in data.items()]
payload = "\n".join(lines).encode()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
if __name__ == "__main__":
with HTTPServer(("127.0.0.1", 8000), Handler) as server:
print("Listening on http://127.0.0.1:8000")
server.serve_forever()
This server accepts POST requests with a JSON body, decodes it, and returns a
plain-text summary of the fields. If the body is not valid JSON, it responds
with send_json.py
#!/usr/bin/python
import requests
url = "http://127.0.0.1:8000"
data = {"name": "Jane", "age": 17}
try:
with requests.post(url, json=data, timeout=(5, 10)) as resp:
resp.raise_for_status()
print(resp.text)
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
The server reads $ ./send_json.py name: Jane age: 17 Working with cookies
A cookie is a small piece of data the server asks the browser (or
any HTTP client) to store and send back with every subsequent request to the
same origin. Cookies are the standard mechanism for maintaining state across
otherwise stateless HTTP connections — session tokens, user preferences, and
tracking identifiers are all commonly stored this way. The client sends
cookies in the
The following examples use a minimal
The table covers both HTTP headers ( Sending cookies from the client
The cookie_server.py
#!/usr/bin/python
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
cookies = self.headers.get("Cookie", "(no cookies)")
body = f"Received cookies: {cookies}\n".encode()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
if __name__ == "__main__":
with HTTPServer(("127.0.0.1", 8000), Handler) as server:
print("Listening on http://127.0.0.1:8000")
server.serve_forever()
This is a simple HTTP server that reads the cookie_request.py
#!/usr/bin/python
import requests
url = "http://127.0.0.1:8000"
cookies = {"user": "jane", "theme": "dark"}
try:
with requests.get(url, cookies=cookies, timeout=(5, 10)) as resp:
resp.raise_for_status()
print(resp.text)
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
The $ ./cookie_request.py Received cookies: user=jane; theme=dark Receiving cookies set by the server
When the server wants to create a cookie in the client, it includes a
cookie_set_server.py
#!/usr/bin/python
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from email.utils import formatdate
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
incoming = self.headers.get("Cookie")
if incoming:
body = f"Client sent cookies: {incoming}\n".encode()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
else:
ts = int(time.time())
expires = formatdate(ts + 3600, usegmt=True)
max_age = 3600
cookies = [
f"sessionid={ts}; Path=/; HttpOnly; Max-Age={max_age}; Expires={expires}",
f"theme=dark; Path=/; Max-Age={max_age}; Expires={expires}",
f"user=jane; Path=/; Max-Age={max_age}; Expires={expires}",
]
body = b"Cookies set send them back on the next request.\n"
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
for cookie in cookies:
self.send_header("Set-Cookie", cookie)
self.end_headers()
self.wfile.write(body)
if __name__ == "__main__":
with HTTPServer(("127.0.0.1", 8000), Handler) as server:
print("Listening on http://127.0.0.1:8000")
server.serve_forever()
The server distinguishes the two requests by checking whether a
cookie_set_request.py
#!/usr/bin/python
import requests
url = "http://127.0.0.1:8000"
try:
# First request — server sets cookies.
with requests.get(url, timeout=(5, 10)) as resp:
resp.raise_for_status()
print("First response:", resp.text)
print("Cookies received:")
for name, value in resp.cookies.items():
print(f" {name} = {value}")
# Second request — send the cookies back.
with requests.get(url, cookies=resp.cookies, timeout=(5, 10)) as resp2:
resp2.raise_for_status()
print("\nSecond response:", resp2.text)
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
This script performs two GET requests to the server. The first one receives the cookies set by the server and prints them to the console. The second request sends the received cookies back to the server, which responds with a message confirming the cookies it got from the client. $ ./cookie_set_request.py First response: Cookies set send them back on the next request. Cookies received: sessionid = 1778336578 theme = dark user = jane Second response: Client sent cookies: sessionid=1778336578; theme=dark; user=jane Retrieving definitions from a dictionary
The following example scrapes word definitions from
get_term.py
#!/usr/bin/python
import sys
import textwrap
import requests
from lxml import html
HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"}
BASE_URL = "https://www.dictionary.com/browse/"
WRAP_WIDTH = 80 # matches the standard 80-column terminal width
# Tried in order; the first that yields text wins.
XPATHS = [
"//span[contains(@class,'one-click-content')]//text()",
"//*[contains(@data-testid,'definition')]//text()",
"//section[contains(@class,'definitions') or contains(@class,'css-pnw38j')]//text()",
"//main//p//text()",
"//meta[@name='description']/@content",
]
def fetch_html(term):
"""Return the parsed HTML tree for *term*, or raise on HTTP/network error."""
with requests.get(BASE_URL + term, headers=HEADERS, timeout=(5, 10)) as resp:
resp.raise_for_status()
return html.fromstring(resp.content)
def extract_definitions(root):
"""Try each XPath in XPATHS and return the first non-empty result set."""
for xpath in XPATHS:
texts = []
for node in root.xpath(xpath):
text = node.strip() if isinstance(node, str) else node.text_content().strip()
if text:
texts.append(text)
if texts:
return texts
# Last resort: first 40 non-empty text nodes anywhere inside main.
return [t.strip() for t in root.xpath("//main//text()") if t.strip()][:40]
def deduplicate(texts):
"""Split on newlines, drop fragments shorter than 4 chars, remove duplicates."""
seen = set()
out = []
for block in texts:
for part in (s.strip() for s in block.split("\n") if s.strip()):
if len(part) > 3 and part not in seen:
seen.add(part)
out.append(part)
return out
def main():
term = sys.argv[1] if len(sys.argv) > 1 else "dog"
try:
root = fetch_html(term)
except requests.exceptions.ConnectTimeout:
sys.exit("Connection timed out — check your network and try again.")
except requests.exceptions.ReadTimeout:
sys.exit("Server took too long to respond — try again later.")
except requests.exceptions.HTTPError as e:
sys.exit(f"HTTP error: {e}")
except requests.exceptions.ConnectionError as e:
sys.exit(f"Network error: {e}")
except requests.exceptions.RequestException as e:
sys.exit(f"Unexpected request error: {e}")
lines = deduplicate(extract_definitions(root))
if not lines:
sys.exit("No definition found — the site structure may have changed.")
for line in lines:
print(textwrap.fill(line, width=WRAP_WIDTH))
if __name__ == "__main__":
main()
The script is structured around three focused functions.
Exception handling lives in
Note: Web scraping is inherently fragile. Sites restructure
their markup without warning, and
dictionary.com in particular
is a React application whose server-rendered HTML can vary by region, A/B test
bucket, or deploy. If the script stops returning definitions, inspect the live
page source and update XPATHS accordingly.
$ ./get_term.py ephemeral lasting a very short time; short-lived transitory The poem celebrates the ephemeral joys of childhood. ... Python requests streaming
By default, This makes streaming the correct approach whenever the response body is large (file downloads, database exports), potentially unbounded (live event feeds, log streams), or when you want to start processing early parts before the transfer is complete. streaming.py
#!/usr/bin/python
from pathlib import Path
from urllib.parse import urlsplit
import requests
def download(url, dest=None, chunk_size=65_536, timeout=(5, 30)):
"""Stream *url* to disk, returning the Path of the saved file.
Args:
url: Remote URL to fetch.
dest: Local file path. Inferred from the URL when omitted.
chunk_size: Bytes per write cycle (default 64 KiB).
timeout: (connect, read) timeout in seconds.
Raises:
requests.HTTPError: Non-2xx response.
requests.Timeout: Connection or read deadline exceeded.
requests.ConnectionError: DNS failure or refused connection.
requests.RequestException: Any other transport-level failure.
OSError: Local filesystem write failure.
"""
dest = Path(dest) if dest else Path(Path(urlsplit(url).path).name or "download.bin")
with requests.get(url, stream=True, timeout=timeout) as r:
r.raise_for_status()
with dest.open("wb") as f:
for chunk in r.iter_content(chunk_size=chunk_size):
f.write(chunk)
return dest
url = "https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf"
try:
path = download(url, "java25spec.pdf")
print(f"Saved to {path} ({path.stat().st_size:,} bytes)")
except requests.exceptions.ConnectTimeout:
print("Connection timed out — server did not accept the connection in time")
except requests.exceptions.ReadTimeout:
print("Read timed out — server stalled mid-transfer")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e}")
except requests.exceptions.ConnectionError as e:
print(f"Network error: {e}")
except requests.exceptions.RequestException as e:
print(f"Unexpected request error: {e}")
except OSError as e:
print(f"Could not write file: {e}")
The
The exception hierarchy moves from most specific to most general.
$ ./streaming.py Saved to java25spec.pdf (5,331,364 bytes) Python requests credentials
The $ sudo apt-get install apache2-utils $ sudo htpasswd -c /etc/nginx/.htpasswd user7 New password: Re-type new password: Adding password for user user7
We use the location /secure {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Inside the nginx index.html
<!DOCTYPE html> <html lang="en"> <head> <title>Secure page</title> </head> <body> <p> This is a secure page. </p> </body> </html>
Inside the credentials.py
#!/usr/bin/python
import requests
user = 'user7'
passwd = '7user'
with requests.get("http://localhost/secure/", auth=(user, passwd)) as resp:
print(resp.text)
The script connects to the secure webpage; it provides the user name and the password necessary to access the page. $ ./credentials.py <!DOCTYPE html> <html lang="en"> <head> <title>Secure page</title> </head> <body> <p> This is a secure page. </p> </body> </html>
With the right credentials, the SourceIn this article we have worked with the Python Requests module. The Requests library is a powerful and user-friendly HTTP client for Python. It allows you to send HTTP requests with ease, making it a popular choice for developers when working with web APIs and handling HTTP interactions in their applications. AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |