Доброго времени суток всем!
На написание данной статьи меня вдохновила эта публикация.
К сожалению, в этой статье не было способа для отправки файлов в Whatsapp и я решил поправить это.
Итак, необходимо:
Python (у меня 3.10.10)
библиотеки к нему (полный список их чуть ниже)
зарегистрированный номер телефона в Whatsapp
Windows компьютер
... ну и желание
Теперь обещанный requrements.txt
async-generator==1.10
attrs==23.1.0
certifi==2023.5.7
cffi==1.15.1
charset-normalizer==3.1.0
colorama==0.4.6
exceptiongroup==1.1.1
h11==0.14.0
idna==3.4
outcome==1.2.0
packaging==23.1
PyAutoIt==0.6.5
pycparser==2.21
PySocks==1.7.1
python-dotenv==1.0.0
requests==2.30.0
selenium==4.9.1
sniffio==1.3.0
sortedcontainers==2.4.0
tqdm==4.65.0
trio==0.22.0
trio-websocket==0.10.2
urllib3==2.0.2
webdriver-manager==3.8.6
wsproto==1.2.0
Установка вышеуказанных пакетов, за исключением PyAutoIt, производится через
pip install -r requrements.txt
и проблем не вызовет.
Установка PyAutoit нужна потому, что Selenium, на момент написания статьи, не может послать текст и команды за пределы браузера. А нужно указать Windows Explorer что за файл подлежит отправке. К слову, AutoIt - прекрасный скриптованный язык для тех, кто хочет автоматизировать работу с приложениями, которые не имеют API. Он запросто может находить, вводить текст и нажимать на кнопочки в windows приложениях.
Поэтому устанавливаем его отдельно.
Скачиваем дистрибутив отсюда
Раскрываем архив, если тащили не через git clone
Переходим в папку pyautoit-master
И запускаем python setup.py install... впрочем в README.md этого пакета написано больше ;).
Ну вот, теперь мы готовы запустить файл whatsapp.py. Вот он
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from time import sleep
import argparse
import autoit
options = webdriver.ChromeOptions()
# если у вас другой браузер, например FireFox, мы просто пишем options = webdriver.FirefoxOptions() .
# if you have the Mozilla Fifefox just write options = webdriver.FirefoxOptions()
options.add_argument('--allow-profiles-outside-user-dir')
options.add_argument('--enable-profile-shortcut-manager')
# УКАЖИТЕ ПУТЬ ГДЕ ЛЕЖИТ ВАШ python ФАЙЛ. Советую создать отдельную папку для него
# Specify the path to where your python file is located. I suggest you create a separate folder for it
options.add_argument(r'user-data-dir=x:\\yyyyyy\\zzzzzz\\')
options.add_argument('--profile-directory=Profile 1')
options.add_argument('--profiling-flush=n')
options.add_argument('--enable-aggressive-domstorage-flushing')
# эти опции нужны чтобы подавить любые сообщения об ошибках SSL, сертификатов и т.п. Но работает только последняя :(
# these options need to disabled any messages about bad ssl, certification & etc
options.add_argument('--ignore-certificate-errors-spki-list')
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
options.add_argument('log-level=3')
# INFO = 0,
# WARNING = 1,
# LOG_ERROR = 2,
# LOG_FATAL = 3.
# default is 0.
# Константы для Selenium
# Constants for Selenium
xpath = "//button[@data-testid='compose-btn-send']"
xpathAttach = "//div[@data-testid='conversation-clip']"
# xpathFile = "//button[@data-testid='attach-document']"
#xpathSendButton = "//div[@data-testid='send']" # data-testid = "send"
cssIdOfDocument = "[aria-label='Документ']"
cssIdOfSendButton = "[aria-label='Отправить']"
# Константы для AutoIt
# Constant for Autoit
idExplorerOpen = "[CLASS:#32770]"
idInputLine = "[CLASS:Edit; INSTANCE:1]"
idOpenButton = "Button1"
# номера телефонов для отправки
# numbers of phones to send
numbers = ["+7xxxxxxxxxx", "+7xxxxxxxxxx", "+7xxxxxxxxxx"]
# текст по умолчанию
# default text
text = "Ничего нет"
# Мы запускаем браузер
# We are starting a browser
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
# ждём его загрузки
# it takes some time to load it
wait = WebDriverWait(driver, 30)
def main(args):
for number in numbers:
# создаём url страницы с телефонныи номером и текстом для отправки
# create a url page with a phone number and text to send
url = f"https://web.whatsapp.com/send?phone={number}&text={args.text}"
# идём туда
# go to there
driver.get(url)
# ждём загрузки страницы Whatsapp
# we are waiting for Whatsapp page to load
wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
if not (args.file is None) :
# нужно отправить файл
# ищем кнопку Attach
# need to send file
# now we look for the Attach Button and click on it
driver.find_element(By.XPATH, xpathAttach).click()
# затем ищем кнопку "Документ" и щелкаем на ней. Используется атрибут CCS, потому что у него нет ID
# then we look for the Document Button and click on it. Used CCS attribute because it doesn't have any ID
driver.find_element(By.CSS_SELECTOR, cssIdOfDocument).click()
# подождать, когда активируется окно проводника Windows
# wait when the Windows's explorer window to activate
autoit.win_wait_active(idExplorerOpen, 5)
# затем отправляем путь к файлу в строку ввода
# then we are sending the path of the file to input line
autoit.control_send(idExplorerOpen, idInputLine, args.file)
# и нажимаем кнопку "Открыть"
# and are clicking the Open Button
autoit.control_click(idExplorerOpen, idOpenButton)
# немного ждём загрузки файла
# it takes some time to load a file
sleep(5)
# ищем кнопку "Отправить" и нажимаем на нее. Используется атрибут CCS, потому что у него нет ID
# we look for the Send Button and click on it. Used CCS attribute because it doesn't have any ID
driver.find_element(By.CSS_SELECTOR, cssIdOfSendButton).click()
else:
# теперь ищем кнопку "Отправить" и нажимаем на нее
# now we look for the Send Button and click on it
driver.find_element(By.XPATH, xpath).click()
# требуется некоторое время для отправки файла или сообщения
# it takes some time to send a file or a message
sleep(5)
# закрыть все
# close all
driver.quit()
if __name__ == '__main__':
# мы разбираем параметры командной строки
# we are parsing command line parameters
parser = argparse.ArgumentParser(description='Send information by Whatsapp')
parser.add_argument('--text', help='Text for send', required=False, default = text)
parser.add_argument('--file', help='File for send', required=False)
args = parser.parse_args()
# начать отправку
# start sending
main(args)
Я постарался документировать в комментариях практически все важные моменты этого приложения, причём на двух языках: на русском и на английском, если кто-то вдруг не владеет русским ;). Поэтому не буду отдельно расписывать его части, но если что-то не понятно, то пишите в комментарии. Постараюсь ответить.
Приложение запускается так
python whatsapp.py --text "Я посылаю тебе ... или тебя ;)"
если нужно отправить текст
или так
python --text "Это очень важный файл" --file "x:\yyyyy\zzzzz\fileToSend.ext"
если нужно отправить файл с пояснением
Первый запуск приложения потребует скачивания драйверов, обязательно согласитесь с этим. Также нужно будет зарегистрировать whatsapp клиента на этом компьютере.
Дальше оно обычно запускается и оправляет всё без вопросов.
Соглашусь с автором поста, который меня вдохновил и процитирую его:
что данная статья не создана с целью научить вас спамить, флудить и портить жизнь людям иными способами. Этот материал несёт исключительно ознакомительный характер. Думайте, перед тем, как что-то делать.
Всем удачи!
TyVik
Я в шоке от вашего requirements.txt. Неужели всё это действительно необходимо?
longvalery Автор
Увы. Это действительно так. Я ставил в чистом, виртуальном окружении всего три пакета, но pip притащил все зависимости.
Позвольте спросить: "А в чем проблема с длинным списком зависимых пакетов?"
TyVik
А можете сказать что это были за 3 пакета?
Мне казалось, что colorama и tqdm более чем опциональные. Странно видеть синхронный requests и trio вместе. Аналогично trio и exceptiongroup...
В большом списке зависимостей нет проблемы, если он оправдан.
longvalery Автор
Через pip
webdriver_manager
selenium
argparse
И отдельно, как описано выше, через setup.py - PyAutoit
Возможно, именно последний притащил много лишнего на Ваш взгляд.
Но я не заморачиваюсь подобными вопросами и не делаю freeze и анализ после каждой установки пакета.