The tricky part is to distinguish between requests from the botnet, and requests from humans who actually want to read the pages. There is no 100% reliable way to do that in all generality, because some DDoS tools are quite good at imitating humans, and also because most humans appear to be quite good at behaving like mindless drones. Nevertheless, that's the gist of the issue: you must find a distinguishing characteristic between botnet requests and non-botnet requests (and if you find one, next month's botnet will have adapted -- this is an endless race).
One possible defence is to require a Proof of Work from clients. Embed in your site some Javascript code which performs a relatively complex computation, the output of which being embedded in the next request from that client. If done properly, then you could make sure that the botnet burned at least some substantial amount of CPU in the process; the idea being that if DDoSing your server is too expensive, then the botnet master will direct his wrath to another target. After all, he is a businessman...
I have not seen PoW deployed in a Web context, and the Javascript idiosyncrasies may make it difficult to do (a browser with a Javascript interpretor will not make that much "work" per second), but the concept seems sound.