Make exception handling more consistent and robust throughout codebase
In general there are lots of places in the code where the error handling could be tidied up for the sake of both readability and robustness. We should avoid catching all exceptions (i.e. except Exception) and instead just catch the ones that should actually be thrown by the code the try/except block is covering, otherwise you risk masking bugs.
We could consider adding some bespoke error types, e.g. an AutocorpusParsingError. We can then try, catch and raise Python's inbuilt error types (e.g. KeyError) for the code that might fail, so we can distinguish between expected and unexpected errors (the latter being bugs in the code). We could add a FatalAutocorpusError to signal that the program should end with an error message without having to globally catch all errors in the code.
Marking as on hold until we do #134 as it'll probably conflict with that work.