python

Парсинг контактов адресной книги Яндекс.почты в CSV на Python

  • суббота, 12 марта 2022 г. в 00:35:15
https://habr.com/ru/post/655205/
  • Python
  • Программирование


Как-то в процессе работы возник вопрос как на корпоративном домене в Яндексе выгрузить все контакты организации из адресной книги Яндекс.почты в файл .csv или .xls, чтобы было красиво и потом удобно работать с этими данными в MS Excel.

Оказывается, что стандартными средствами Яндекс.почты можно выгрузить только в Vcard формат, что мне совсем не подходит. Нужно потом его как-то конвертировать сторонними средствами. Такие попадались на просторах интернета, что совсем неудобно.

На помощь пришел Python.

C помощью библиотеки BeautifulSoup очень удобно и быстро парсить html и вынимать всю нужную нам информацию.

Проведя анализ исходного кода страницы и выведя особые закономерности в представлении на ней информации было решено “В бой!”.

Для парсинга нам понадобится Python 3, библиотека BeautifulSoup и браузер с инспектором кода.

Для начала импортируем необходимые для работы библиотеки.

from bs4 import BeautifulSoup as BS
import re
import csv

В результате анализа кода страницы выявлено, что данные которые нам нужны спрятаны в тегах span с классами: 

mail-AbookEntry-Contact - содержит имя контакта

mail-AbookEntry-Emails - содержит список Email-ов

mail-AbookEntry-Phones - телефоны

Сохраняем нашу веб страницу на диск в папку со скриптом, я назвал ее просто “contacts.html”. Если в адресной книге много контактов, то перед сохранением не забываем нажать “Показать все контакты” в конце страницы.

Открываем сохраненную страницу и получаем весь код внутри тегов span, сохраняя их в три списка:

with open('contacts.html', 'r', encoding='utf-8') as f:
    html_str = f.read()
    res = BS(html_str, features="lxml")
    
    #список с именами
    l_contacts = res.findAll('span', class_='mail-AbookEntry-Contact')
    #список с Emailами
    l_emails = res.findAll('span', class_='mail-AbookEntry-Emails')
    #список с телефонами
    l_phones = res.findAll('span', class_='mail-AbookEntry-Phones')
    #Объединяем в один большой список с которым и будем дальше работать
    l_res = list(zip(l_contacts, l_emails, l_phones))

Каждый элемент списка l_res это список с кусочками кода с нашей страницы, соответствующий строке с контактом.

Пробегаем циклом по нашему списку:    

    for i in l_res:
        l_row_contact=[] #список, в который будут записываться данные о контакте (имя, емейлы, телефон) для записи в файл .csv

Так как i элемент это тоже список, то проходим по всем элементам этого списка.

        for j in i:

Может быть такое, что у контакта несколько Email-ов. В коде они хранятся в списке ul с классом _nb-popup-menu, а каждый email обернут в теги li и a.

#Если существует список дополнительных емейлов у контакта, то извлекаем емейлы и сохраняем в список l_a
            if j.find('ul', class_='_nb-popup-menu') is not None:

Для начала находим и получаем содержимое тегов li:

                l_li = j.findAll('li', class_='_nb-popup-line')
                l_a=[]

Пробегаем по каждому полученному li и извлекаем текст Email, обернутый в тег a  с классом _nb-popup-link

                for k in l_li:
                    if k.find('a', class_='_nb-popup-link') is not None:
                        l_a.append(k.find('a', class_='_nb-popup-link').text) 

Добавляем к результирующему списку с данными о контакте строку с разделителями \r\n, чтобы в ячейке был перенос, если несколько email-ов.          

                l_row_contact.append('\r\n'.join(l_a))
            #если доп списка нет, то просто извлекаем все данные по контакту
            elif j.find('span', class_='mail-ui-Overflower') is not None:
                l_row_contact.append(j.find('span', class_='mail-ui-Overflower').text)

В результате одного прохода мы получаем список со строками следующего формата: [ ‘Имя контакта’, ‘Email_1\r\nEmail_2…Email_k’, ‘Телефон’ ]

#записываем строку с данными контакта в файл csv
        with open('contacts.csv','a') as f:

Я использую разделитель ‘;’ только, чтобы при открытии было красиво в Excel.

            file_writer = csv.writer(f, delimiter = ";", lineterminator="\r")
            file_writer.writerow(['Имя', 'Emails', 'Телефон'])
            file_writer.writerow(l_row_contact)

И так проходим по всему списку, дописывая в файл строки с данными о контактах.

Прошу не судите строго. Это моя первая подобная статья и я только принялся осваивать язык Python.

Уже позже данный скрипт был доработан с использованием драйвера Chromium и парсинга страницы из под сохраненной сессии пользователя в Яндекс почте, который достает два списка контактов (личные и общие в организации).

Если использовать разделитель ‘,’ и убрать дополнительные Email-ы в получившемся файле, то можно импортировать всё в Outlook.

Код проекта вы можете посмотреть и скачать на GitHub.