The best way is SectionTable[]. SectionTable (part of PE header) has PointerToRawData field. Code section (.text/.code/CODE32) doesn’t always follow strictly the SectionTables array.
Maybe you have a chance to skip FileHeader and OptionalHeader (if you know PE32/+ magic word).
In another angle:
You need to know
required architecture
file alignment (set in PE header)
For raw disassembling a binary. You can find POP or MOV instructions (ex. for IA32e) and this way to determine the start of code.
It seems to me that this way is very unsafe or at least very inaccurate in most cases.