How can i make one Enemy prefab which will load the right ScriptableObject information?
I would put those scriptable objects on the script which spawns said enemies. Give it two inspector variables: Your general Enemy prefab and an array of your enemy ScriptableObjects. When it instantiates an enemy, it picks a scriptable object from that array at random and assigns it to the newly instantiated enemy. This allows you to set the pool of available enemies for each enemy spawner separately.
But if you want to avoid the hassle of assigning each new enemy type to the enemy spawner after you created it, then Addressables might be an option. The new Addressables system allows you to give each asset in your project an unique name. Scripts can then obtain that asset by using that name.
Should i create new prefab for each monster? Or is approach 1 the right way to do it, if you plan to expand over 100+ creatures.
Prefab variants could give you the best of both worlds. You can create a general Enemy prefab with all the components all the enemies have in common and with reasonable defaults for all settings on those components. And then you can create prefab variants for all the enemies which are based on that basic Enemy prefab. If you change something at the basic Enemy prefab, then that change will also affect all the variants which didn't override that particular setting.
This solution generates the same amount of clutter as the ScriptableObject solution. In both cases you will have one asset per monster in your project folder. But they give you a lot more flexibility because prefab variants allow you to override properties you didn't think about when you programmed your ScriptableObject. Like attaching new components or even new child game-objects to individual prefabs.
Oh, and you can even create prefab variants inheriting from prefabs which are variants themselves. This can be useful if you have families of monsters which share some properties but have a lot of properties in common (like palette swaps).