Parse one table out of CSV with multiple titled tables with the Python CSV module
In general this code look very nice and seems to follow style guidelines well. I found your code easy to follow.  You asked for:


  ... suggestions to make the code more compact, idiomatic, and fit for my goals.


I will start with a couple of points for compact and idiomatic, and then add something a bit bigger picture.

Only need one stop iteration:

Your main loop uses this construct twice:

try:
    ....
except StopIteration:
    break


But the net result of these is identical, and the inner one could simply be removed.

Acccessing Dunders is generally uneeded:

In several places your code has something like:

curr = reader.__next__()


The idiomatic way to do this is:

curr = next(reader)


The __next__ (dunder next) method is there because that is how an object publishes its interface for next().  And while you can access this, you probably shouldn't.    

In addition your entire loop is using next().  I suggest that this likely indicates your iteration structure could use some reworking.  Looking for a more natural iteration element may allow the code to be simplified.

Use natural record breaks:

Now something a little more substantive.  

The data structure has a natural break between records as an empty line.  By noting the presence of this empty line in the parsing logic, the parsing logic can get quite a bit simpler. In your code you need to have:

prev = curr


Which is needed to reach back a line to find the category name.  But that same category was already marked via a blank line before.

I started to recast your code as an example of how to take advantage of that blank line, and found it just as easy to read in the entire file. The example below does not preserve the logic of looking for category heads of:

 ['id', ' book', ' pages']


But is was not super clear why in your requirements this was important.  But adding that back into the code below, or taking the resulting data and then finding that record in said data, could also be done.

Recast Parsing Code:

def read_my_csv(filename):
    category = None
    header = None
    record_data = {}
    table = []

    def make_record():
        return [dict(zip(header, x)) for x in table]

    with open(filename, 'rU') as csv_data:
        reader = csv.reader(csv_data)

        for row in reader:
            if not row:
                if category:
                    record_data[category] = make_record()
                table = []
                category = next(reader)[0]
                header = tuple(x.strip() for x in next(reader))
            else:
                table.append([x.strip() for x in row])

    if category:
        record_data[category] = make_record()
    return record_data


Test Code:

import csv
data = read_my_csv('csvfile.csv')
for item in data.items():
    print(item[0])
    for records in item[1]:
        for record in records.items():
            print('   {}'.format(record))
        print()


Results from recast:

Manhattan Produce Market
   ('id', '1')
   ('fruit', 'orange')
   ('color', 'orange')

   ('id', '2')
   ('fruit', 'apple')
   ('color', 'red')

Books
   ('id', '1')
   ('book', 'Websterâ€™s Dictionary')
   ('pages', '1000')

   ('id', '2')
   ('book', 'Tony the Towtruck')
   ('pages', '20')

   ('id', '3')
   ('book', 'The Twelfth Night')
   ('pages', '144')

Rope
   ('id', '1')
   ('rope', 'hemp')
   ('length', '12-feet')
   ('diameter', '.5')
   ('color', 'green')

   ('id', '2')
   ('rope', 'sisal')
   ('length', '50-feet')
   ('diameter', '.125')
   ('color', 'brown')

Kings County Candy
   ('id', '1')
   ('flavor', 'grape')
   ('color', 'purple')
   ('bigleague', 'yes')

   ('id', '2')
   ('flavor', 'mega mango')
   ('color', 'yellow-orange')
   ('bigleague', 'no')