MongoDB рдФрд░ рдЖрдИрдЯреА рдиреМрдХрд░реА рдмрд╛рдЬрд╛рд░ рдЕрдиреБрд╕рдВрдзрд╛рди

рдХреНрдпрд╛ рдЖрдкрдиреЗ рдХрднреА рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд┐рдпрд╛ рд╣реИ?

рдЙрдиреНрд╣реЛрдВрдиреЗ рд╕рд╡рд╛рд▓ рдкреВрдЫрд╛, рд╢реНрд░рдо рдмрд╛рдЬрд╛рд░ рдореЗрдВ рдХреМрди рд╕реА рддрдХрдиреАрдХ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдЪрд╛рд▓реВ рд╣реИ? рдПрдХ рдорд╣реАрдиреЗ рдкрд╣рд▓реЗ? рдПрдХ рд╕рд╛рд▓ рдкрд╣рд▓реЗ?

рдЖрдкрдХреЗ рд╢рд╣рд░ рдХреЗ рдХрд┐рд╕реА рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рдирдИ рдЬрд╛рд╡рд╛ рдЬреЙрдм рдУрдкрдирд┐рдВрдЧ рдХрд┐рддрдиреА рдмрд╛рд░ рдЖрддреА рд╣реИ рдФрд░ рд╡реЗ рдХрд┐рддрдиреА рд╕рдХреНрд░рд┐рдпрддрд╛ рд╕реЗ рдмрдВрдж рд╣реЛрддреА рд╣реИрдВ?

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рдЖрдкрдХреЛ рдмрддрд╛рдКрдВрдЧрд╛ рдХрд┐ рдЖрдк рдХреИрд╕реЗ рд╡рд╛рдВрдЫрд┐рдд рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рд░реБрдЪрд┐ рдХреЗ рд╡рд┐рд╖рдп рдкрд░ рд░рд┐рдкреЛрд░реНрдЯрд┐рдВрдЧ рдкреНрд░рдгрд╛рд▓реА рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!


(рдЫрд╡рд┐ рд╕реНрд░реЛрдд)

рдЪреБрдирд╛рд╡ Headhunter.ru рдкрд░ рдЧрд┐рд░ рдЧрдпрд╛


рд╢рд╛рдпрдж рдЖрдк рдореЗрдВ рд╕реЗ рдХрдИ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВ рдФрд░ рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ Headhunter.ru рдЬреИрд╕реЗ рд╕рдВрд╕рд╛рдзрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рд╡рд┐рднрд┐рдиреНрди рдХреНрд╖реЗрддреНрд░реЛрдВ рдореЗрдВ рд╣рдЬрд╛рд░реЛрдВ рдирдИ рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЛ рдЗрд╕ рд╕рд╛рдЗрдЯ рдкрд░ рд░реЛрдЬрд╛рдирд╛ рдкреЛрд╕реНрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд╣реЗрдбрд╣рдВрдЯрд░ рдореЗрдВ рдПрдХ рдПрдкреАрдЖрдИ рднреА рд╣реИ рдЬреЛ рдбреЗрд╡рд▓рдкрд░ рдХреЛ рдЗрд╕ рд╕рдВрд╕рд╛рдзрди рдХреЗ рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

рдЙрдкрдХрд░рдг


рдПрдХ рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рд╣рдо рд░рд┐рдкреЛрд░реНрдЯрд┐рдВрдЧ рдкреНрд░рдгрд╛рд▓реА рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рдПрдкреАрдЖрдИ рд╕рд╛рдЗрдЯ Headhunter.ru рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИред рд╕реВрдЪрдирд╛ рдХреЗ рдордзреНрдпрд╡рд░реНрддреА рднрдВрдбрд╛рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдо рдПрдореНрдмреЗрдбреЗрдб SQLite DBMS рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ, рд╕рдВрд╕рд╛рдзрд┐рдд рдбреЗрдЯрд╛ рдХреЛ рдореБрдЦреНрдп рднрд╛рд╖рд╛ рдХреЗ рд░реВрдк рдореЗрдВ MongoDB рдХреЗ NoSQL рдбреЗрдЯрд╛рдмреЗрд╕, рдкрд╛рдпрдерди 3.4 рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

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

