На этой неделе я решил взглянуть на Кукольника. Я знал, что это способ создавать небольшие автоматизированные сценарии браузера, и был рад попробовать. Я сделал простой скрипт для поиска на веб-сайте документации JavaScript MDN метода Array, возврата определения метода и ссылки на веб-страницу метода Array. Это было доказательством концепции, просто чтобы я разобрался с Кукловодом.

Если вы с ним не знакомы, Puppeteer - это библиотека Node, которая предоставляет высокоуровневый API для управления Chrome или Chromium через DevTools Protocol. По умолчанию он работает без головы, но может быть настроен для работы в полном объеме (без головы).

Для этого проекта я решил также использовать пакеты Chalk и Inquirer. Мел позволит мне легко отформатировать вывод в терминале, а Inquirer позволит мне легко создать пользовательский интерфейс командной строки.

Я создал новую папку с именем searchMDN, перешел к ней в терминале и установил три зависимости.

$ npm i puppeteer chalk inquirer

Создайте новый файл JavaScript с именем app.js, и для начала я создам пользовательское приглашение, которое будет отображаться в терминале. Я также зашел в файл package.json и установил команду запуска на «node app.js»…

Создание пользовательской подсказки

Сначала потребуйте пакет inquirer в нашем файле.

const inquirer = require('inquirer')

Теперь, когда он доступен, я могу использовать его для создания подсказки пользователю. Я просто хочу, чтобы подсказка была «Введите метод массива для поиска». У Inquirer есть много вариантов того, как подсказывать пользователю, но мне не нужно много - достаточно его самого основного запроса, метода «prompt ()». Этот метод потребует от меня указать тип приглашения. Я хочу, чтобы пользователь написал имя метода, который он хочет найти. Для такого типа пользовательского ввода я буду использовать запросчик типа «ввод». Затем я должен назвать ответ, который я получаю от пользователя. Я назову его userInput.

inquirer.prompt({
  type: 'input',
  name: 'userInput',
  message: 'Enter array method to search for'
})

Затем мне нужно взять userInput и отправить его функции, которая использует Puppeteer для поиска на веб-сайте MDN и возврата результатов. Поскольку метод prompt () возвращает обещание, я воспользуюсь методом .then (), возьму результат userInput и передам его функции «getResults ()», которую я создам с помощью Puppeteer. Я также заключу это в функцию под названием start (). Поскольку эта функция должна быть первой, что вызывается и запускает функцию getResults (), имеет смысл вызвать start () в конце файла для инициализации этого сценария поиска метода массива.

const start = () => {
  inquirer.prompt({
    type: 'input',
    name: 'userInput',
    message: 'Enter array method to search for'
  })
  .then(resp => getResults(resp.userInput))
}

Автоматизация с Puppeteer

Как и выше, мне нужен пакет Puppeteer в файле app.js.

const puppeteer = require('puppeteer')

Все функции, которые мне нужны от Puppeteer, будут асинхронными, что имеет смысл. Мне нужно создать экземпляр браузера Chrome, открыть новую страницу, загрузить веб-сайт, найти идентификатор строки поиска, ввести поисковый запрос, дождаться загрузки новой страницы, затем найти первый результат, сохранить результат url в переменной, перейдите на веб-страницу результата, найдите определение, сохраните определение в переменной и выведите его в последнюю очередь на консоль. Мне нужен код, чтобы дождаться завершения каждой из этих задач, прежде чем двигаться дальше. Поэтому я собираюсь обернуть все это асинхронной функцией.

async function getResults(term) {
  const browser = await puppeteer.launch()
  const page = await browser.newPage() 
}

Выше я создал функцию async getResults (), которая принимает параметр, отправляемый функцией start () поискового запроса, который я буду использовать для запроса на сайте MDN. Я создаю экземпляр браузера Chrome без головы с помощью puppeteer.launch (). Как только это будет завершено, код создаст новую страницу. Затем нам нужно предоставить Puppeteer URL-адрес, по которому я хочу начать поиск.

Когда я разрабатывал этот сценарий, я включил возможность создания полноценного браузера (без заголовка). Таким образом, продвигаясь к каждому шагу, я мог быстро понять в браузере, какой следующий шаг мне нужно сделать. Я сделал это, вызвав опцию {headless: false} в puppeteer.launch ().

