The standard random number generator, in languages like Java or Python, does not generate real random numbers but pseudorandom numbers determined by an initial seed value. If an attacker can somehow guess or determine this seed value, they can reconstruct the entire sequence of pseudorandom outputs.
Furthermore, the default pseudorandom number generators in most languages are not cryptographically secure. In particular:
The set of possible seeds is typically rather small; for example, the seed may be a 32-bit integer, meaning that an attacker only needs to test 232 different seeds to find the correct one. On a modern computer, this can often be done in a few seconds.
Worse yet, the initial seed is often chosen based on some fairly easily guessable value, such as the system time. If such a seed is used, and if you know roughly when a key was generated, you can narrow down the range of likely seed values significantly.
Even if the seed is chosen securely (i.e. completely unpredictably) from a set large enough to resists brute force guessing, the algorithms used to generate the pseudorandom outputs from the seed are typically not designed to withstand cryptanalytic attacks. Thus, for many commonly used algorithms, the seed can be fairly efficiently reconstructed from the outputs.
The first two attacks above only require that the attacker has access to some ciphertext, and can try to decrypt it with keys based on different seeds and see for which seed the resulting decrypted data makes sense. The last attack generally requires a bit more, namely that the attacker must know at least part of the keystream — but if the attacker has the ciphertext and can guess part of the plaintext, they can easily obtain the corresponding parts of the keystream.
Of course, if you created your key based on a pseudorandom number generator that did not have any of these weaknesses, then the scheme you describe would be secure. Indeed, that is pretty much exactly what a synchronous stream cipher is.