329 lines
13 KiB
Python
329 lines
13 KiB
Python
import docx
|
||
import csv
|
||
import re
|
||
|
||
|
||
def extract_quoted_text(text):
|
||
"""Извлекает текст в кавычках «»"""
|
||
if not text:
|
||
return ""
|
||
match = re.search(r'«([^»]+)»', text)
|
||
return match.group(1).strip() if match else text.strip()
|
||
|
||
|
||
def extract_address(text):
|
||
"""Извлекает адрес, содержащий г. или Г."""
|
||
if not text:
|
||
return ""
|
||
# Ищем текст с "г." или "Г."
|
||
if re.search(r'[гГ]\.', text):
|
||
return text.strip()
|
||
return ""
|
||
|
||
|
||
def extract_email(text):
|
||
"""Извлекает email из текста и убирает пробелы после @"""
|
||
if not text:
|
||
return ""
|
||
|
||
# Убираем пробелы после @
|
||
text = re.sub(r'@\s+', '@', text)
|
||
|
||
# Ищем паттерн email
|
||
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
||
match = re.search(email_pattern, text)
|
||
return match.group(0) if match else ""
|
||
|
||
|
||
def extract_phone(text):
|
||
"""Извлекает телефон из текста"""
|
||
if not text:
|
||
return ""
|
||
# Убираем лишние пробелы
|
||
return text.strip()
|
||
|
||
|
||
def parse_contact_data(cells):
|
||
"""Парсит данные контакта из ячеек таблицы"""
|
||
# Объединяем весь текст из всех ячеек для анализа
|
||
all_text = ' '.join(cells)
|
||
|
||
# ВАЖНО: Парсим только строки с email!
|
||
has_email = '@' in all_text
|
||
if not has_email:
|
||
return None
|
||
|
||
contact = {
|
||
'Имя': '',
|
||
'Фамилия': '',
|
||
'Отчество': '',
|
||
'Организация': '',
|
||
'Телефон': '',
|
||
'E-mail': ''
|
||
}
|
||
|
||
# Ищем email
|
||
for cell in cells:
|
||
email = extract_email(cell)
|
||
if email:
|
||
contact['E-mail'] = email
|
||
break
|
||
|
||
# Ищем организацию/имя в кавычках «»
|
||
organization_found = False
|
||
for cell in cells:
|
||
quoted_match = re.search(r'«([^»]+)»', cell)
|
||
if quoted_match:
|
||
contact['Организация'] = quoted_match.group(1).strip()
|
||
organization_found = True
|
||
break
|
||
|
||
# Если не нашли в кавычках, ищем название ЗАГЛАВНЫМИ буквами
|
||
if not organization_found:
|
||
for cell in cells:
|
||
# Ищем текст заглавными буквами (название организации обычно так)
|
||
# Исключаем строки с email и телефонами
|
||
if cell and not '@' in cell and not re.search(r'8\s*\(\d+\)', cell):
|
||
# Проверяем, есть ли заглавные слова
|
||
upper_words = re.findall(r'[А-ЯЁA-Z][А-ЯЁA-Z\s\-\.]+', cell)
|
||
if upper_words:
|
||
# Берем самое длинное совпадение
|
||
longest = max(upper_words, key=len).strip()
|
||
if len(longest) > 10: # Минимум 10 символов для названия
|
||
contact['Организация'] = longest
|
||
organization_found = True
|
||
break
|
||
|
||
# Ищем адрес (содержит г. или Г.) и добавляем к организации
|
||
for cell in cells:
|
||
address_match = re.search(r'\d{6},\s*[гГ]\.\s*[^,]+(?:,\s*[^,]+)*', cell)
|
||
if address_match:
|
||
address = address_match.group(0)
|
||
if contact['Организация'] and address not in contact['Организация']:
|
||
contact['Организация'] += ', ' + address
|
||
elif not contact['Организация']:
|
||
contact['Организация'] = address
|
||
break
|
||
|
||
# Ищем телефон (только основной, если есть)
|
||
for cell in cells:
|
||
# Ищем паттерн телефона
|
||
phone_match = re.search(r'8\s*\(\d+\)\s*[\d\-]+', cell)
|
||
if phone_match:
|
||
contact['Телефон'] = phone_match.group(0).strip()
|
||
break
|
||
|
||
return contact
|
||
|
||
|
||
def parse_phone_book():
|
||
"""Парсит телефонную книгу из Word документа и сохраняет в CSV"""
|
||
|
||
# Открываем документ
|
||
doc = docx.Document('./04_Телефонный справочник_МАКСИМ.docx')
|
||
|
||
# Список для хранения контактов
|
||
contacts = []
|
||
|
||
# Обрабатываем каждую таблицу в документе
|
||
for table in doc.tables:
|
||
# Пропускаем первую строку (заголовок), если она есть
|
||
rows = table.rows
|
||
|
||
for i, row in enumerate(rows):
|
||
cells = [cell.text.strip() for cell in row.cells]
|
||
|
||
# Пропускаем пустые строки или заголовки
|
||
if not cells or not any(cells):
|
||
continue
|
||
|
||
# Проверяем, что это не заголовок таблицы
|
||
if i == 0 and ('имя' in cells[0].lower() or 'фамилия' in ' '.join(cells).lower()):
|
||
continue
|
||
|
||
# Парсим данные контакта
|
||
contact = parse_contact_data(cells)
|
||
|
||
# Добавляем только если есть email и данные
|
||
if contact and any(contact.values()):
|
||
contacts.append(contact)
|
||
|
||
# Записываем в CSV файл
|
||
with open('contacts.csv', 'w', encoding='utf-8', newline='') as csvfile:
|
||
fieldnames = ['Имя', 'Фамилия', 'Отчество', 'Организация', 'Телефон', 'E-mail']
|
||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=';')
|
||
|
||
# Записываем заголовок
|
||
writer.writeheader()
|
||
|
||
# Записываем все контакты
|
||
writer.writerows(contacts)
|
||
|
||
print(f"Успешно обработано {len(contacts)} контактов")
|
||
print(f"Данные сохранены в файл contacts.csv")
|
||
|
||
return contacts
|
||
|
||
|
||
def export_for_gmail():
|
||
"""Экспортирует контакты в формате для Gmail"""
|
||
# Читаем существующий CSV файл
|
||
contacts = []
|
||
with open('contacts.csv', 'r', encoding='utf-8') as csvfile:
|
||
reader = csv.DictReader(csvfile, delimiter=';')
|
||
contacts = list(reader)
|
||
|
||
# Создаем CSV в формате Gmail
|
||
with open('contacts_gmail.csv', 'w', encoding='utf-8', newline='') as csvfile:
|
||
# Gmail использует специальные заголовки
|
||
fieldnames = ['Name', 'Given Name', 'Additional Name', 'Family Name',
|
||
'Organization 1 - Name', 'Phone 1 - Value', 'E-mail 1 - Value']
|
||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||
|
||
writer.writeheader()
|
||
|
||
for contact in contacts:
|
||
# Очищаем название организации от многострочного текста
|
||
org = contact['Организация']
|
||
if org:
|
||
org = org.split('\n')[0].strip()
|
||
org = re.sub(r',\s*\d{6},.*$', '', org)
|
||
org = org.rstrip(',').strip()
|
||
|
||
# Формируем полное имя
|
||
full_name = f"{contact['Фамилия']} {contact['Имя']} {contact['Отчество']}".strip()
|
||
|
||
gmail_contact = {
|
||
'Name': org if org else full_name,
|
||
'Given Name': contact['Имя'],
|
||
'Additional Name': contact['Отчество'],
|
||
'Family Name': contact['Фамилия'],
|
||
'Organization 1 - Name': org,
|
||
'Phone 1 - Value': contact['Телефон'],
|
||
'E-mail 1 - Value': contact['E-mail']
|
||
}
|
||
writer.writerow(gmail_contact)
|
||
|
||
print(f"\n✅ Файл для Gmail создан: contacts_gmail.csv ({len(contacts)} контактов)")
|
||
print("📧 Импортируйте его в Gmail: Контакты -> Импорт -> Выберите файл")
|
||
return contacts
|
||
|
||
|
||
def export_for_mailru():
|
||
"""Экспортирует контакты в формате для Mail.ru"""
|
||
# Читаем существующий CSV файл
|
||
contacts = []
|
||
with open('contacts.csv', 'r', encoding='utf-8') as csvfile:
|
||
reader = csv.DictReader(csvfile, delimiter=';')
|
||
contacts = list(reader)
|
||
|
||
# Создаем CSV в формате Mail.ru (простой формат)
|
||
with open('contacts_mailru.csv', 'w', encoding='utf-8', newline='') as csvfile:
|
||
# Mail.ru использует простые заголовки
|
||
fieldnames = ['First Name', 'Last Name', 'E-mail Address', 'Home Phone', 'Company']
|
||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||
|
||
writer.writeheader()
|
||
|
||
for contact in contacts:
|
||
# Очищаем название организации от многострочного текста
|
||
org = contact['Организация']
|
||
if org:
|
||
org = org.split('\n')[0].strip()
|
||
org = re.sub(r',\s*\d{6},.*$', '', org)
|
||
org = org.rstrip(',').strip()
|
||
|
||
mailru_contact = {
|
||
'First Name': contact['Имя'] if contact['Имя'] else org,
|
||
'Last Name': contact['Фамилия'],
|
||
'E-mail Address': contact['E-mail'],
|
||
'Home Phone': contact['Телефон'],
|
||
'Company': org
|
||
}
|
||
writer.writerow(mailru_contact)
|
||
|
||
print(f"\n✅ Файл для Mail.ru создан: contacts_mailru.csv ({len(contacts)} контактов)")
|
||
print("📧 Импортируйте его в Mail.ru: Настройки -> Контакты -> Импорт")
|
||
return contacts
|
||
|
||
|
||
def export_to_vcard():
|
||
"""Экспортирует контакты в формат VCard (.vcf)"""
|
||
# Читаем существующий CSV файл
|
||
contacts = []
|
||
with open('contacts.csv', 'r', encoding='utf-8') as csvfile:
|
||
reader = csv.DictReader(csvfile, delimiter=';')
|
||
contacts = list(reader)
|
||
|
||
with open('contacts.vcf', 'w', encoding='utf-8') as vcf_file:
|
||
for contact in contacts:
|
||
# Очищаем название организации от многострочного текста
|
||
org = contact['Организация']
|
||
if org:
|
||
org = org.split('\n')[0].strip()
|
||
org = re.sub(r',\s*\d{6},.*$', '', org)
|
||
org = org.rstrip(',').strip()
|
||
|
||
# Формируем VCard для каждого контакта
|
||
vcf_file.write("BEGIN:VCARD\n")
|
||
vcf_file.write("VERSION:3.0\n")
|
||
|
||
# Имя
|
||
if contact['Фамилия'] or contact['Имя']:
|
||
vcf_file.write(f"FN:{contact['Фамилия']} {contact['Имя']} {contact['Отчество']}\n".strip() + "\n")
|
||
vcf_file.write(f"N:{contact['Фамилия']};{contact['Имя']};{contact['Отчество']};;\n")
|
||
else:
|
||
# Если нет имени, используем организацию
|
||
vcf_file.write(f"FN:{org}\n")
|
||
|
||
# Организация
|
||
if org:
|
||
vcf_file.write(f"ORG:{org}\n")
|
||
|
||
# Email
|
||
if contact['E-mail']:
|
||
vcf_file.write(f"EMAIL;TYPE=INTERNET:{contact['E-mail']}\n")
|
||
|
||
# Телефон
|
||
if contact['Телефон']:
|
||
vcf_file.write(f"TEL;TYPE=WORK,VOICE:{contact['Телефон']}\n")
|
||
|
||
vcf_file.write("END:VCARD\n")
|
||
|
||
print(f"\n✅ Файл VCard создан: contacts.vcf ({len(contacts)} контактов)")
|
||
print("📱 Импортируйте его в почтовый клиент или телефон")
|
||
return contacts
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("=" * 60)
|
||
print("ПАРСЕР ТЕЛЕФОННОЙ КНИГИ")
|
||
print("=" * 60)
|
||
|
||
# Сначала парсим Word документ
|
||
print("\n📖 Парсинг телефонной книги из Word...")
|
||
contacts = parse_phone_book()
|
||
|
||
# Затем создаем файлы для импорта
|
||
print("\n" + "=" * 60)
|
||
print("СОЗДАНИЕ ФАЙЛОВ ДЛЯ ИМПОРТА")
|
||
print("=" * 60)
|
||
|
||
export_for_mailru()
|
||
export_for_gmail()
|
||
export_to_vcard()
|
||
|
||
# Выводим статистику
|
||
print("\n" + "=" * 60)
|
||
print("ПРИМЕРЫ КОНТАКТОВ:")
|
||
print("=" * 60)
|
||
for i, contact in enumerate(contacts[:5], 1):
|
||
org = contact['Организация'].split('\n')[0][:50]
|
||
print(f"{i}. {org}... - {contact['E-mail']}")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("✅ ГОТОВО! Используйте файлы для импорта:")
|
||
print(" 📧 contacts_mailru.csv - для Mail.ru")
|
||
print(" 📧 contacts_gmail.csv - для Gmail, Outlook")
|
||
print(" 📱 contacts.vcf - для Apple Mail, телефонов")
|
||
print("=" * 60) |