Я использовал домашнюю страницу разработчика и установил локаль на en-US, чтобы получить результаты только на английском языке. Используя инструменты разработчика Chrome, я обнаружил, что строка поиска на сайте имеет html-идентификатор «main-q».

const mdnUrl = 'https://developer.mozilla.org/en-US/search?locale=en-US'
async function getResults(term) {
  const browser = await puppeteer.launch({ headless: false })
  const page = await browser.newPage()
  await page.goto(mdnUrl)
  await page.type('#main-q', `Array.prototype.${term}`)
  await page.keyboard.down('Enter')
}

Используя page.type, я выбираю элемент, в который хочу ввести текст, и во втором параметре текст, который хочу ввести. Я ищу только методы массива, поэтому я введу термин в конце строки «Array.prototype.». После нажатия клавиши ВВОД отправляется поисковый запрос.

Теперь нам нужно дождаться загрузки новой страницы и найти первый результат. Все результаты на странице имеют класс «результат», а каждый заголовок имеет класс «заголовок-результат». Если бы я просто вызвал querySelector для «заголовка результата», он бы получил первый результат, а затем все, что осталось сделать, это получить ссылку из заголовка результата.

const mdnUrl = 'https://developer.mozilla.org/en-US/search?locale=en-US'
async function getResults(term) {
  const browser = await puppeteer.launch({ headless: false })
  const page = await browser.newPage()
  await page.goto(mdnUrl)
  await page.type('#main-q', `Array.prototype.${term}`)
  await page.keyboard.down('Enter')
  await page.waitForSelector('.result')
  const resultUrl = await page.evaluate(() => {
    const topResult = document.querySelector('.result-title')
    return topResult.href
  })
}

Хорошо, почти готово! У меня есть URL-адрес определения метода, теперь все, что мне нужно, это захватить определение для вывода на терминал.

Следуя аналогичной схеме, я открываю страницу с URL-адресом, который мы сохранили в константе resultUrl. Определение находится в самом верхнем теге ‹p›, поэтому я выбираю его с помощью querySelector, а затем сохраняю innerText в константу resultDef. После этого мы закончили с браузером, и нам нужно будет закрыть его, чтобы он больше не работал в фоновом режиме.

const mdnUrl = 'https://developer.mozilla.org/en-US/search?locale=en-US'
async function getResults(term) {
  const browser = await puppeteer.launch({ headless: false })
  const page = await browser.newPage()
  await page.goto(mdnUrl)
  await page.type('#main-q', `Array.prototype.${term}`)
  await page.keyboard.down('Enter')
  await page.waitForSelector('.result')
  const resultUrl = await page.evaluate(() => {
    const topResult = document.querySelector('.result-title')
    return topResult.href
  })
  await page.goto(resultUrl)
  
  const resultDef = await page.evaluate(() => {
    const termDef = document.querySelector('p')
    return termDef.innerText
  })
  browser.close()
}

Теперь у меня есть все необходимые данные, и единственное, что мне нужно сделать, это вывести результаты на консоль.

Вывод на консоль

Затем мне нужно добавить пакет Chalk в файл app.js.

const chalk = require('chalk')

У Chalk есть масса вариантов форматирования текста, и самое приятное то, что все они на простом английском языке, без шестнадцатеричных значений или значений RGB. Что ж, вы можете использовать значения RGB с Chalk, если хотите, но я буду просто. Я просто хочу добавить немного цвета, чтобы определение и URL-адрес казались разделенными, а вывод было легче читать.

console.log(chalk.magentaBright(resultDef))
console.log(chalk.underline.dim(resultUrl))

Я делаю текст resultDef ярким пурпурным цветом, а для URL-адреса я подчеркиваю его и затемняю его цвет по сравнению с цветом по умолчанию для консоли, в которой он отображается.

Теперь я удаляю опцию {headless: false} из puppeteer.launch (), и на этом этот простой сценарий проверки концепции завершен.

Для получения дополнительной информации о любом из этих пакетов ознакомьтесь с соответствующими ссылками ниже.

Кукольник

Мел

Спрашивающий