рдкреНрд░рддреНрдпреЗрдХ рд░рд┐рдХреНрддрд┐ 30 рджрд┐рдиреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рдЗрдЯ рдкрд░ рд▓рдЯрдХреА рд░рд╣рддреА рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рдж, рдпрджрд┐ рдЗрд╕реЗ рдирд╡реАрдиреАрдХреГрдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдпрджрд┐ 30 рджрд┐рдиреЛрдВ рдХреА рд╕рдорд╛рдкреНрддрд┐ рд╕реЗ рдкрд╣рд▓реЗ рд░рд┐рдХреНрддрд┐ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рддреЛ рдЗрд╕реЗ рдирд┐рдпреЛрдХреНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдмрдВрдж рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред

рд╣реЗрдбрд╣рдВрдЯрд░ рдПрдкреАрдЖрдИ (рдЗрд╕рдХреЗ рдмрд╛рдж рдПрдЪрдПрдЪ рдПрдкреАрдЖрдИ рдХреЗ рд░реВрдк рдореЗрдВ рд╕рдВрджрд░реНрднрд┐рдд) рдЖрдкрдХреЛ рдкрд┐рдЫрд▓реЗ 30 рджрд┐рдиреЛрдВ рдореЗрдВ рдХрд┐рд╕реА рднреА рддрд╛рд░реАрдЦ рдХреЗ рд▓рд┐рдП рдкреНрд░рдХрд╛рд╢рд┐рдд рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рд╣рдо рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ - рд╣рдо рджреИрдирд┐рдХ рдЖрдзрд╛рд░ рдкрд░ рдкреНрд░рддреНрдпреЗрдХ рджрд┐рди рдХреЗ рд▓рд┐рдП рдкреНрд░рдХрд╛рд╢рд┐рдд рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЛ рдПрдХрддреНрд░ рдХрд░реЗрдВрдЧреЗред

рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


  • рдХрдиреЗрдХреНрдЯ SQLite DB

    import sqlite3 conn_db = sqlite3.connect('hr.db', timeout=10) c = conn_db.cursor() 
  • рдиреМрдХрд░реА рдХреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рднрдВрдбрд╛рд░рдг рдХреЗ рд▓рд┐рдП рддрд╛рд▓рд┐рдХрд╛
    рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, рд╣рдо SQLite рдбреЗрдЯрд╛рдмреЗрд╕ рдХреА рдПрдХ рд╡рд┐рд╢реЗрд╖ рддрд╛рд▓рд┐рдХрд╛ рдореЗрдВ рд░рд┐рдХреНрддрд┐ рд╕реНрдерд┐рддрд┐ рдкрд░рд┐рд╡рд░реНрддрди (рддрд╛рд░реАрдЦ рддрдХ рдЙрдкрд▓рдмреНрдзрддрд╛) рдХреЗ рдЗрддрд┐рд╣рд╛рд╕ рдХреЛ рдмрдЪрд╛рдПрдВрдЧреЗред рд╡реЗрдХреЗрдВрд╕реА_рд╣реЙрд╕реНрдЯрд░ рдЯреЗрдмрд▓ рдХреА рдмрджреМрд▓рдд, рд╣рдореЗрдВ рд╕рд╛рдЗрдЯ рдкрд░ рд╡реИрдХреЗрдВрд╕реА рдХреА рдЙрдкрд▓рдмреНрдзрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкрддрд╛ рдЪрд▓ рдЬрд╛рдПрдЧрд╛ред рд╡рд╣ рдХрд┐рди рддрд┐рдерд┐рдпреЛрдВ рдореЗрдВ рд╕рдХреНрд░рд┐рдп рдереАред

     c.execute(''' create table if not exists vacancy_history ( id_vacancy integer, date_load text, date_from text, date_to text )''') 
  • рд░рд┐рдХреНрддрд┐ рдЫрд╛рдирдиреЗ рдХрд╛ рдХрд╛рдо
    рдПрдХ рдкреНрд░рддрд┐рдмрдВрдз рд╣реИ рдХрд┐ рдПрдХ рдЕрдиреБрд░реЛрдз 2000 рд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрдЧреНрд░рд╣ рдирд╣реАрдВ рд▓реМрдЯрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдЪреВрдВрдХрд┐ рдПрдХ рджрд┐рди рдореЗрдВ рд╕рд╛рдЗрдЯ рдкрд░ рдХрдИ рдФрд░ рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЛ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЕрдиреБрд░реЛрдз рдирд┐рдХрд╛рдп рдореЗрдВ рдПрдХ рдлрд╝рд┐рд▓реНрдЯрд░ рдбрд╛рд▓реЗрдВрдЧреЗ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП: рдХреЗрд╡рд▓ рд╕реЗрдВрдЯ рдкреАрдЯрд░реНрд╕рдмрд░реНрдЧ рдореЗрдВ рд░рд┐рдХреНрддрд┐рдпрд╛рдВ (рдХреНрд╖реЗрддреНрд░ = 2) , рдЖрдИрдЯреА рд╡рд┐рд╢реЗрд╖рдЬреНрдЮрддрд╛ рджреНрд╡рд╛рд░рд╛ (рд╡рд┐рд╢реЗрд╖рдЬреНрдЮрддрд╛ = 1)

     path = ("/vacancies?area=2&specialization=1&page={}&per_page={}&date_from={}&date_to={}".format(page, per_page, date_from, date_to)) 
  • рдЕрддрд┐рд░рд┐рдХреНрдд рдЪрдпрди рд╢рд░реНрддреЗрдВ
    рд╢реНрд░рдо рдмрд╛рдЬрд╛рд░ рддреЗрдЬреА рд╕реЗ рдмрдврд╝ рд░рд╣рд╛ рд╣реИ рдФрд░ рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдлрд╝рд┐рд▓реНрдЯрд░ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреЗ рд╣реБрдП, рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ 2000 рд╕реЗ рдЕрдзрд┐рдХ рд╣реЛ рд╕рдХрддреА рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рджрд┐рди рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рд▓реЙрдиреНрдЪ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рд╕реАрдорд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░реЗрдВрдЧреЗ: рджрд┐рди рдХреЗ рдкрд╣рд▓реЗ рдЖрдзреЗ рдХреЗ рд▓рд┐рдП рд░рд┐рдХреНрддрд┐рдпрд╛рдВ рдФрд░ рджреВрд╕рд░реЗ рдЫрдорд╛рд╣реА рдХреЗ рд▓рд┐рдП рд░рд┐рдХреНрддрд┐рдпрд╛рдВред

     def get_vacancy_history(): ... count_days = 30 hours = 0 while count_days >= 0: while hours < 24: date_from = (cur_date.replace(hour=hours, minute=0, second=0) - td(days=count_days)).strftime('%Y-%m-%dT%H:%M:%S') date_to = (cur_date.replace(hour=hours + 11, minute=59, second=59) - td(days=count_days)).strftime('%Y-%m-%dT%H:%M:%S') while count == per_page: path = ("/vacancies?area=2&specialization=1&page={} &per_page={}&date_from={}&date_to={}" .format(page, per_page, date_from, date_to)) conn.request("GET", path, headers=headers) response = conn.getresponse() vacancies = response.read() conn.close() count = len(json.loads(vacancies)['items']) ... #     try: c.executemany('INSERT INTO vacancy_history VALUES (?,?,?,?)', collection_for_ins) except sqlite3.DatabaseError as err: print("Error: ", err) else: conn_db.commit() if collection_for_ins: page = page + 1 total = total + count #   del(collection_for_ins[:]) hours = hours + 12 count_days = count_days - 1 hours = 0 


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

  c.execute(""" select a.id_vacancy, date(a.date_load) as date_last_load, date(a.date_from) as date_publish, ifnull(a.date_next, date(a.date_load, '+1 day')) as date_close from ( select vh1.id_vacancy, vh1.date_load, vh1.date_from, min(vh2.date_load) as date_next from vacancy_history vh1 left join vacancy_history vh2 on vh1.id_vacancy = vh2.id_vacancy and vh1.date_load < vh2.date_load where date(vh1.date_load) between :date_in and :date_out group by vh1.id_vacancy, vh1.date_load, vh1.date_from ) as a where a.date_next is null """, {"date_in" : date_in, "date_out" : date_out}) date_in = dt.datetime(2018, 7, 1) date_out = dt.datetime(2018, 7, 31) closed_vacancies = get_closed_by_period(date_in, date_out) df = pd.DataFrame(closed_vacancies, columns = ['id_vacancy', 'date_last_load', 'date_publish', 'date_close']) df.head() 

