рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛрдордзреНрдпреЗ рдИрдореЗрд▓рд╡рд░реВрди рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА ETL рдкреНрд░рдХреНрд░рд┐рдпрд╛

рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛрдордзреНрдпреЗ рдИрдореЗрд▓рд╡рд░реВрди рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА ETL рдкреНрд░рдХреНрд░рд┐рдпрд╛

рддрдВрддреНрд░рдЬреНрдЮрд╛рди рдХрд┐рддреАрд╣реА рд╡рд┐рдХрд╕рд┐рдд рд╣реЛрдд рдЕрд╕рд▓реЗ рддрд░реА рдХрд╛рд▓рдмрд╛рд╣реНрдп рдкрдзреНрджрддреАрдВрдЪрд╛ рдПрдХ рд╕рд┐рд▓рд╕рд┐рд▓рд╛ рдиреЗрд╣рдореА рд╡рд┐рдХрд╛рд╕рд╛рдЪреНрдпрд╛ рдорд╛рдЧреЗ рдЕрд╕рддреЛ. рд╣реЗ рдЧреБрд│рдЧреБрд│реАрдд рд╕рдВрдХреНрд░рдордг, рдорд╛рдирд╡реА рдШрдЯрдХ, рддрд╛рдВрддреНрд░рд┐рдХ рдЧрд░рдЬрд╛ рдХрд┐рдВрд╡рд╛ рдЗрддрд░ рдХрд╢рд╛рдореБрд│реЗ рдЕрд╕реВ рд╢рдХрддреЗ. рдбреЗрдЯрд╛ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧрдЪреНрдпрд╛ рдХреНрд╖реЗрддреНрд░рд╛рдд, рдбреЗрдЯрд╛ рд╕реНрд░реЛрдд рдпрд╛ рднрд╛рдЧрд╛рдд рд╕рд░реНрд╡рд╛рдд рдЬрд╛рд╕реНрдд рдкреНрд░рдХрдЯ рд╣реЛрддрд╛рдд. рдЖрдкрдг рдпрд╛рдкрд╛рд╕реВрди рдореБрдХреНрдд рд╣реЛрдгреНрдпрд╛рдЪреЗ рдХрд┐рддреАрд╣реА рд╕реНрд╡рдкреНрди рдкрд╛рд╣рдд рдЕрд╕рд▓реЛ рддрд░реА, рдкрд░рдВрддреБ рдЖрддрд╛рдкрд░реНрдпрдВрдд рдбреЗрдЯрд╛рдЪрд╛ рдХрд╛рд╣реА рднрд╛рдЧ рдЗрдиреНрд╕реНрдЯрдВрдЯ рдореЗрд╕реЗрдВрдЬрд░ рдЖрдгрд┐ рдИрдореЗрд▓рдордзреНрдпреЗ рдкрд╛рдард╡рд┐рд▓рд╛ рдЬрд╛рддреЛ, рдЕрдзрд┐рдХ рдкреБрд░рд╛рддрди рд╕реНрд╡рд░реВрдкрд╛рдВрдЪрд╛ рдЙрд▓реНрд▓реЗрдЦ рдирд╛рд╣реА. рдореА рддреБрдореНрд╣рд╛рд▓рд╛ Apache Airflow рд╕рд╛рдареА рдкрд░реНрдпрд╛рдпрд╛рдВрдкреИрдХреА рдПрдХ рд╡реЗрдЧрд│реЗ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдордВрддреНрд░рд┐рдд рдХрд░рддреЛ, рддреБрдореНрд╣реА рдИрдореЗрд▓рдордзреВрди рдбреЗрдЯрд╛ рдХрд╕рд╛ рдШреЗрдК рд╢рдХрддрд╛ рд╣реЗ рд╕реНрдкрд╖реНрдЯ рдХрд░реВрди.

prehistory

