У моїй попередній компанії я розробив пакетне завдання, яке відстежувало показники в соціальних мережах, таких як Twitter, LinkedIn, Mastodon, Bluesky, Reddit тощо. Потім я зрозумів, що можу скопіювати це для своєї власної «персони». Проблема полягає в тому, що деякі засоби масової інформації не надають API HTTP для потрібних мені показників. Ось показники, які мені потрібні в LinkedIn:
Я довго шукав, але не знайшов доступу до API для наведених вище показників. Довгий час я збирав показники вручну щоранку і нарешті вирішив автоматизувати це виснажливе завдання. Ось що я дізнався.
Контекст
Робота виконана на Python, тому я хочу залишатися в тому самому технічному стеку. Після швидкого дослідження я знайшов Playwright, інструмент автоматизації браузера з кількома мовними API, включаючи Python. Основним випадком використання Playwright є наскрізне тестування, але він також може керувати браузером поза контекстом тестування.
Я використовую Poetry для керування залежностями. Встановити Playwright так само просто:
poetry add playwright
На цьому етапі Playwright готовий до використання. Він пропонує два різних API, один синхронний і один асинхронний. Через мій варіант використання першого смаку більш ніж достатньо.
Мочу ноги
Мені подобається підходити до розвитку поступово.
Ось уривок API:
Це перетворюється на такий код:
from playwright.sync_api import Browser, Locator, Page, sync_playwright
with (sync_playwright() as pw): #1
browser: Browser = pw.chromium.launch() #2
page: Page = browser.new_page() #3
page.goto('https://www.linkedin.com/login') #4
page.locator('#username').press_sequentially(getenv('LINKEDIN_USERNAME')) #5
page.locator('#password').press_sequentially(getenv('LINKEDIN_PASSWORD')) #5
page.locator('button[type=submit]').press('Enter') #6
page.goto('https://www.linkedin.com/dashboard/') #4
metrics_container: Locator = page.locator('.pcd-analytic-view-items-container')
metrics: List[Locator] = metrics_container.locator('p.text-body-large-bold').all() #7
impressions = atoi(metrics[0].inner_text()) #8
# Get other metrics
browser.close() #9
-
отримати a
playwright
об'єкт. -
Запустіть екземпляр браузера. Доступно декілька типів браузерів; Я вибрав Chromium з примхи. Зверніть увагу, що ви повинні були встановити певний браузер раніше, тобто,
playwright install --with-deps chromium
.За замовчуванням відкривається браузер безголовий; воно не з'являється. Я б порадив запустити його видимо на початку для легшого налагодження:
headless = True
. -
Відкрийте нове вікно браузера.
-
Перейдіть до нового місця.
-
Знайдіть вказані поля введення та заповніть їх моїми обліковими даними.
-
Знайдіть зазначену кнопку та натисніть її.
-
Знайдіть все зазначені елементи.
-
Отримайте внутрішній текст першого елемента.
-
Закрийте браузер, щоб очистити.
Зберігання файлів cookie
Вищезазначене спрацювало, як очікувалося. Єдиним мінусом є те, що я отримував електронний лист від LinkedIn кожного разу, коли запускав сценарій:
Привіт Ніколас,
Ви успішно активували Запам'ятати мене на новому пристрої HeadlessChrome,
в . Дізнайтеся більше про те, як Запам’ятати мене працює на пристрої., ,
Я також познайомився з Фаб’єном Вошеллем на конференції JavaCro. Він спеціалізується на веб-збиранні та сказав мені, що більшість людей у цій галузі використовують профілі браузера. Дійсно, якщо ви ввійдете в LinkedIn, ви отримаєте маркер автентифікації, збережений у вигляді файлів cookie, і вам не потрібно буде повторно автентифікувати його до закінчення терміну дії. На щастя, Playwright пропонує таку функцію launch_persistent_context
метод.
Ми можемо замінити вищезазначене launch
з наступним:
with sync_playwright() as pw:
playwright_profile_dir = f'{Path.home()}/.social-metrics/playwright-profile'
context: BrowserContext = pw.chromium.launch_persistent_context(playwright_profile_dir) #1
try: #2
page: Page = context.new_page() #3
page.goto('https://www.linkedin.com/dashboard/') #4
if 'session_redirect' in page.url: #4
page.locator('#username').press_sequentially(getenv('LINKEDIN_USERNAME'))
page.locator('#password').press_sequentially(getenv('LINKEDIN_PASSWORD'))
page.locator('button[type=submit]').press('Enter')
page.goto('https://www.linkedin.com/dashboard/')
metrics_container: Locator = page.locator('.pcd-analytic-view-items-container')
# Same as in the previous snippet
except Exception as e: #2
logger.error(f'Could not fetch metrics: {e}')
finally: #5
context.close()
-
Playwright зберігатиме профіль у вказаній папці та повторно використовуватиме його під час серії.
-
Покращити обробку винятків.
-
The
BrowserContext
також може відкривати сторінки. -
Пробуємо перейти до приладової панелі. LinkedIn перенаправить нас на сторінку входу, якщо ми не пройшли автентифікацію; тоді ми можемо автентифікувати.
-
Закрийте контекст незалежно від результату.
На цьому етапі нам потрібно лише пройти автентифікацію з обома обліковими даними вперше. Це залежить від наступних прогонів.
Адаптація до реальності
Я був здивований, побачивши, що код вище не працює надійно. Це спрацювало при першому запуску, а іноді і при наступних. Оскільки я зберігаю профіль веб-переглядача під час запусків, коли мені потрібно автентифікуватися, LinkedIn запитує лише пароль, а не ім’я для входу! Оскільки код намагається ввести логін, у цьому випадку це не вдається. Виправлення досить просте:
username_field = page.locator('#username')
if username_field.is_visible():
username_field.press_sequentially(getenv('LINKEDIN_USERNAME'))
page.locator('#password').press_sequentially(getenv('LINKEDIN_PASSWORD'))
Висновок
Хоча я не експерт у Python, мені вдалося досягти того, чого я хотів, із Playwright. Я віддав перевагу використанню API синхронізації, тому що це полегшує розробку коду, і я не маю жодних вимог до продуктивності. Я використовував лише основні функції, запропоновані Playwright. Playwright дозволяє записувати відео в контексті тестів, що дуже корисно, коли тест не вдається під час виконання конвеєра CI.
Щоб піти далі:
Вперше опубліковано на сайті A Java Geek 19 січня 2024 року