Match any character including newline
A dot does not match a newline by default. Turn on DOTALL, written (?s) in PCRE and Python, or use [sS] when the engine has no such flag.
A pattern with .* stops at the end of a line, and you want it to run across several.
import re
text = "BEGIN\nline one\nline two\nEND"
print(re.search(r"BEGIN.*END", text))
print(re.search(r"BEGIN.*END", text, re.DOTALL).group().count("\n"))
print(re.search(r"(?s)BEGIN.*END", text).group().count("\n"))
print(re.search(r"BEGIN[\s\S]*END", text).group().count("\n"))None
3
3
3Outside of the DOTALL mode a dot matches any character except the newline, which the re documentation states. The flag can be passed as an argument or switched on inside the pattern as (?s). The last form, a class of whitespace together with non-whitespace, is the portable one, since together they cover every character in any engine.
The name of the flag differs: JavaScript uses s, Perl s, and Ruby calls it m. grep works line by line, so it needs -z with -P to see a newline at all. A greedy .* with DOTALL swallows everything to the last END, so use .*? when you want the shortest span.