рдЖрдВрддрд░рд╡реИрдпрдХреНрддрд┐рдХ рд╕рдВрдкреНрд░реЗрд╖рдгрд╛рдВрдкрд╛рд╕реВрди рдХрдВрдкрдиреНрдпрд╛рдВрдордзреАрд▓ рдкрд░рд╕реНрдкрд░рд╕рдВрд╡рд╛рджрд╛рдЪреНрдпрд╛ рдорд╛рдирдХрд╛рдВрдкрд░реНрдпрдВрдд рдмрд░рд╛рдЪ рдбреЗрдЯрд╛ рдЕрдЬреВрдирд╣реА рдИ-рдореЗрд▓рджреНрд╡рд╛рд░реЗ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХреЗрд▓рд╛ рдЬрд╛рддреЛ. рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА рдЗрдВрдЯрд░рдлреЗрд╕ рд▓рд┐рд╣рд┐рдгреЗ рдХрд┐рдВрд╡рд╛ рдЕрдзрд┐рдХ рд╕реЛрдпреАрд╕реНрдХрд░ рд╕реНрддреНрд░реЛрддрд╛рдВрдордзреНрдпреЗ рд╣реА рдорд╛рд╣рд┐рддреА рдкреНрд░рд╡рд┐рд╖реНрдЯ рдХрд░рдгрд╛рд░реНтАНрдпрд╛ рд▓реЛрдХрд╛рдВрдирд╛ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдд рдареЗрд╡рдгреЗ рд╢рдХреНрдп рдЕрд╕рд▓реНрдпрд╛рд╕ рдЪрд╛рдВрдЧрд▓реЗ рдЖрд╣реЗ, рдкрд░рдВрддреБ рдмрд░реНтАНрдпрд╛рдЪрджрд╛ рд╣реЗ рд╢рдХреНрдп рд╣реЛрдгрд╛рд░ рдирд╛рд╣реА. рдорд▓рд╛ рдЬреНрдпрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдЪрд╛ рд╕рд╛рдордирд╛ рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧрд▓рд╛ рддреЗ рдХреБрдЦреНрдпрд╛рдд CRM рдкреНрд░рдгрд╛рд▓реАрд▓рд╛ рдбреЗрдЯрд╛ рд╡реЗрдЕрд░рд╣рд╛рдКрд╕рд╢реА рдЖрдгрд┐ рдирдВрддрд░ OLAP рдкреНрд░рдгрд╛рд▓реАрд╢реА рдЬреЛрдбрдгреЗ. рд╣реЗ рдРрддрд┐рд╣рд╛рд╕рд┐рдХрджреГрд╖реНрдЯреНрдпрд╛ рдЕрд╕реЗ рдШрдбрд▓реЗ рдХреА рдЖрдордЪреНрдпрд╛ рдХрдВрдкрдиреАрд╕рд╛рдареА рдпрд╛ рдкреНрд░рдгрд╛рд▓реАрдЪрд╛ рд╡рд╛рдкрд░ рд╡реНрдпрд╡рд╕рд╛рдпрд╛рдЪреНрдпрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╖реЗрддреНрд░рд╛рдд рд╕реЛрдпреАрд╕реНрдХрд░ рд╣реЛрддрд╛. рдореНрд╣рдгреВрди, рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдЦрд░реЛрдЦрд░рдЪ рдпрд╛ рддреГрддреАрдп-рдкрдХреНрд╖ рдкреНрд░рдгрд╛рд▓реАрдЪреНрдпрд╛ рдбреЗрдЯрд╛рд╕рд╣ рдХрд╛рд░реНрдп рдХрд░рдгреНрдпрд╛рд╕ рд╕рдХреНрд╖рдо рд╡реНрд╣рд╛рдпрдЪреЗ рд╣реЛрддреЗ. рд╕рд░реНрд╡ рдкреНрд░рдердо, рдЕрд░реНрдерд╛рддрдЪ, рдЦреБрд▓реНрдпрд╛ API рд╡рд░реВрди рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рдЪреНрдпрд╛ рд╢рдХреНрдпрддреЗрдЪрд╛ рдЕрднреНрдпрд╛рд╕ рдХреЗрд▓рд╛ рдЧреЗрд▓рд╛. рджреБрд░реНрджреИрд╡рд╛рдиреЗ, API рдиреЗ рд╕рд░реНрд╡ рдЖрд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ рдорд┐рд│рд╡рдгреЗ рдХрд╡реНрд╣рд░ рдХреЗрд▓реЗ рдирд╛рд╣реА, рдЖрдгрд┐, рд╕реЛрдкреНрдпрд╛ рднрд╛рд╖реЗрдд, рддреЗ рдЕрдиреЗрдХ рдорд╛рд░реНрдЧрд╛рдВрдиреА рдХреБрдЯрд┐рд▓ рд╣реЛрддреЗ, рдЖрдгрд┐ рдЕрдзрд┐рдХ рд╡реНрдпрд╛рдкрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдкреНрд░рджрд╛рди рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддрд╛рдВрддреНрд░рд┐рдХ рд╕рдорд░реНрдерди рдирдХреЛ рд╣реЛрддреЗ рдХрд┐рдВрд╡рд╛ рдЕрд░реНрдзреНрдпрд╛ рдорд╛рд░реНрдЧрд╛рдиреЗ рдкреВрд░реНрдг рдХрд░реВ рд╢рдХрдд рдирд╡реНрд╣рддреЗ. рдкрд░рдВрддреБ рдпрд╛ рдкреНрд░рдгрд╛рд▓реАрдиреЗ рд╕рдВрдЧреНрд░рд╣рдг рдЕрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рджреБрд╡реНрдпрд╛рдЪреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд рдореЗрд▓рджреНрд╡рд╛рд░реЗ рдЧрд╣рд╛рд│ рдбреЗрдЯрд╛ рд╡реЗрд│реЛрд╡реЗрд│реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдгреНрдпрд╛рдЪреА рд╕рдВрдзреА рдкреНрд░рджрд╛рди рдХреЗрд▓реА.

рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреНрдпрд╛рд╡реЗ рдХреА рд╣реЗ рдПрдХрдореЗрд╡ рдкреНрд░рдХрд░рдг рдирд╡реНрд╣рддреЗ рдЬреНрдпрд╛рдордзреНрдпреЗ рд╡реНрдпрд╡рд╕рд╛рдп рдИрдореЗрд▓ рдХрд┐рдВрд╡рд╛ рдЗрдиреНрд╕реНрдЯрдВрдЯ рдореЗрд╕реЗрдВрдЬрд░рдХрдбреВрди рдбреЗрдЯрд╛ рдЧреЛрд│рд╛ рдХрд░реВ рдЗрдЪреНрдЫрд┐рдд рд╣реЛрддрд╛. рддрдерд╛рдкрд┐, рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд, рдЖрдореНрд╣реА рдХреЗрд╡рд│ рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░реЗ рдбреЗрдЯрд╛рдЪрд╛ рднрд╛рдЧ рдкреНрд░рджрд╛рди рдХрд░рдгрд╛рд░реНтАНрдпрд╛ рддреГрддреАрдп-рдкрдХреНрд╖ рдХрдВрдкрдиреАрд╡рд░ рдкреНрд░рднрд╛рд╡ рдЯрд╛рдХреВ рд╢рдХрдд рдирд╛рд╣реА.

рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛ

ETL рдкреНрд░рдХреНрд░рд┐рдпрд╛ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рдмрд╣реБрддреЗрдХрджрд╛ Apache Airflow рд╡рд╛рдкрд░рддреЛ. рдпрд╛ рддрдВрддреНрд░рдЬреНрдЮрд╛рдирд╛рд╢реА рдЕрдкрд░рд┐рдЪрд┐рдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╡рд╛рдЪрдХрд╛рд▓рд╛ рддреЗ рд╕рдВрджрд░реНрдн рдЖрдгрд┐ рд╕рд░реНрд╡рд╕рд╛рдзрд╛рд░рдгрдкрдгреЗ рдХрд╕реЗ рджрд┐рд╕рддреЗ рд╣реЗ рдЪрд╛рдВрдЧрд▓реНрдпрд╛ рдкреНрд░рдХрд╛рд░реЗ рд╕рдордЬреВрди рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА, рдореА рдХрд╛рд╣реА рдкреНрд░рд╛рд╕реНрддрд╛рд╡рд┐рдХрд╛рдВрдЪреЗ рд╡рд░реНрдгрди рдХрд░реЗрди.

