HTTP redirects generally take the form of a 3xx response code plus a "Location:" header which indicates where to redirect to. This is codified in the HTTP protocol, and so any conformant client implementation will simply do whatever that spec says.
See RFC 7231 Section 6.4.
In so many words, if you call requests
to visit a URL (with redirection allowed - it can be turned off with an option in requests
) and the server says "go here instead", requests
will internally call itself on the new URL, and add the previous one to the history, as many times as it takes to reach a page which does not redirect, or you hit the limit (commonly set to something like 30 to prevent shenanigans such as a page redirecting to itself in an endless loop).
Many web servers such as CMSes rely on server-side URL rewriting configurations which allow a programmer to generate a (structurally) simple URL which the server then resolves and redirects to a different location which may be more friendly to the human eye or conform to a unified convention defined by that server's administrator, and some content delivery networks use redirection to send each visitor to a server which is close to them geographically or in terms of network topology. Clicktracking also frequently causes your browser to jump via a unique URL before sending it off to actually fetch the content it is trying to display. Because of these techniques, it's not uncommon to see multiple redirects when you attempt to fetch something.
In addition, but really outside of what requests
or similar libraries support, interactive browsers also commonly support JavaScript, which allows a web page to run code in the browser which may cause it to visit a new page under programmatic control (i.e. perhaps under complex conditions which might not even be entirely deterministic). If you need to support this, the currently popular solution is to run a real interactive browser (perhaps "headless", i.e. with no observable user interface) and have it communicate its state to Python somehow.