When importing multiple connected objects, it's critical that you retain the success files that Data Loader emits. These allow you to map from old Ids to new Ids as you're populating lookup fields in downstream objects.
For example, you'd load Accounts, and get back an Account success file. Then, in your Opportunities input file, you'd use an Excel construct like
INDEX()/MATCH() (preferable) to map from the old Account Id in your
Opportunity.AccountId column to the corresponding new Id in the data you just loaded.
I always do this by maintaining a separate Excel sheet with just two columns: old Id on the left, new Id on the right. I accumulate that data from all of my success files so that I can map any old Id just by looking it up in that table, which you'll need if you're working with polymorphic lookups like
Using the success files to map old Id to new Id obviates the need for External Ids, although you can also use that approach. If you use External Ids, you may need to change your schema; each parent object would have to have an External Id field, whose value you have present on your child object rows in CSV. You'd then perform an Upsert operation rather than an insert and have Data Loader use the External Id field to select the parent relationship.
Disclaimer: I am the author of this free and open source tool.
Amaxa (documentation) is a multi-object data loader I wrote. Using a simple YAML definition file, it can load up connected objects in a single operation with no manual mapping required. The definition for an Accounts-and-Opportunities load would look something like this:
This would load (or extract) Accounts first, and then Opportunities, and maintain the relationships between them.