Apache Airflow рд╣реЗ рдПрдХ рд╡рд┐рдирд╛рдореВрд▓реНрдп рдкреНрд▓реЕрдЯрдлреЙрд░реНрдо рдЖрд╣реЗ рдЬреЗ Python рдордзреНрдпреЗ ETL (Extract-Transform-Loading) рдкреНрд░рдХреНрд░рд┐рдпрд╛ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рдореЙрдирд┐рдЯрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ. рдПрдЕрд░рдлреНрд▓реЛрдордзрд▓реА рдореБрдЦреНрдп рд╕рдВрдХрд▓реНрдкрдирд╛ рд╣реА рдПрдХ рдирд┐рд░реНрджреЗрд╢рд┐рдд рдЕреЕрд╕рд╛рдпрдХреНрд▓рд┐рдХ рдЖрд▓реЗрдЦ рдЖрд╣реЗ, рдЬрд┐рдереЗ рдЖрд▓реЗрдЦрд╛рдЪреЗ рд╢рд┐рд░реЛрдмрд┐рдВрджреВ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдЖрд╣реЗрдд рдЖрдгрд┐ рдЖрд▓реЗрдЦрд╛рдЪреНрдпрд╛ рдХрдбрд╛ рдореНрд╣рдгрдЬреЗ рдирд┐рдпрдВрддреНрд░рдг рдХрд┐рдВрд╡рд╛ рдорд╛рд╣рд┐рддреАрдЪрд╛ рдкреНрд░рд╡рд╛рд╣. рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА рдкрд╛рдпрдерди рдлрдВрдХреНрд╢рдирд▓рд╛ рдлрдХреНрдд рдХреЙрд▓ рдХрд░реВ рд╢рдХрддреЗ рдХрд┐рдВрд╡рд╛ рдХреНрд▓рд╛рд╕рдЪреНрдпрд╛ рд╕рдВрджрд░реНрднрд╛рдд рдЕрдиреЗрдХ рдлрдВрдХреНрд╢рдиреНрд╕рд▓рд╛ рдХреНрд░рдорд╛рдиреЗ рдХреЙрд▓ рдХрд░рдгреНрдпрд╛рдкрд╛рд╕реВрди рддреЗ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рддрд░реНрдХ рдЕрд╕реВ рд╢рдХрддреЗ. рд╕рд░реНрд╡рд╛рдд рд╡рд╛рд░рдВрд╡рд╛рд░ рдСрдкрд░реЗрд╢рдиреНрд╕рд╕рд╛рдареА, рдЖрдзреАрдкрд╛рд╕реВрдирдЪ рдЕрдиреЗрдХ рддрдпрд╛рд░-рддрдпрд╛рд░ рдШрдбрд╛рдореЛрдбреА рдЖрд╣реЗрдд рдЬреНрдпрд╛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореНрд╣рдгреВрди рд╡рд╛рдкрд░рд▓реНрдпрд╛ рдЬрд╛рдК рд╢рдХрддрд╛рдд. рдЕрд╢рд╛ рд╡рд┐рдХрд╛рд╕рд╛рдВрдордзреНрдпреЗ рд╣реЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЖрд╣реЗ:

  • рдСрдкрд░реЗрдЯрд░ - рдПрдХрд╛ рдард┐рдХрд╛рдгрд╛рд╣реВрди рджреБрд╕рд▒реНрдпрд╛ рдард┐рдХрд╛рдгреА рдбреЗрдЯрд╛ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдбреЗрдЯрд╛рдмреЗрд╕ рдЯреЗрдмрд▓рд╡рд░реВрди рдбреЗрдЯрд╛ рд╡реЗрдЕрд░рд╣рд╛рдКрд╕рдордзреНрдпреЗ;
  • рд╕реЗрдиреНрд╕рд░реНрд╕ - рдПрдЦрд╛рджреНрдпрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдШрдЯрдиреЗрдЪреНрдпрд╛ рдШрдЯрдиреЗрдЪреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рдЖрд▓реЗрдЦрд╛рдЪреНрдпрд╛ рдкреБрдвреАрд▓ рд╢рд┐рд░реЛрдмрд┐рдВрджреВрдВрдХрдбреЗ рдирд┐рдпрдВрддреНрд░рдгрд╛рдЪрд╛ рдкреНрд░рд╡рд╛рд╣ рдирд┐рд░реНрджреЗрд╢рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА;
  • рд╣реБрдХ - рд▓реЛрдЕрд░ рд▓реЗрд╡реНрд╣рд▓ рдСрдкрд░реЗрд╢рдиреНрд╕рд╕рд╛рдареА, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдбреЗрдЯрд╛рдмреЗрд╕ рдЯреЗрдмрд▓рдордзреВрди рдбреЗрдЯрд╛ рдорд┐рд│рд╡рдгреНрдпрд╛рд╕рд╛рдареА (рд╕реНрдЯреЗрдЯрдореЗрдВрдЯрдордзреНрдпреЗ рд╡рд╛рдкрд░рд▓реЗрд▓реЗ);
  • рдЖрдгрд┐ рдпрд╛рдкреНрд░рдорд╛рдгреЗ.

рдпрд╛ рд▓реЗрдЦрд╛рдд рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛрдЪреЗ рддрдкрд╢реАрд▓рд╡рд╛рд░ рд╡рд░реНрдгрди рдХрд░рдгреЗ рдЕрдпреЛрдЧреНрдп рдард░реЗрд▓. рд╕рдВрдХреНрд╖рд┐рдкреНрдд рдкрд░рд┐рдЪрдп рдкрд╛рд╣рддрд╛ рдпреЗрдИрд▓ рдпреЗрдереЗ рдХрд┐рдВрд╡рд╛ рдпреЗрдереЗ.

рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА рд╣реБрдХ