рд╣рдореЗрдВ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдорд┐рд▓рддрд╛ рд╣реИ:
id_vacancydate_last_loaddate_publishdate_close
0181266972018/07/092018/07/092018/07/10
1181551212018/07/092018/06/192018/07/10
2188816052018/07/092018/02/072018/07/10
3196207832018/07/092018/06/272018/07/10
4196961882018/07/092018/06/152018/07/10
рдпрджрд┐ рд╣рдо рдПрдХреНрд╕реЗрд▓ рдЯреВрд▓реНрд╕ рдпрд╛ рдерд░реНрдб-рдкрд╛рд░реНрдЯреА рдмреАрдЖрдИ рдЯреВрд▓реНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рд░рд┐рдХреНрддрд┐_рд╣рд┐рд╕реНрдЯреНрд░реА рдЯреЗрдмрд▓ рдХреЛ рдФрд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реАрдПрд╕рд╡реА рдлрд╛рдЗрд▓ рдореЗрдВ рдЕрдкрд▓реЛрдб рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

 #       CSV data = c.execute('select * from vacancy_history') with open('vacancy_history.csv','w', newline='') as out_csv_file: csv_out = csv.writer(out_csv_file) csv_out.writerow(d[0] for d in data.description) csv_out.writerows(data.fetchall()) conn_db.close() 

рднрд╛рд░реА рддреЛрдкрдЦрд╛рдиреЗ


рд▓реЗрдХрд┐рди рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдЕрдЧрд░ рд╣рдореЗрдВ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдбреЗрдЯрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдпрд╣рд╛рдБ MongoDB рджрд╕реНрддрд╛рд╡реЗрдЬрд╝-рдЙрдиреНрдореБрдЦ NoSQL рдбреЗрдЯрд╛рдмреЗрд╕ рдмрдЪрд╛рд╡ рдореЗрдВ рдЖрддрд╛ рд╣реИ, рдЬреЛ рдЖрдкрдХреЛ JSON рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

  • рдореЗрд░реЗ MongoDB рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛ рдПрдХ рдбреЗрдореЛ mLab рдХреНрд▓рд╛рдЙрдб рд╕реЗрд╡рд╛ рдореЗрдВ рддреИрдирд╛рдд рд╣реИ, рдЬреЛ рдЖрдкрдХреЛ рдореБрдлреНрдд рдореЗрдВ 500MB рддрдХ рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рдмрдирд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЬреЛ рд╡рд░реНрддрдорд╛рди рдХрд╛рд░реНрдп рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реИред Hr_db рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдПрдХ рд░рд┐рдХреНрддрд┐ рд╕рдВрдЧреНрд░рд╣ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдПрдХ рдХрдиреЗрдХреНрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВрдЧреЗ:

     #    Mongo from pymongo import MongoClient from pymongo import ASCENDING from pymongo import errors client = MongoClient('mongodb://<db_user>:<dbpassword>@ds115219.mlab.com:15219/hr_db') db = client.hr_db VacancyMongo = db.Vacancy 
  • рдпрд╣ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рд╡реЗрддрди рд╕реНрддрд░ рд╣рдореЗрд╢рд╛ рд░реВрдмрд▓ рдореЗрдВ рдЗрдВрдЧрд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП, рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рд╕рднреА рдореВрд▓реНрдпреЛрдВ рдХреЛ рдмрд░реНрдмрд╛рдж рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд╕рдордХрдХреНрд╖ рдкрд░ рд▓рд╛рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо HH API рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП рд╢рдмреНрджрдХреЛрд╢реЛрдВ рдХрд╛ рдПрдХ рд╕рдВрдЧреНрд░рд╣ рдкрдВрдк рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдореЗрдВ рд╡рд░реНрддрдорд╛рди рддрд┐рдерд┐ рдХреЗ рд▓рд┐рдП рд╡рд┐рдирд┐рдордп рджрд░ рдХреА рдЬрд╛рдирдХрд╛рд░реА рд╣реЛрддреА рд╣реИ:

     #   def get_dictionaries(): conn = http.client.HTTPSConnection("api.hh.ru") conn.request("GET", "https://api.hh.ru/dictionaries", headers=headers) response = conn.getresponse() if response.status != 200: conn.close() conn = http.client.HTTPSConnection("api.hh.ru") conn.request("GET", "https://api.hh.ru/dictionaries", headers=headers) response = conn.getresponse() dictionaries = response.read() dictionaries_json = json.loads(dictionaries) return dictionaries_json 
  • рд╡рд░реНрддрдорд╛рди рд╡рд┐рдирд┐рдордп рджрд░реЛрдВ рдХреЗ рд╕рд╛рде рдореБрджреНрд░рд╛рдУрдВ рдореЗрдВ рд╢рдмреНрджрдХреЛрд╢ рднрд░рдирд╛:

     hh_dictionary = get_dictionaries() currencies = hh_dictionary['currency'] currency_rates = {} for currency in currencies: currency_rates[currency['code']] = currency['rate'] 

рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрд░реЛрдХреНрдд рдХрд╛рд░реНрдп рджреИрдирд┐рдХ рдЖрдзрд╛рд░ рдкрд░ рд╢реБрд░реВ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рд░ рдмрд╛рд░ рд╕рднреА рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЛ рджреЗрдЦрдиреЗ рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╕реНрддреГрдд рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдХреЛрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рд╣рдо рдХреЗрд╡рд▓ рд╡рд╣реА рд▓реЗрдВрдЧреЗ рдЬреЛ рдкрд┐рдЫрд▓реЗ рдкрд╛рдВрдЪ рджрд┐рдиреЛрдВ рдореЗрдВ рдкреНрд░рд╛рдкреНрдд рд╣реБрдП рдереЗред
  • SQLite рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдкрд┐рдЫрд▓реЗ 5 рджрд┐рдиреЛрдВ рдХреЗ рд▓рд┐рдП рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛:

     def get_list_of_vacancies_sql(): conn_db = sqlite3.connect('hr.db', timeout=10) conn_db.row_factory = lambda cursor, row: row[0] c = conn_db.cursor() items = c.execute(""" select distinct id_vacancy from vacancy_history where date(date_load) >= date('now', '-5 day') """).fetchall() conn_db.close() return items 
  • MongoDB рд╕реЗ рдкрд┐рдЫрд▓реЗ рдкрд╛рдВрдЪ рджрд┐рдиреЛрдВ рдХреЗ рд▓рд┐рдП рдиреМрдХрд░рд┐рдпреЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛:

     def get_list_of_vacancies_nosql(): date_load = (dt.datetime.now() - td(days=5)).strftime('%Y-%m-%d') vacancies_from_mongo = [] for item in VacancyMongo.find({"date_load" : {"$gte" : date_load}}, {"id" : 1, "_id" : 0}): vacancies_from_mongo.append(int(item['id'])) return vacancies_from_mongo 
  • рдпрд╣ рджреЛ рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдмрдиреА рд╣реБрдИ рд╣реИ, рдЙрди рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЬреЛ рдореЛрдВрдЧреЛрдбреАрдмреА рдореЗрдВ рдирд╣реАрдВ рд╣реИрдВ, рд╡рд┐рд╕реНрддреГрдд рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рд▓рд┐рдЦреЗрдВ:

     sql_list = get_list_of_vacancies_sql() mongo_list = get_list_of_vacancies_nosql() vac_for_pro = [] s = set(mongo_list) vac_for_pro = [x for x in sql_list if x not in s] vac_id_chunks = [vac_for_pro[x: x + 500] for x in range(0, len(vac_for_pro), 500)] 
  • рдЗрд╕рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдирдИ рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд░рдгреА рд╣реИ рдЬреЛ рдЕрднреА рддрдХ MongoDB рдореЗрдВ рдЙрдкрд▓рдмреНрдз рдирд╣реАрдВ рд╣реИрдВ, рдЙрдирдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рд╣рдо HH API рдореЗрдВ рдПрдХ рдЕрдиреБрд░реЛрдз рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╡рд┐рд╕реНрддреГрдд рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗ, рдЗрд╕реЗ рд╕реАрдзреЗ MongoDB рдореЗрдВ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░реЗрдВрдЧреЗ:
    1. рд╣рдо рд░реВрдмрд▓ рдХреЗ рдмрд░рд╛рдмрд░ рдордЬрджреВрд░реА рдХреА рд░рд╛рд╢рд┐ рд▓рд╛рддреЗ рд╣реИрдВ;
    2. рдкреНрд░рддреНрдпреЗрдХ рд░рд┐рдХреНрддрд┐ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рд╢реЗрд╖рдЬреНрдЮ рд╕реНрддрд░ рдХрд╛ рд╕реНрдирд╛рддрдХ рдЬреЛрдбрд╝реЗрдВ (рдЬреВрдирд┐рдпрд░ / рдордзреНрдп / рд╡рд░рд┐рд╖реНрда рдЖрджрд┐)

    рдпрд╣ рд╕рдм рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдореЗрдВ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ_рдкреНрд░рдХрд░рдг рдХрд╛рд░реНрдп:

     from nltk.stem.snowball import SnowballStemmer stemmer = SnowballStemmer("russian") def vacancies_processing(vacancies_list): cur_date = dt.datetime.now().strftime('%Y-%m-%d') for vacancy_id in vacancies_list: conn = http.client.HTTPSConnection("api.hh.ru") conn.request("GET", "/vacancies/{}".format(vacancy_id), headers=headers) response = conn.getresponse() if response.status != 404: vacancy_txt = response.read() conn.close() vacancy = json.loads(vacancy_txt) # salary salary = None if 'salary' in vacancy: if vacancy['salary'] != None: ... max_salary = 500000 if salary is not None: salary = int(salary) if salary >= max_salary: salary = max_salary # grade grade = None if 'name' in vacancy: p_grade = '' title = re.sub(u'[^a-z-]+', ' ', vacancy['name'].lower(), re.UNICODE) words = re.split(r'\s{1,}', title.strip()) for title_word in words: title_word = stemmer.stem(title_word) if len(title_word.strip()) > 1: p_grade = p_grade + " " + title_word.strip() if re.search('()|(princip)', p_grade): grade = 'principal' elif re.search('()|(senior)|([f|F]ull)', p_grade): grade = 'senior' ... else: grade = 'not specify' vacancy['salary_processed'] = salary vacancy['date_load'] = cur_date vacancy['grade'] = grade vacancy.pop('branded_description', None) try: post_id = VacancyMongo.insert_one(vacancy) except errors.DuplicateKeyError: print ('Cant insert the duplicate vacancy_id:', vacancy['id']) 
  • рдПрдЪрдПрдЪ рдПрдкреАрдЖрдИ рддрдХ рдкрд╣реБрдВрдЪрдХрд░ рд╡рд┐рд╕реНрддреГрдд рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛, рдкреВрд░реНрд╡-рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдкреНрд░рд╛рдкреНрдд рд╣реБрдЖ
    MongoDB рдбреЗрдЯрд╛ рдмрд╛рд╣рд░ рд▓реЗ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдЗрд╕реЗ рдХрдИ рдзрд╛рд░рд╛рдУрдВ рдореЗрдВ рд╕рдореНрдорд┐рд▓рд┐рдд рдХрд░реЗрдЧрд╛, рдкреНрд░рддреНрдпреЗрдХ рдореЗрдВ 500 рд░рд┐рдХреНрддрд┐рдпрд╛рдВ рд╣реЛрдВрдЧреА:

     t_num = 1 threads = [] for vac_id_chunk in vac_id_chunks: print('starting', t_num) t_num = t_num + 1 t = threading.Thread(target=vacancies_processing, kwargs={'vacancies_list': vac_id_chunk}) threads.append(t) t.start() for t in threads: t.join() 


