It's clear that you have to supply the characters with the correct catcodes, which can be done by the following ways:
The proper way: Lookup the package's documentation to see if there's any command that works without changing catcode.
For verbatim there's \texttt, for pythontex there's \py and \pyc, for listings there's \lstinline.
The table header seems to be added correctly if the index is added to the table:
df_tex = df.to_latex(index=True, escape=False)
But then the index is shown which may not be desired.
If this is the case, one option would be to set the index to empty
strings and then remove the index column.
n_rows, n_cols = df.shape
df = df.set_index([[""] * n_rows]...
The problem seems to be that the LaTeX code generated by Python is inside a TeX group. Fancyhdr now does local changes by default, so these will not take effect. However you can make them global with the following trick (\globaldefs=1). The extra pair of braces in the code is to prevent the \globaldefs to affect other pycode.