рд╕рд░реНрд╡ рдкреНрд░рдердо, рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣рд╛рд▓рд╛ рдПрдХ рд╣реБрдХ рд▓рд┐рд╣рд┐рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдЬреНрдпрд╛рджреНрд╡рд╛рд░реЗ рдЖрдореНрд╣реА рд╣реЗ рдХрд░реВ рд╢рдХрддреЛ:

  • рдИрдореЗрд▓рд╢реА рдХрдиреЗрдХреНрдЯ рдХрд░рд╛
  • рдпреЛрдЧреНрдп рдкрддреНрд░ рд╢реЛрдзрд╛
  • рдкрддреНрд░рд╛рддреВрди рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рд╛.

from airflow.hooks.base_hook import BaseHook
import imaplib
import logging

class IMAPHook(BaseHook):
    def __init__(self, imap_conn_id):
        """
           IMAP hook ╨┤╨╗╤П ╨┐╨╛╨╗╤Г╤З╨╡╨╜╨╕╤П ╨┤╨░╨╜╨╜╤Л╤Е ╤Б ╤Н╨╗╨╡╨║╤В╤А╨╛╨╜╨╜╨╛╨╣ ╨┐╨╛╤З╤В╤Л

           :param imap_conn_id:       ╨Ш╨┤╨╡╨╜╤В╨╕╤Д╨╕╨║╨░╤В╨╛╤А ╨┐╨╛╨┤╨║╨╗╤О╤З╨╡╨╜╨╕╤П ╨║ ╨┐╨╛╤З╤В╨╡
           :type imap_conn_id:        string
        """
        self.connection = self.get_connection(imap_conn_id)
        self.mail = None

    def authenticate(self):
        """ 
            ╨Я╨╛╨┤╨║╨╗╤О╤З╨░╨╡╨╝╤Б╤П ╨║ ╨┐╨╛╤З╤В╨╡
        """
        mail = imaplib.IMAP4_SSL(self.connection.host)
        response, detail = mail.login(user=self.connection.login, password=self.connection.password)
        if response != "OK":
            raise AirflowException("Sign in failed")
        else:
            self.mail = mail

    def get_last_mail(self, check_seen=True, box="INBOX", condition="(UNSEEN)"):
        """
            ╨Ь╨╡╤В╨╛╨┤ ╨┤╨╗╤П ╨┐╨╛╨╗╤Г╤З╨╡╨╜╨╕╤П ╨╕╨┤╨╡╨╜╤В╨╕╤Д╨╕╨║╨░╤В╨╛╤А╨░ ╨┐╨╛╤Б╨╗╨╡╨┤╨╜╨╡╨│╨╛ ╨┐╨╕╤Б╤М╨╝╨░, 
            ╤Г╨┤╨╛╨▓╨╗╨╡╤В╨▓╨╛╤А╨░╤П╤О╤Й╨╡╨│╨╛ ╤Г╤Б╨╗╨╛╨▓╨╕╤П╨╝ ╨┐╨╛╨╕╤Б╨║╨░

            :param check_seen:      ╨Ю╤В╨╝╨╡╤З╨░╤В╤М ╨┐╨╛╤Б╨╗╨╡╨┤╨╜╨╡╨╡ ╨┐╨╕╤Б╤М╨╝╨╛ ╨║╨░╨║ ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╜╨╛╨╡
            :type check_seen:       bool
            :param box:             ╨Э╨░╨╕╨╝╨╡╨╜╨╛╨▓╨░╨╜╨╕╤П ╤П╤Й╨╕╨║╨░
            :type box:              string
            :param condition:       ╨г╤Б╨╗╨╛╨▓╨╕╤П ╨┐╨╛╨╕╤Б╨║╨░ ╨┐╨╕╤Б╨╡╨╝
            :type condition:        string
        """
        self.authenticate()
        self.mail.select(mailbox=box)
        response, data = self.mail.search(None, condition)
        mail_ids = data[0].split()
        logging.info("╨Т ╤П╤Й╨╕╨║╨╡ ╨╜╨░╨╣╨┤╨╡╨╜╤Л ╤Б╨╗╨╡╨┤╤Г╤О╤Й╨╕╨╡ ╨┐╨╕╤Б╤М╨╝╨░: " + str(mail_ids))

        if not mail_ids:
            logging.info("╨Э╨╡ ╨╜╨░╨╣╨┤╨╡╨╜╨╛ ╨╜╨╛╨▓╤Л╤Е ╨┐╨╕╤Б╨╡╨╝")
            return None

        mail_id = mail_ids[0]

        # ╨╡╤Б╨╗╨╕ ╤В╨░╨║╨╕╤Е ╨┐╨╕╤Б╨╡╨╝ ╨╜╨╡╤Б╨║╨╛╨╗╤М╨║╨╛
        if len(mail_ids) > 1:
            # ╨╛╤В╨╝╨╡╤З╨░╨╡╨╝ ╨╛╤Б╤В╨░╨╗╤М╨╜╤Л╨╡ ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╜╤Л╨╝╨╕
            for id in mail_ids:
                self.mail.store(id, "+FLAGS", "\Seen")

            # ╨▓╨╛╨╖╨▓╤А╨░╤Й╨░╨╡╨╝ ╨┐╨╛╤Б╨╗╨╡╨┤╨╜╨╡╨╡
            mail_id = mail_ids[-1]

        # ╨╜╤Г╨╢╨╜╨╛ ╨╗╨╕ ╨╛╤В╨╝╨╡╤В╨╕╤В╤М ╨┐╨╛╤Б╨╗╨╡╨┤╨╜╨╡╨╡ ╨┐╤А╨╛╤З╨╕╤В╨░╨╜╨╜╤Л╨╝
        if not check_seen:
            self.mail.store(mail_id, "-FLAGS", "\Seen")

        return mail_id