MongoDB рдореЗрдВ рдЖрдмрд╛рдж рд╕рдВрдЧреНрд░рд╣ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:



рдХреБрдЫ рдФрд░ рдЙрджрд╛рд╣рд░рдг


рд╣рдорд╛рд░реЗ рдирд┐рдкрдЯрд╛рди рдореЗрдВ рдПрдХрддреНрд░рд┐рдд рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рдмрд╛рдж, рд╣рдо рд╡рд┐рднрд┐рдиреНрди рд╡рд┐рд╢реНрд▓реЗрд╖рдгрд╛рддреНрдордХ рдирдореВрдиреЗ рдкреНрд░рджрд░реНрд╢рди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдореИрдВ рд╕реЗрдВрдЯ рдкреАрдЯрд░реНрд╕рдмрд░реНрдЧ рдореЗрдВ рдкрд╛рдпрдерди-рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЗ рд╢реАрд░реНрд╖ 10 рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рднреБрдЧрддрд╛рди рдХрд┐рдП рдЧрдП рд░рд┐рдХреНрдд рдкрджреЛрдВ рдХреЛ рдмрд╛рд╣рд░ рд▓рд╛рдКрдВрдЧрд╛:

 cursor_mongo = VacancyMongo.find({"name" : {"$regex" : ".*[pP]ython*"}}) df_mongo = pd.DataFrame(list(cursor_mongo)) del df_mongo['_id'] pd.concat([df_mongo.drop(['employer'], axis=1), df_mongo['employer'].apply(pd.Series)['name']], axis=1)[['grade', 'name', 'salary_processed' ]].sort_values('salary_processed', ascending=False)[:10] 

