Read the tables

JavaScript (ES6), 108

t=>(S=s=>s.split(/ *\| */),t=t.split`
`,[y,x]=t.pop().split` `,S(t.find(r=>S(r)[0]==y))[S(t[0]).indexOf(x)])

TEST in Firefox

 S=s=>s.split(/ *\| */),
 [y,x]=t.pop().split` `,

function test(){
#T { width: 50%; height: 9em}
Input<br><textarea id=T>   |a|z |_*|  ab  |
atb|1|85|22| 5    |
b  |5|6 |e$|  8   |
/+-|8|we|th| 126  |
atb ab</textarea><br>
<button onclick="test()">Find</button>
<span id=O></span>

Haskell, 117 116 111 bytes

import Data.Lists
s=splitOn"|".filter(>' ')
(t#b)a|l<-lines t=[c|r<-l,(d,c)<-zip(s$l!!0)$s r,d==a,s r!!0==b]!!0

Usage example:

*Main> ("  | x| b |\n----------\nab|l |mmm|\nb |le| l |\nb b" # "b") "b"

How it works:

s=splitOn"|".filter(>' ')         -- helper function to remove spaces and split a
                                  -- line at bars into words
l<-lines t                        -- split table at \n into lines and bind to l
[c|r<-l,                      ]   -- take c for every line r in l, where
       (d,c)<-zip(s$l!!0)$s r     -- a pair (d,c) is made by zipping the (split)
                                  -- header of the table with the (split) line r 
        ,d==a                     -- and d (=header element) equals parameter a
        ,s r!!0==b                -- and the first word in r equals parameter b
                             !!0  -- pick the first (and only) element

Retina, 90 bytes

s`^(?=.*\n(.*) (.*))((?<a>\|)|.)*\|\s*\2\s*\|.*\n\1\s*((?<-a>\|)|[^|])*\|\s*([^\s|]*).*

My first balancing group regex. It should be still well golfable. Will try to do it later.

The main idea is counting the pipes until the column name and then using the same amount of pipes up in the row starting with the desired row's name. After that we capture the next value which is the result.

Try it online here.