рддрд░реНрдХ рд╣реЗ рдЖрд╣реЗ: рдЖрдореНрд╣реА рдХрдиреЗрдХреНрдЯ рдХрд░рддреЛ, рд╢реЗрд╡рдЯрдЪреЗ рд╕рд░реНрд╡рд╛рдд рд╕рдВрдмрдВрдзрд┐рдд рдкрддреНрд░ рд╢реЛрдзрддреЛ, рдЬрд░ рдЗрддрд░ рдЕрд╕рддреАрд▓ рддрд░ рдЖрдореНрд╣реА рддреНрдпрд╛рдВрдЪреНрдпрд╛рдХрдбреЗ рджреБрд░реНрд▓рдХреНрд╖ рдХрд░рддреЛ. рд╣реЗ рдлрдВрдХреНрд╢рди рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ, рдХрд╛рд░рдг рдирдВрддрд░рдЪреНрдпрд╛ рдЕрдХреНрд╖рд░рд╛рдВрдордзреНрдпреЗ рдкреВрд░реНрд╡реАрдЪрд╛ рд╕рд░реНрд╡ рдбреЗрдЯрд╛ рдЕрд╕рддреЛ. рдЕрд╕реЗ рдирд╕рд▓реНрдпрд╛рд╕, рддреБрдореНрд╣реА рд╕рд░реНрд╡ рдЕрдХреНрд╖рд░рд╛рдВрдЪрд╛ рдЕреЕрд░реЗ рдкрд░рдд рдХрд░реВ рд╢рдХрддрд╛ рдХрд┐рдВрд╡рд╛ рдкрд╣рд┐рд▓реНрдпрд╛рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░реВ рд╢рдХрддрд╛ рдЖрдгрд┐ рдЙрд░реНрд╡рд░рд┐рдд рдкреБрдвреАрд▓ рдкрд╛рд╕рд╡рд░ рдХрд░реВ рд╢рдХрддрд╛. рд╕рд░реНрд╡рд╕рд╛рдзрд╛рд░рдгрдкрдгреЗ, рд╕рд░реНрд╡рдХрд╛рд╣реА, рдиреЗрд╣рдореАрдкреНрд░рдорд╛рдгреЗ, рдХрд╛рд░реНрдпрд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЗ.

рдЖрдореНрд╣реА рд╣реБрдХрдордзреНрдпреЗ рджреЛрди рд╕рд╣рд╛рдпрдХ рдХрд╛рд░реНрдпреЗ рдЬреЛрдбрддреЛ: рдлрд╛рдЗрд▓ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рдИрдореЗрд▓рд╡рд░реВрди рд▓рд┐рдВрдХ рд╡рд╛рдкрд░реВрди рдлрд╛рдЗрд▓ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рддрд╕реЗ, рддреЗ рдСрдкрд░реЗрдЯрд░рдХрдбреЗ рд╣рд▓рд╡рд┐рд▓реЗ рдЬрд╛рдК рд╢рдХрддрд╛рдд, рддреЗ рд╣реА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рд╡рд╛рд░рдВрд╡рд╛рд░рддреЗрд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЗ. рд╣реБрдХрдордзреНрдпреЗ рдЖрдгрдЦреА рдХрд╛рдп рдЬреЛрдбрд╛рдпрдЪреЗ рдЖрд╣реЗ, рдкреБрдиреНрд╣рд╛, рдХрд╛рд░реНрдпрд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЖрд╣реЗ: рдЬрд░ рдкрддреНрд░рд╛рдд рдлрд╛рдпрд▓реА рддреНрд╡рд░рд┐рдд рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реНрдпрд╛ рддрд░ рдЖрдкрдг рдкрддреНрд░рд╛рд╢реА рд╕рдВрд▓рдЧреНрдирдХ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реВ рд╢рдХрддрд╛, рдЬрд░ рдкрддреНрд░рд╛рдд рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓рд╛ рдЕрд╕реЗрд▓ рддрд░ рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рдкрддреНрд░ рд╡рд┐рд╢реНрд▓реЗрд╖рд┐рдд рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ, рдЗ. рдорд╛рдЭреНрдпрд╛ рдмрд╛рдмрддреАрдд, рдкрддреНрд░ рд╕рдВрдЧреНрд░рд╣рдгрд╛рдЪреНрдпрд╛ рдПрдХрд╛ рджреБрд╡реНрдпрд╛рд╕рд╣ рдпреЗрддреЗ, рдЬреЗ рдорд▓рд╛ рдПрдХрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдард┐рдХрд╛рдгреА рдареЗрд╡рдгреЗ рдЖрдгрд┐ рдкреБрдвреАрд▓ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реБрд░реВ рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ.

    def download_from_url(self, url, path, chunk_size=128):
        """
            ╨Ь╨╡╤В╨╛╨┤ ╨┤╨╗╤П ╤Б╨║╨░╤З╨╕╨▓╨░╨╜╨╕╤П ╤Д╨░╨╣╨╗╨░

            :param url:              ╨Р╨┤╤А╨╡╤Б ╨╖╨░╨│╤А╤Г╨╖╨║╨╕
            :type url:               string
            :param path:             ╨Ъ╤Г╨┤╨░ ╨┐╨╛╨╗╨╛╨╢╨╕╤В╤М ╤Д╨░╨╣╨╗
            :type path:              string
            :param chunk_size:       ╨Я╨╛ ╤Б╨║╨╛╨╗╤М╨║╨╛ ╨▒╨░╨╣╤В╨╛╨▓ ╨┐╨╕╤Б╨░╤В╤М
            :type chunk_size:        int
        """
        r = requests.get(url, stream=True)
        with open(path, "wb") as fd:
            for chunk in r.iter_content(chunk_size=chunk_size):
                fd.write(chunk)

    def download_mail_href_attachment(self, mail_id, path):
        """
            ╨Ь╨╡╤В╨╛╨┤ ╨┤╨╗╤П ╤Б╨║╨░╤З╨╕╨▓╨░╨╜╨╕╤П ╤Д╨░╨╣╨╗╨░ ╨┐╨╛ ╤Б╤Б╤Л╨╗╨║╨╡ ╨╕╨╖ ╨┐╨╕╤Б╤М╨╝╨░

            :param mail_id:         ╨Ш╨┤╨╡╨╜╤В╨╕╤Д╨╕╨║╨░╤В╨╛╤А ╨┐╨╕╤Б╤М╨╝╨░
            :type mail_id:          string
            :param path:            ╨Ъ╤Г╨┤╨░ ╨┐╨╛╨╗╨╛╨╢╨╕╤В╤М ╤Д╨░╨╣╨╗
            :type path:             string
        """
        response, data = self.mail.fetch(mail_id, "(RFC822)")
        raw_email = data[0][1]
        raw_soup = raw_email.decode().replace("r", "").replace("n", "")
        parse_soup = BeautifulSoup(raw_soup, "html.parser")
        link_text = ""

        for a in parse_soup.find_all("a", href=True, text=True):
            link_text = a["href"]

        self.download_from_url(link_text, path)