рд╢реАрд░реНрд╖ 10 рдкрд╛рдпрдерди рдХреЗ рдЙрдЪреНрдЪрддрдо рднреБрдЧрддрд╛рди рд╡рд╛рд▓реА рдиреМрдХрд░рд┐рдпрд╛рдВ
рдЧреНрд░реЗрдбрдирд╛рдордирд╛рдоsalary_processed
рд╡рд░рд┐рд╖реНрдард╡реЗрдм рдЯреАрдо рд▓реАрдб / рд╡рд╛рд╕реНрддреБрдХрд╛рд░ (рдкрд╛рдпрдерди / Django / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛)рдЗрдиреНрд╡реЗрд╕реНрдЯреЗрдХреНрд╕ рд▓рд┐293901.0
рд╡рд░рд┐рд╖реНрдардореЛрдВрдЯреЗрдиреЗрдЧреНрд░реЛ рдореЗрдВ рд╡рд░рд┐рд╖реНрда рдкрд╛рдпрдерди рдбреЗрд╡рд▓рдкрд░Betmaster277141.0
рд╡рд░рд┐рд╖реНрдардореЛрдВрдЯреЗрдиреЗрдЧреНрд░реЛ рдореЗрдВ рд╡рд░рд┐рд╖реНрда рдкрд╛рдпрдерди рдбреЗрд╡рд▓рдкрд░Betmaster275289.0
рдмреАрдЪрдмреИрдХ-рдПрдВрдб рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ (рдкрд╛рдпрдерди)Soshace250000.0
рдмреАрдЪрдмреИрдХ-рдПрдВрдб рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ (рдкрд╛рдпрдерди)Soshace250000.0
рд╡рд░рд┐рд╖реНрдард╕реНрд╡рд┐рд╕ рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рдХреЗ рд▓рд┐рдП рд▓реАрдб рдкрд╛рдпрдерди рдЗрдВрдЬреАрдирд┐рдпрд░рдПрд╢рд┐рдпрд╛ рдЗрдВрдЯрд░рдиреЗрд╢рдирд▓ рдПрдЬреА250000.0
рдмреАрдЪрдмреИрдХ-рдПрдВрдб рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ (рдкрд╛рдпрдерди)Soshace250000.0
рдмреАрдЪрдмреИрдХ-рдПрдВрдб рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ (рдкрд╛рдпрдерди)Soshace250000.0
рд╡рд░рд┐рд╖реНрдардкрд╛рдпрдерди рдЯреАрдорд▓реЗрдбDigitalHR230000.0
рд╡рд░рд┐рд╖реНрдард▓реАрдб рдбреЗрд╡рд▓рдкрд░ (рдкрд╛рдпрдерди, рдкреАрдПрдЪрдкреА, рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ)IK рдЧреНрд░реБрдк220231.0



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

 cursor_mongo = VacancyMongo.find({"name" : {"$regex" : ".*[jJ]ava[^sS]"}, "address" : {"$ne" : None}}) df_mongo = pd.DataFrame(list(cursor_mongo)) df_mongo['metro'] = df_mongo.apply(lambda x: x['address']['metro']['station_name'] if x['address']['metro'] is not None else None, axis = 1) df_mongo.groupby('metro')['_id'] \ .count() \ .reset_index(name='count') \ .sort_values(['count'], ascending=False) \ [:10] 

