The idea in python is that s[:12] gives you the first 12 characters. That is quite convenient.
So, s[:12] is s[0], s[1], ..., s[11]. It gives you the 1st (s[0]), 2nd (s[1]), 3rd (s[2]), ..., 12th (s[11]) characters of s.
s[:12] is implicitly s[0:12]. That is quite logical too.
If you want to start from the 3rd character (s[2]), and finish at the 12th character (s[11]) you use s[2:12].
Or, for your example, to extract the 2nd character (s[1]), the 3rd (s[2]), 4th (s[3]) and 5th (s[4]), you need to use s[1:5].
So, you see, those rules (1st bound included, last one not included; index starting at 0) may seem illogical, but in fact, they are very logical.
Another way to put it is to say that
s[A:B]
gives the B first characters of s but the A first characters.
(hence, if you just want the B first characters of s, you can use s[0:B] or s[:B]; if you want all characters of s, but the A first, you can use s[A:])
Same logic goes for range; you know, as in "for i in range(10)":
it iterates i through the first 10 integers, starting with 0. So 0, 1, 2 ... 9. 10 is excluded.
if you specify two boundaries, like in range(5,10), it means also "iterates through the 10 first integers, but skip the 5 first". range(10) is just an alias of range(0,10) "iterates through the 10 first integers, but skip the 0 first"
Another reason why this is convenient (first included, last excluded), is because the number of iterations is then obvious
for i in range(5, 12) iterates 12-5 times = 7
No +1 or -1, here.
Likewise s[5:12] contains 12-5=7 characters.
Again another, it makes it easier to split in two parts.
s is the concatenation of s[:5], s[5:12], s[12:33], s[33:]
No characters are forgotten or counted twice in that splitting (s[5] is part of s[5:12], but not of s[:5]; s[12] is part of s[12:33] but not of s[5:12], etc)"