рдХреЛрдб рд╕реЛрдкрд╛ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рддреНрдпрд╛рд▓рд╛ рдЕрдзрд┐рдХ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдгрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╛рд╣реА. рдореА рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд imap_conn_id рдпрд╛ рдЬрд╛рджреВрдЪреНрдпрд╛ рдУрд│реАрдмрджреНрджрд▓ рд╕рд╛рдВрдЧреЗрди. Apache Airflow рдХрдиреЗрдХреНтАНрд╢рди рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕ (рд▓реЙрдЧрд┐рди, рдкрд╛рд╕рд╡рд░реНрдб, рдЕреЕрдбреНрд░реЗрд╕ рдЖрдгрд┐ рдЗрддрд░ рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕) рд╕реНрдЯреЛрдЕрд░ рдХрд░рддреЗ рдЬреНрдпрд╛рдд рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЖрдпрдбреЗрдВрдЯрд┐рдлрд╛рдпрд░рджреНрд╡рд╛рд░реЗ рдкреНрд░рд╡реЗрд╢ рдХреЗрд▓рд╛ рдЬрд╛рдК рд╢рдХрддреЛ. рджреГрд╢реНрдпрдорд╛рдирдкрдгреЗ, рдХрдиреЗрдХреНрд╢рди рд╡реНрдпрд╡рд╕реНрдерд╛рдкрди рдЕрд╕реЗ рджрд┐рд╕рддреЗ

рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛрдордзреНрдпреЗ рдИрдореЗрд▓рд╡рд░реВрди рдбреЗрдЯрд╛ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА ETL рдкреНрд░рдХреНрд░рд┐рдпрд╛

рдбреЗрдЯрд╛рдЪреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕реЗрдиреНрд╕рд░

рдЖрдореНрд╣рд╛рд▓рд╛ рдореЗрд▓рдордзреВрди рдбреЗрдЯрд╛ рдХрд╕рд╛ рдХрдиреЗрдХреНрдЯ рдХрд░рд╛рдпрдЪрд╛ рдЖрдгрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░рд╛рдпрдЪрд╛ рд╣реЗ рдЖрдзреАрдЪ рдорд╛рд╣рд┐рдд рдЕрд╕рд▓реНрдпрд╛рдиреЗ, рдЖрдореНрд╣реА рдЖрддрд╛ рддреНрдпрд╛рдВрдЪреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕реЗрдиреНрд╕рд░ рд▓рд┐рд╣реВ рд╢рдХрддреЛ. рдорд╛рдЭреНрдпрд╛ рдмрд╛рдмрддреАрдд, рдЖрддреНрддрд╛рдЪ рдПрдЦрд╛рджрд╛ рдСрдкрд░реЗрдЯрд░ рд▓рд┐рд╣рд┐рдгреЗ рдХрд╛рд░реНрдп рдХрд░рдд рдирд╛рд╣реА рдЬреЛ рдбреЗрдЯрд╛рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░реЗрд▓, рдЬрд░ рдЕрд╕реЗрд▓ рддрд░, рдХрд╛рд░рдг рдЗрддрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрд▓рдордзреВрди рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдбреЗрдЯрд╛рд╡рд░ рдЖрдзрд╛рд░рд┐рдд рдХрд╛рд░реНрдп рдХрд░рддрд╛рдд, рдЬреНрдпрд╛рдд рдЗрддрд░ рд╕реНрддреНрд░реЛрддрд╛рдВрдХрдбреВрди рд╕рдВрдмрдВрдзрд┐рдд рдбреЗрдЯрд╛ рдШреЗрддрд╛рдд (API, рдЯреЗрд▓рд┐рдлреЛрдиреА , рд╡реЗрдм рдореЗрдЯреНрд░рд┐рдХреНрд╕, рдЗ.) рдЗ.). рдореА рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреЗрддреЛ. CRM рдкреНрд░рдгрд╛рд▓реАрдордзреНрдпреЗ рдПрдХ рдирд╡реАрди рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рджрд┐рд╕рд▓рд╛ рдЖрд╣реЗ, рдЖрдгрд┐ рдЖрдореНрд╣рд╛рд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛ UUID рдмрджреНрджрд▓ рдЕрджреНрдпрд╛рдк рдорд╛рд╣рд┐рддреА рдирд╛рд╣реА. рддреНрдпрд╛рдирдВрддрд░, SIP рдЯреЗрд▓рд┐рдлреЛрдиреА рд╡рд░реВрди рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рддрд╛рдирд╛, рдЖрдореНрд╣рд╛рд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛ UUID рд╢реА рдЬреЛрдбрд▓реЗрд▓реЗ рдХреЙрд▓ рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреАрд▓, рдкрд░рдВрддреБ рдЖрдореНрд╣реА рддреЗ рдЬрддрди рдЖрдгрд┐ рдпреЛрдЧреНрдпрд░рд┐рддреНрдпрд╛ рд╡рд╛рдкрд░рдгреНрдпрд╛рд╕ рд╕рдХреНрд╖рдо рдЕрд╕рдгрд╛рд░ рдирд╛рд╣реА. рдЕрд╢рд╛ рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ, рдбреЗрдЯрд╛рдЪреЗ рдЕрд╡рд▓рдВрдмрд┐рддреНрд╡ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ, рд╡рд┐рд╢реЗрд╖рдд: рдЬрд░ рддреЗ рднрд┐рдиреНрди рд╕реНрддреНрд░реЛрддрд╛рдВрдХрдбреВрди рдЕрд╕рддреАрд▓. рд╣реЗ рдЕрд░реНрдерд╛рддрдЪ, рдбреЗрдЯрд╛ рдЕрдЦрдВрдбрддреЗрдЪреЗ рд╕рдВрд░рдХреНрд╖рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЕрдкреБрд░реЗ рдЙрдкрд╛рдп рдЖрд╣реЗрдд, рдкрд░рдВрддреБ рдХрд╛рд╣реА рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ рддреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗрдд. рд╣реЛрдп, рдЖрдгрд┐ рд╕рдВрд╕рд╛рдзрдиреЗ рд╡реНрдпрд╛рдкрдгреНрдпрд╛рд╕рд╛рдареА рдЖрд│рд╢реАрдкрдгрд╛ рджреЗрдЦреАрд▓ рддрд░реНрдХрд╣реАрди рдЖрд╣реЗ.

рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░реЗ, рдореЗрд▓рдордзреНрдпреЗ рдирд╡реАрди рдорд╛рд╣рд┐рддреА рдЕрд╕рд▓реНрдпрд╛рд╕ рдЖрдордЪрд╛ рд╕реЗрдиреНрд╕рд░ рдЖрд▓реЗрдЦрд╛рдЪреЗ рдкреБрдвреАрд▓ рд╢рд┐рд░реЛрдмрд┐рдВрджреВ рд▓рд╛рдБрдЪ рдХрд░реЗрд▓ рдЖрдгрд┐ рдорд╛рдЧреАрд▓ рдорд╛рд╣рд┐рддреАрд▓рд╛ рдЕрд╕рдВрдмрджреНрдз рдореНрд╣рдгреВрди рдЪрд┐рдиреНрд╣рд╛рдВрдХрд┐рдд рдХрд░реЗрд▓.

from airflow.sensors.base_sensor_operator import BaseSensorOperator
from airflow.utils.decorators import apply_defaults
from my_plugin.hooks.imap_hook import IMAPHook

class MailSensor(BaseSensorOperator):
    @apply_defaults
    def __init__(self, conn_id, check_seen=True, box="Inbox", condition="(UNSEEN)", *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.conn_id = conn_id
        self.check_seen = check_seen
        self.box = box
        self.condition = condition

    def poke(self, context):
        conn = IMAPHook(self.conn_id)
        mail_id = conn.get_last_mail(check_seen=self.check_seen, box=self.box, condition=self.condition)

        if mail_id is None:
            return False
        else:
            return True

рдЖрдореНрд╣реА рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЛ рдЖрдгрд┐ рд╡рд╛рдкрд░рддреЛ

рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдкрдг рд╕реНрд╡рддрдВрддреНрд░ рдСрдкрд░реЗрдЯрд░ рд▓рд┐рд╣реВ рд╢рдХрддрд╛, рдЖрдкрдг рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реЗ рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛. рдЖрддрд╛рдкрд░реНрдпрдВрдд рддрд░реНрдХ рдХреНрд╖реБрд▓реНрд▓рдХ рдЕрд╕рд▓реНрдпрд╛рдиреЗ - рдкрддреНрд░рд╛рддреВрди рдбреЗрдЯрд╛ рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдореА рдорд╛рдирдХ рдкрд╛рдпрдердирдСрдкрд░реЗрдЯрд░ рд╕реБрдЪрд╡рд┐рддреЛ

from airflow.models import DAG

from airflow.operators.python_operator import PythonOperator
from airflow.sensors.my_plugin import MailSensor
from my_plugin.hooks.imap_hook import IMAPHook

start_date = datetime(2020, 4, 4)

# ╨б╤В╨░╨╜╨┤╨░╤А╤В╨╜╨╛╨╡ ╨║╨╛╨╜╤Д╨╕╨│╤Г╤А╨╕╤А╨╛╨▓╨░╨╜╨╕╨╡ ╨│╤А╨░╤Д╨░
args = {
    "owner": "example",
    "start_date": start_date,
    "email": ["[email protected]"],
    "email_on_failure": False,
    "email_on_retry": False,
    "retry_delay": timedelta(minutes=15),
    "provide_context": False,
}

dag = DAG(
    dag_id="test_etl",
    default_args=args,
    schedule_interval="@hourly",
)

# ╨Ю╨┐╤А╨╡╨┤╨╡╨╗╤П╨╡╨╝ ╤Б╨╡╨╜╤Б╨╛╤А
mail_check_sensor = MailSensor(
    task_id="check_new_emails",
    poke_interval=10,
    conn_id="mail_conn_id",
    timeout=10,
    soft_fail=True,
    box="my_box",
    dag=dag,
    mode="poke",
)

# ╨д╤Г╨╜╨║╤Ж╨╕╤П ╨┤╨╗╤П ╨┐╨╛╨╗╤Г╤З╨╡╨╜╨╕╤П ╨┤╨░╨╜╨╜╤Л╤Е ╨╕╨╖ ╨┐╨╕╤Б╤М╨╝╨░
def prepare_mail():
    imap_hook = IMAPHook("mail_conn_id")
    mail_id = imap_hook.get_last_mail(check_seen=True, box="my_box")
    if mail_id is None:
        raise AirflowException("Empty mailbox")

    conn.download_mail_href_attachment(mail_id, "./path.zip")

prepare_mail_data = PythonOperator(task_id="prepare_mail_data", default_args=args, dag=dag, python_callable= prepare_mail)

# ╨Ю╨┐╨╕╤Б╨░╨╜╨╕╨╡ ╨╛╤Б╤В╨░╨╗╤М╨╜╤Л╤Е ╨▓╨╡╤А╤И╨╕╨╜ ╨│╤А╨░╤Д╨░
...

# ╨Ч╨░╨┤╨░╨╡╨╝ ╤Б╨▓╤П╨╖╤М ╨╜╨░ ╨│╤А╨░╤Д╨╡
mail_check_sensor >> prepare_mail_data
prepare_data >> ...
# ╨Ю╨┐╨╕╤Б╨░╨╜╨╕╨╡ ╨╛╤Б╤В╨░╨╗╤М╨╜╤Л╤Е ╨┐╨╛╤В╨╛╨║╨╛╨▓ ╤Г╨┐╤А╨░╨▓╨╗╨╡╨╜╨╕╤П

рддрд╕реЗ, рдЬрд░ рддреБрдордЪрд╛ рдХреЙрд░реНрдкреЛрд░реЗрдЯ рдореЗрд▓ рджреЗрдЦреАрд▓ mail.ru рд╡рд░ рдЕрд╕реЗрд▓, рддрд░ рддреБрдореНрд╣реА рд╡рд┐рд╖рдп, рдкреНрд░реЗрд╖рдХ рдЗрддреНрдпрд╛рджреАрдВрдиреБрд╕рд╛рд░ рдЕрдХреНрд╖рд░реЗ рд╢реЛрдзреВ рд╢рдХрдгрд╛рд░ рдирд╛рд╣реА. 2016 рдордзреНрдпреЗ, рддреНрдпрд╛рдВрдиреА рддреЗ рд╕рд╛рджрд░ рдХрд░рдгреНрдпрд╛рдЪреЗ рд╡рдЪрди рджрд┐рд▓реЗ рд╣реЛрддреЗ, рдкрд░рдВрддреБ рд╡рд░рд╡рд░ рдкрд╛рд╣рддрд╛ рддреНрдпрд╛рдВрдЪреЗ рд╡рд┐рдЪрд╛рд░ рдмрджрд▓рд▓реЗ. рдореА рдЖрд╡рд╢реНрдпрдХ рдЕрдХреНрд╖рд░рд╛рдВрд╕рд╛рдареА рд╕реНрд╡рддрдВрддреНрд░ рдлреЛрд▓реНрдбрд░ рддрдпрд╛рд░ рдХрд░реВрди рдЖрдгрд┐ рдореЗрд▓ рд╡реЗрдм рдЗрдВрдЯрд░рдлреЗрд╕рдордзреНрдпреЗ рдЖрд╡рд╢реНрдпрдХ рдЕрдХреНрд╖рд░рд╛рдВрд╕рд╛рдареА рдлрд┐рд▓реНрдЯрд░ рд╕реЗрдЯ рдХрд░реВрди рд╣реА рд╕рдорд╕реНрдпрд╛ рд╕реЛрдбрд╡рд▓реА. рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░реЗ, рд╢реЛрдзрд╛рд╕рд╛рдареА рдлрдХреНрдд рдЖрд╡рд╢реНрдпрдХ рдЕрдХреНрд╖рд░реЗ рдЖрдгрд┐ рдЕрдЯреА, рдорд╛рдЭреНрдпрд╛ рдмрд╛рдмрддреАрдд, рдлрдХреНрдд (рди рдкрд╛рд╣рд┐рд▓реЗрд▓реЗ) рдпрд╛ рдлреЛрд▓реНрдбрд░рдордзреНрдпреЗ рдпреЗрддрд╛рдд.

рд╕рд╛рд░рд╛рдВрд╢, рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЦрд╛рд▓реАрд▓ рдХреНрд░рдо рдЖрд╣реЗ: рдЖрдореНрд╣реА рдЕрдЯреА рдкреВрд░реНрдг рдХрд░рдгрд╛рд░реА рдирд╡реАрди рдЕрдХреНрд╖рд░реЗ рдЖрд╣реЗрдд рдХрд╛ рддреЗ рддрдкрд╛рд╕рддреЛ, рдЬрд░ рдЕрд╕рддреАрд▓ рддрд░ рдЖрдореНрд╣реА рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рдЕрдХреНрд╖рд░рд╛рддреАрд▓ рджреБрд╡рд╛ рд╡рд╛рдкрд░реВрди рд╕рдВрдЧреНрд░рд╣рдг рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рддреЛ.
рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рдмрд┐рдВрджреВрдВрдЦрд╛рд▓реА, рд╣реЗ рд╡рдЧрд│рд▓реЗ рдЖрд╣реЗ рдХреА рд╣реЗ рд╕рдВрдЧреНрд░рд╣рдг рдЕрдирдкреЕрдХ рдХреЗрд▓реЗ рдЬрд╛рдИрд▓, рд╕рдВрдЧреНрд░рд╣рдгрд╛рддреАрд▓ рдбреЗрдЯрд╛ рд╕рд╛рдл рдХреЗрд▓рд╛ рдЬрд╛рдИрд▓ рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗрд▓реА рдЬрд╛рдИрд▓ рдЖрдгрд┐ рдкрд░рд┐рдгрд╛рдореА, рд╕рдВрдкреВрд░реНрдг рдЧреЛрд╖реНрдЯ рдИрдЯреАрдПрд▓ рдкреНрд░рдХреНрд░рд┐рдпреЗрдЪреНрдпрд╛ рдкрд╛рдЗрдкрд▓рд╛рдЗрдирд╡рд░ рдЬрд╛рдИрд▓, рдкрд░рдВрддреБ рд╣реЗ рдЖрдзреАрдЪ рдкрд▓реАрдХрдбреЗ рдЖрд╣реЗ. рд▓реЗрдЦрд╛рдЪреА рд╡реНрдпрд╛рдкреНрддреА. рдЬрд░ рддреЗ рдордиреЛрд░рдВрдЬрдХ рдЖрдгрд┐ рдЙрдкрдпреБрдХреНрдд рдард░рд▓реЗ, рддрд░ рдореА рдЖрдирдВрджрд╛рдиреЗ рдЕрдкрд╛рдЪреЗ рдПрдЕрд░рдлреНрд▓реЛрд╕рд╛рдареА рдИрдЯреАрдПрд▓ рд╕реЛрд▓реНрдпреВрд╢рдиреНрд╕ рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рднрд╛рдЧрд╛рдВрдЪреЗ рд╡рд░реНрдгрди рдХрд░рдгреЗ рд╕реБрд░реВ рдареЗрд╡реЗрди.

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