рдореЗрдЯреНрд░реЛ рд╕реНрдЯреЗрд╢рдиреЛрдВ рдореЗрдВ рдЬрд╛рд╡рд╛ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЗ рд▓рд┐рдП рдиреМрдХрд░рд┐рдпрд╛рдВ
рдореЗрдЯреНрд░реЛрдЧрд┐рдирддреА
Vasileoostrovskaya87
рдкреЗрдЯреНрд░реЛрдЧреНрд░реИрдб68
Vyborg46
рд▓реЗрдирд┐рди рд╕реНрдХреНрд╡рд╛рдпрд░45
рдЧреЛрд░реНрдХреА45
Chkalovskaya43
рдирд╛рд░реНрд╡рд╛32
рд╡рд┐рджреНрд░реЛрд╣реА рд╡рд░реНрдЧ29
рдкреБрд░рд╛рдирд╛ рдЧрд╛рдБрд╡29
Elizarovskaya27


рдкрд░рд┐рдгрд╛рдо


рддреЛ, рд╡рд┐рдХрд╕рд┐рдд рдкреНрд░рдгрд╛рд▓реА рдХреА рд╡рд┐рд╢реНрд▓реЗрд╖рдгрд╛рддреНрдордХ рдХреНрд╖рдорддрд╛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╡реНрдпрд╛рдкрдХ рд╣реИ рдФрд░ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рдХреА рдпреЛрдЬрдирд╛ рдмрдирд╛рдиреЗ рдпрд╛ рдЧрддрд┐рд╡рд┐рдзрд┐ рдХреА рдПрдХ рдирдИ рджрд┐рд╢рд╛ рдЦреЛрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдореИрдВ рдзреНрдпрд╛рди рджреЗрддрд╛ рд╣реВрдВ рдХрд┐ рдЕрдм рддрдХ рдХреЗрд╡рд▓ рд╕рд┐рд╕реНрдЯрдо рдХреА рдмреБрдирд┐рдпрд╛рджреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдкреНрд░рд╕реНрддреБрдд рдХреА рдЬрд╛рддреА рд╣реИ, рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЗрд╕реЗ рднреМрдЧреЛрд▓рд┐рдХ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рджреНрд╡рд╛рд░рд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреА рджрд┐рд╢рд╛ рдореЗрдВ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдФрд░ рд╢рд╣рд░ рдХреЗ рдХрд┐рд╕реА рд╡рд┐рд╢реЗрд╖ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рд░рд┐рдХреНрддрд┐рдпреЛрдВ рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдХреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдХрд░рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рд╣реИред

рдЗрд╕ рд▓реЗрдЦ рдХреЗ рд▓рд┐рдП рдкреВрд░реНрдг рд╕реНрд░реЛрдд рдХреЛрдб рдореЗрд░реЗ GitHub рдХреЗ рд▓рд┐рдВрдХ рдкрд░ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред

PS рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ рд▓реЗрдЦ рдкрд░ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИ, рдореБрдЭреЗ рдЖрдкрдХреЗ рд╕рднреА рд╕рд╡рд╛рд▓реЛрдВ рдХреЗ рдЬрд╡рд╛рдм рджреЗрдиреЗ рдФрд░ рдЖрдкрдХреА рд░рд╛рдп рдЬрд╛рдирдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реЛрдЧреАред рдзрдиреНрдпрд╡рд╛рдж!

Source: https://habr.com/ru/post/hi422627/


All Articles