## Python 2, 252249238212211213 209 bytes

n=input()
x=(n+22)/23or 1
for i in range(x):b=n/x+(n%x>i);c=r"""    _V_
/(@I@)\
/361%s163\
|408717804|
\5201025/
''-!-''"""%'|O'[b%2];i=0;exec"c=c.replace(i%9,' |oO'[i>9::2][i<b/2],2);i+=1;"*11;print c

Try it online!

• Saved 9 bytes thanks to Kevin Cruijssen
• Saved 18 bytes thanks to Mr. Xcoder
• Saved 2 bytes thanks to Jonathan Frech

## JavaScript (ES6), 183 186 bytes

Uses the same formula as TFeld's answer to split the spots among the ladybugs.

n=>(g=k=>k--?    _V_
/(@I@)\\
/3\\
|4|
\\3/
''-!-''
.replace(/\d/g,i=>(h=s=>i--?h((p=N?(N-=2,'o'):' ')+s+p):s)('|O'[N>2*i|N&1&&+!!N--]),N=(n%j>k)+n/j|0)+g(k):'')(j=n/23+.99|0||1)

### Demo

let f =

n=>(g=k=>k--?    _V_
/(@I@)\\
/3\\
|4|
\\3/
''-!-''
.replace(/\d/g,i=>(h=s=>i--?h((p=N?(N-=2,'o'):' ')+s+p):s)('|O'[N>2*i|N&1&&+!!N--]),N=(n%j>k)+n/j|0)+g(k):'')(j=n/23+.99|0||1)

console.log(f(0))
console.log(f(1))
console.log(f(9))
console.log(f(25))

## Befunge, 292 279 bytes

#j07-00p&>::1-27*9+/\!!*:1+06pv-1_@#:<<g61$<<: v"h"**95%2:+g61%g60\/g60::p61<>-0g*+35*-,:!| >\-30p2/:55+:59**"g"\-40p-26pv^*84\!"."::<<9 v\%+55+g62:%+55+4*g62:::-1<+55<>:"/n"$#->#<^#<
>26g!+"O"*"Y"\-\-\5+0p:>#^_" 66<0<66// >,-"v
"n //>7OXO8k />'&%$%&'k !(*+)#)+*(! /k,-.$."<v

Try it online!

Explanation

The ASCII art for the ladybug is encoded in a single Befunge string, offset by 15, to allow the first 15 printable characters to be reserved for special purposes. The first two of these special characters represent the newline and the | character, both of which would otherwise not be printable. The third isn't used, because it's a ", which can't be used in a string. The next two represent the large spots in the centre. And the remaining ten are for the spots on the wings.

These special characters are translated into their final form via a lookup table, which is written over the first part of the first line.

To make it easier to explain, this is the code with the various component parts highlighted:

We start by initialising the newline and | character in the lookup table, since these are constant.
Next we read in the number of spots from stdin and calculate the number of ladybugs required.
We can then start the outer loop for the set of bugs, calculating the number of spots for the next ladybug to be rendered.
For each ladybug, we calculate whether the large centre spot needs to be shown (if spots%2 == 1), and write the appropriate value into the lookup table.
Similarly, we calculate whether the other pair of large spots need to be shown (if spots/2 > 10), again updating the lookup table. We also calculate the remaining spots required on each wing.
The final part of the lookup table initialization is a loop that calculates which of the small spots need to be displayed. Essentially the algorithm is: if (spotnum*spotcount+4)%10 > ((spotnum+1)*spotcount+4)%10, then the spot needs to be displayed.
Next we push the encoded string representation of the ladybug onto the stack. This is essentially just a simple string, but it became a bit convoluted as I tried to squeeze it into the gaps in the code so the source would form a rectangle.
At this point, we're ready to begin the output loop, processing the characters one by one, converting the special cases (the spots, line breaks, etc.) via the previously constructed lookup table.
Finally we check whether we've displayed all the required ladybugs, otherwise continue back to the start the of the outer loop.