# PDF Attachment Extraction from Multipart/Mixed Emails Airline ticket receipts (Copa, American, United, Delta) often embed PDF attachments inside `multipart/mixed` MIME messages alongside inline images (logos, QR codes). ## Key pattern The PDF is typically the **last meaningful attachment** in the MIME tree, surrounded by 6-8 `image/jpeg` inline images. Don't assume the first `.pdf` match is the right one — iterate all parts and pick the one with the correct filename. ```python for part in raw.walk(): fn = part.get_filename() if fn and fn.endswith('.pdf'): payload = part.get_payload(decode=True) # Save to disk with open("/tmp/ticket.pdf", 'wb') as f: f.write(payload) # Or parse directly from memory from pdfminer.high_level import extract_text from io import BytesIO text = extract_text(BytesIO(payload)) ``` ## Parsing e-ticket PDFs with pdfminer Install: `pip3 install pdfminer.six` ```python from pdfminer.high_level import extract_text from io import BytesIO text = extract_text(BytesIO(pdf_bytes)) # Extract flight numbers (Copa uses CM###) for line in text.split('\n'): if 'Flight Number' in line or 'CM ' in line: flights.append(line.strip()) ``` ## Common airline PDF fields | Airline | Flight prefix | Confirmation length | PDF attachment name | |---|---|---|---| | Copa | CM | 6 alphanumeric (A4GSFD) | ETKT_*.pdf | | American | AA | 6 alphanumeric | e-ticket-*.pdf | | United | UA | 6 alphanumeric | itinerary-*.pdf | | Delta | DL | 6 alphanumeric | eTicket*.pdf | ## Pitfalls - **Copa tickets have 6-8 inline image parts** (logo, QR, barcode, social icons) before the PDF. The PDF is typically the second-to-last or last attachment. - **`get_content_charset()` may return None** on PDFs — use `utf-8` or `iso-8859-1` fallback for the PDF parsing step (the PDF itself is binary, the charset applies to the text portion of the email, not the attachment). - **Copa confirmation codes look like A4GSFD** — 6 chars, alphanumeric. Found in the email subject line and the PDF body. Also stored in the email's `X-Confirmation` header (if present). - **Header-only fetch** (`BODY.PEEK[HEADER.FIELDS]`) won't show attachment filenames. You need to fetch the full message body to find PDFs. - **Some airlines use `Content-Type: application/octet-stream`** instead of `application/pdf` for PDF attachments. Check `get_filename()` regardless of content type.