>>105585282proof of work is really the only way, and it will only make such scraping comutationally infeasible in large amounts.
to implement on the frontend:
- implement SHA512, Whirlpool, or whatever other cryptographic hashing algorithm (preferably not in JS, but in Webassembly or using a browser's native cryptographic API, because JS is not fast enough and dedicated attackers will be using a native implementation).
to implement on the backend, for each request:
- use a PRNG such as ChaCha20 to generate a number between 0 and N, where you set N based on the level of difficulty you want (higher = slower requests).
- hash the generated number using whatever hashing algorithm you are using on the frontend.
- create a MAC of the hash using whatever MAC algorithm (I like Whirlpool HMAC). use a secret key that you change on something like an hourly basis.
- send N, hash, and the MAC to the client.
the client will have to find the number that produces the hash. when found, it will send the number, the hash, and the MAC back to the server. the server will validate the hash using the MAC, then validate the number produces the hash.