Рубрики
Без рубрики

Создание API поисковой системы с узлом / Express и uppereer с использованием поиска Google

В этом посте мы собираемся построить API поисковой системы с узлом / Express & Cuppeteer. Это будет нам … Помечено JavaScript, узел, uppereer, WebDev.

В этом посте мы собираемся построить API поисковой системы с узлом/Express & Cuppeteer. Это будет использовать Web Scraping, чтобы получить лучшие результаты от Google

Если вы еще не прочитали первый пост, я настоятельно рекомендую прочитать его! Он проходит через основы веб-соскабливания с кукловом.

Примечание. К сожалению, концепции, обсуждаемые в части 2 и 3, все еще действительны, однако примеры, используемые для демонстрации этих концепций, больше не работают. Это характер веб-соскоба. Если веб-сайт решает изменить имя своего класса для определенного HTML-элемента, то веб-стирал должен быть отрегулирован на эти названия классов. В этом примере мы использовали имена классов, которые Google использовался во время написания этого поста, однако эти названия классов изменились с тех пор, и поэтому пример больше не работает.

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

Это часть a 3 части серии :

  1. 1-й Часть Основы кукловода и создание простого веб-скребка.
  2. 2-я часть: Создание API поисковой системы с использованием поиска Google с узлом/Express и Cuppeteer.
  3. 3-я часть: Оптимизация наших API, увеличение производительности, устранения неисправностей Основы и развертывание нашего API uppeter в Интернет.

Содержание – часть 2

  • API требования
  • Настройка узла/экспресс-сервера
  • Создание API поисковой системы с uppereer

API требования

Прежде чем мы начнем, важно знать, что мы пытаемся построить. Мы собираемся построить API, который примет запрос на поиск и вернуть JSON С топовыми результатами от результатов поиска Google.

Информация, которую мы заботимся о результатах:

  • Название сайта
  • Описание Вебсайта
  • ссылка на сайт

Запрос на поиск будет Получить запрос и мы собираемся использовать URL запросы params Чтобы указать поисковый запрос. Пользователь отправит запрос на /поиск С поиском запроса Searchery = Cats :

localhost:3000/search?searchquery=cat

Ожидается, что наша API вернет лучшие результаты о кошках из Google в JSON :

[
    {
      title: 'Cats Are Cool',
      description: 'This website is all about cats and cats are cool',
      url: 'catsarecool.com'
    },
    ...
    {
      title: 'Cats funny videos',
      description: 'Videos all about cats and they are funny!',
      url: 'catsfunnyvideos.com'
    }
]

Теперь, когда мы знаем наши требования, мы можем пойти дальше начать строить нашу API

Настройка узла/экспресс-сервера

Если вы хотите пропустить настройку сервера узла/Express, вы можете пропустить прямо до того, как мы начнем писать код для Cuppeter, чтобы сканировать Google. Хотя я рекомендую прочитать эту часть.

Чтобы начать, мы собираемся создать новый каталог проекта и инилизом NPM:

mkdir search-engine-api
cd search-engine-api
npm init -y

Для этого API мы собираемся использовать Express.js создать простое API И поэтому нам нужно установить выражать , Кукла и узел . Мы собираемся использовать Номемон для развития. Номемон Обнаружете какие-либо изменения в нашем файле сервера и автоматически перезагрузите наш сервер. Это сэкономит нам время в долгосрочной перспективе.

npm i express puppeteer nodemon

Теперь мы можем создать наш файл сервера:

touch server.js

После этого нам нужно настроить наши package.json и добавлять скрипты для NPM начать начать наш сервер. Для целей развития мы можем создать скрипт с Номемон Отказ Мы будем использовать NPM запустить Dev Для запуска сценария для удостоверения лиц:

{
  "name": "search-engine-api",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "nodemon": "^2.0.2",
    "puppeteer": "^2.0.0"
  }
}

Теперь, если мы запустим NPM запустить Dev и попытаться внести изменения в нашу Server.js Файл, Nodemon автоматически перезапустит сервер. Теперь мы можем начать писать код для нашего сервера.

Прежде чем мы попадем в создание наших API, нам нужно настроить простой Экспресс сервер. Мы собираемся использовать Здравствуйте, мир Пример предоставлен Экспресс Документы :

const express = require('express');
const app = express();
const port = 3000;

//Catches requests made to localhost:3000/
app.get('/', (req, res) => res.send('Hello World!'));


//Initialises the express server on the port 30000
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Это создает экспресс-сервер на порту 3000 нашей локальной машины. Если кто-то посылает Получить Запрос на localhost: 3000/ Наш сервер отвечает Здравствуйте, мир Отказ Мы можем видеть, что он работает, открыв URL localhost: 3000/ в браузере.

Мы собираемся создать новый маршрут для нашего поиска. Это где мы пройдем информацию в URL с Запросы параметров Например, если мы хотим результаты поиска для запроса «Собаки», мы можем отправить запрос на:

localhost:3000/search?searchquery=dogs

Для реализации этого нам нужно создать новый Получить Запросить функцию с Express и так как мы ожидаем, что это будет Получить Запрос, мы можем использовать app.get (маршрут, callbackfunc)

const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
const port = 3000;

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {
    //Do something when someone makes request to localhost:3000/search
    //request parameter - information about the request coming in
   //response parameter - response object that we can use to send a response
});

//Catches requests made to localhost:3000/
app.get('/', (req, res) => res.send('Hello World!'));


//Initialises the express server on the port 30000
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Теперь, когда у нас есть функция, которая ловит запросы, сделанные в localhost: 3000/Поиск Мы можем начать смотреть, как мы можем использовать какие-либо параметры запроса, которые находятся в URL. Любые запросы, сделанные на этот маршрут, выполнит функцию обратного вызова в этом обработчике.

Express позволяет нам получить доступ к параметрам запроса через параметр запроса. В нашем случае, так как мы назвали наше поле запроса Searchery мы можем получить доступ через это:

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {

  //Holds value of the query param 'searchquery' 
    const searchQuery = request.query.searchquery;
});

Однако, если этот запрос не существует, то нам нечего искать, поэтому мы можем справиться с этим случаем, только делая что-то, когда будет предоставлен поисковый запрос. Если поисковый запрос не существует, мы сможем быстро прекратить ответ без каких-либо данных с помощью Ответ.end ()

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {

  //Holds value of the query param 'searchquery'.
    const searchQuery = request.query.searchquery;

  //Do something when the searchQuery is not null.
  if(searchQuery != null){

  }else{
    response.end();
  }
});

Теперь, когда у нас есть наш узел/Express Server Server, мы можем начать запись кода для нашего скребка.

Создание API поисковой системы с uppereer

Когда дело доходит до веб-соскабливания Google, один из способов поиска что-то непосредственно в Google Search – пройти поисковый запрос в виде параметра запроса URL:

https://www.google.com/search?q=cat

Это покажет нам результаты для ключевого слова «кошка» в Google. Это был бы идеальным подходом, однако, с целью этого поста мы собираемся делать вещи трудным путем, открыв Google.com (Главная страница) иметь Кукла Введите окно поиска и нажмите Введите получить результаты.

Мы сделаем это таким образом, потому что не все сайты используют параметры запроса, а иногда и единственным способом добраться до следующего шага сайта (в нашем случае страница результатов) – это делать вещи вручную на первом шаге.

На данный момент наша Server.js Похоже:

const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
const port = 3000;

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {

    //Holds value of the query param 'searchquery'.
    const searchQuery = request.query.searchquery;

    //Do something when the searchQuery is not null.
    if(searchQuery != null){

    }else{
      response.end();
    }
});

//Catches requests made to localhost:3000/
app.get('/', (req, res) => res.send('Hello World!'));


//Initialises the express server on the port 30000
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Мы собираемся создать новую функцию под названием SearchGoogle Отказ Это займет в Searchery В качестве входного параметра и верните массив JSON с лучшими результатами.

Прежде чем мы продолжим писать SearchGoogle с uppeteer. , мы собираемся написать след функции, поэтому мы знаем, как код должен вести себя:

const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
const port = 3000;

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {

    //Holds value of the query param 'searchquery'.
    const searchQuery = request.query.searchquery;

    //Do something when the searchQuery is not null.
    if (searchQuery != null) {

        searchGoogle(searchQuery)
            .then(results => {
                //Returns a 200 Status OK with Results JSON back to the client.
                response.status(200);
                response.json(results);
            });
    } else {
        response.end();
    }
});

//Catches requests made to localhost:3000/
app.get('/', (req, res) => res.send('Hello World!'));


//Initialises the express server on the port 30000
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

С Кукла работает асинхронно , нам нужно ждать, пока результаты будут возвращены из SearchGoogle Отказ По этой причине нам нужно добавить .then так что мы обязательно подожди, пока SearchGoogle Процессы и извлекают результаты, прежде чем мы использовали их, и результаты могут быть доступны с функцией обратного вызова, которая будет иметь результаты в качестве первого параметра. После чего мы можем ответить обратно клиенту, используя Ответ.json () Отказ

Ответ.json () Возвращает JSON Вернуться к клиенту. Существуют разные методы, которые вы можете использовать с ответом. Вы можете прочитать больше о них по официальному Экспресс Документы Отказ

Теперь мы можем начать писать код и создавать функцию uppeter SearchGoogle Отказ Для этого мы собираемся создать новый файл в одном каталоге. Это связано с тем, что имея отдельный файл позволит нам проверить наш файл uppeter, без необходимости делать ручной запрос на наш сервер, который может быть процессом по времени. Мы назовем это searchgoogle.js :

touch searchGoogle.js

Теперь нам нужно инициализировать функцию в файле:

const puppeteer = require('puppeteer');

const searchGoogle = async (searchQuery) => {
    const browser = await puppeteer.launch();

    const page = await browser.newPage();
    await page.goto('https://google.com');

    await browser.close();
};

export default searchGoogle;

Прямо сейчас мы просто запускаем безголовый экземпляр Chrome и просмотра в Google. Нам нужно теперь найти панель поиска, где мы можем написать запрос. Для этого нам нужно проверить исходный код домашней страницы Google.

После использования инструмента мыши для выбора элементов мы можем увидеть HTML Для этой панели поиска:

Мы видим, что у него есть Имя = "Q" Мы можем использовать его, чтобы идентифицировать и нацелиться на вход через uppeteer. . Чтобы ввести на наш поисковый запрос, Cuppeteer предоставляет функцию для страницы Page.type (селектор, текстотип) Отказ С этим мы можем нацелить любые формы и вводить наши значения напрямую:

const puppeteer = require('puppeteer');

const searchGoogle = async (searchQuery) => {
    const browser = await puppeteer.launch();

    const page = await browser.newPage();
    await page.goto('https://google.com');

    //Finds input element with name attribue 'q' and types searchQuery
    await page.type('', searchQuery);

  await browser.close();
};

export default searchGoogle;

Просто чтобы убедиться, что все работает, мы можем взять скриншот после того, как это сделано ввести:

const puppeteer = require('puppeteer');

const searchGoogle = async (searchQuery) => {
    const browser = await puppeteer.launch();

    const page = await browser.newPage();
    await page.goto('https://google.com');

    //Finds input element with name attribue 'q' and types searchQuery
    await page.type('', searchQuery);

    await page.screenshot({path: 'example.png'});

    await browser.close();
};

//Exports the function so we can access it in our server
module.exports = searchGoogle;

searchGoogle('cats');

Как вы можете видеть, в конце файла мы позвоните в SearchGoogle функция. Это так, что мы можем начать тестировать его. Теперь мы можем пойти в нашу командную строку и выполнить:

node searchGoogle.js

Через несколько секунд файл должен закончить выполнение, и вы сможете просмотреть скриншот:

Теперь все, что нам нужно сделать, это либо есть Кукла Нажмите «Enter» на клавиатуре или нажмите кнопку «Google Search» под панелью поиска.

Оба подхода являются подходящими решениями, однако, для точности мы будем иметь uppereer, нажмите «Google Search». Однако, если бы вы нажимаете, вот как вы это сделаете:

 await page.keyboard.press('Enter');

Мы собираемся снова осмотреть страницу и найти информацию о кнопке «поиск Google». Это показывает это:

Мы видим, что имеет имя «BTNK». Мы можем использовать это, чтобы нацелиться на элемент и нажмите на него:

//Finds the first input with name 'btnK', after it is found, it executes .click() DOM Event Method
await page.$eval('input[name=btnK]', button => button.click());

Добавляя его в наш файл:

const puppeteer = require('puppeteer');

const searchGoogle = async (searchQuery) => {
    const browser = await puppeteer.launch();

    const page = await browser.newPage();
    await page.goto('https://google.com');

    //Finds input element with name attribue 'q' and types searchQuery
    await page.type('', searchQuery);

    //Finds an input with name 'btnK', after so it executes .click() DOM Method
    await page.$eval('input[name=btnK]', button => button.click());

    await page.screenshot({path: 'example.png'});

    await browser.close();
};

searchGoogle('cats');

//Exports the function so we can access it in our server
module.exports = searchGoogle;

Выполнение файла и видение скриншота дает этот результат:

Нам нужно убедиться, что Google загрузит все результаты, прежде чем делать что-либо. Есть разные способы сделать это. Если мы хотим ждать определенного времени, мы можем использовать:

await page.waitFor(durationInMilliseconds)

В качестве альтернативы, если мы уже знаем элемент, который мы ищем, то мы можем использовать Waitforselector ждать uppereer загрузить первый элемент с подходящим селектором перед продолжением:

await page.waitForSelector('selector');

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

После прохождения HTML Исходный код для результатов поиска я обнаружил, что все результаты поиска хранятся в A Div с идентификатором Поиск :

Итак, мы можем использовать WaitForselector (селектор) и нацеливаться на Div с ID = Поиск :

const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.goto('https://google.com');

    //Finds input element with name attribue 'q' and types searchQuery
    await page.type('', searchQuery);

    //Finds an input with name 'btnK', after so it executes .click() DOM Method
    await page.$eval('input[name=btnK]', button => button.click());

    //Wait until the first div element with id search laods
    await page.waitForSelector('div[id=search]');

    await page.screenshot({path: 'example.png'});

    await browser.close();
};

searchGoogle('cats');

//Exports the function so we can access it in our server
module.exports = searchGoogle;

Теперь, когда наши результаты загружены, мы можем начать их разбирать. Если вы хотите пропустить часть, где мы пытаемся найти Divs с соответствующей информацией, вы можете пропустить прямо к реализации.

Если мы посмотрим посмотрите исходный код, чтобы выбрать значение из HTML, мы видим, что информация, которую мы ищем хранятся в Divs с класс = BKWMGD Однако не все Divs с этим классом содержит соответствующую информацию, некоторые из этих DIV содержат видеорегистраторы, новостные истории и т. Д. Те, которые мы заинтересованы, являются теми, что с H2 Название с Веб-результаты текст.

Если мы посмотрим, посмотрим на этот Div, мы видим, что он вложен очень глубоко. По этой причине мы собираемся использовать специальные селекторы для ориентирования глубоких детей. Основная информация хранится в Div с классом 'g' :

Мы можем нацелиться на конкретные девлины, которые мы заботимся о. Мы собираемся использовать '>' Селекторы CSS, известные как Детские комбинаторы нацелиться на вложенную информацию.

Мы можем нацелить вложенные элементы, такие как так:

Information

Для HTML-файла со структурой, как это, мы можем получить доступ к абзацу, выполнив:

'div[class=1] > div[class=2] > div[class=3] > p'

Мы можем выбрать Div с результатами:

//Finds the first div with class 'bkWMgd' and returns it
const parent = await page.$eval('div[class=bkWMgd]', result => result);

Поскольку родительская переменная представляет собой узел DOM, возвращенный из страница. $ eval () , мы можем запустить методы HTML DOM на этом объекте. Поскольку вся информация доступна в Div с классом g Мы можем установить родителя к своему непосредственному ребенку.

//Sets the parent to the div with all the information 
parent = parent.querySelector('div[class=g]');

С этим мы можем нацелить информацию, которую мы заботимся о том, эта информация видно на этом изображении:

Заголовок

//Targets h3 Website Title i.e. 'Cats  (2019 film)  - Wikipedia'
const title = parent.querySelector('div[class=rc] > div[class=r] > a >  h3').innerText;

Урл

Описание

const desc = parent.querySelector('div[class=rc] > div[class=s] > div > span[class=st]').innerText;

Теперь, когда мы знаем, как нацелеть нашу информацию, мы можем добавить это в наш файл. Мы смотрели только на анализ информации только из одного результата поиска, но есть несколько результатов поиска, поэтому нам нужно использовать Страница. $$ eval для цели Все Divs с H2 Веб-результаты и целевые дивы с классом g Здесь мы видим, что некоторые Divs имеют несколько результатов поиска:

Когда есть несколько девсов с классом g Они вложены в другой div с классом SRG Отказ Давайте начнем добавлять все это в наш код, чтобы мы могли начать помещать все части вместе. Пожалуйста, внимательно прочитайте этот код, это может показаться запутанным, но он основан на скриншоте выше.

//Find all div elements with class 'bkWMgd'
const searchResults = await page.$$eval('div[class=bkWMgd]', results => {
        //Array to hold all our results
        let data = [];

        //Iterate over all the results
        results.forEach(parent => {

            //Check if parent has h2 with text 'Web Results'
            const ele = parent.querySelector('h2');

            //If element with 'Web Results' Title is not found  then continue to next element
            if (ele === null) {
                return;
            }

            //Check if parent contains 1 div with class 'g' or contains many but nested in div with class 'srg'
            let gCount = parent.querySelectorAll('div[class=g]');

            //If there is no div with class 'g' that means there must be a group of 'g's in class 'srg'
            if (gCount.length === 0) {
                //Targets all the divs with class 'g' stored in div with class 'srg'
                gCount = parent.querySelectorAll('div[class=srg] > div[class=g]');
            }

            //Iterate over all the divs with class 'g'
            gCount.forEach(result => {
                //Target the title
                const title = result.querySelector('div[class=rc] > div[class=r] > a >  h3').innerText;

                //Target the url
                const url = result.querySelector('div[class=rc] > div[class=r] > a').href;

                //Target the description
                const desciption = result.querySelector('div[class=rc] > div[class=s] > div > span[class=st]').innerText;

                //Add to the return Array
                data.push({title, desciption, url});
            });
        });

        //Return the search results
        return data;
    });

Код выше будет разбирать страницу и получить нам наши результаты в массиве. Теперь мы можем вернуть этот массив из нашей основной функции SearchGoogle :

const puppeteer = require('puppeteer');

const searchGoogle = async (searchQuery) => {
    const browser = await puppeteer.launch();

    const page = await browser.newPage();
    await page.goto('https://google.com');

    //Finds input element with name attribue 'q' and types searchQuery
    await page.type('', searchQuery);

    //Finds an input with name 'btnK', after so it executes .click() DOM Method
    await page.$eval('input[name=btnK]', button => button.click());

    //Wait for one of the div classes to load
    await page.waitForSelector('div[id=search]');

    const searchResults = await page.$$eval('div[class=bkWMgd]', results => {
        //Array to hold all our results
        let data = [];
        ...
        ...
                //Return the search results
        return data;
    });

    await browser.close();

    return searchResults;
};

module.exports = searchGoogle;

Теперь мы можем удалить последнюю строку, где мы вручную называем функцию. Теперь мы закончили с помощью этой поисковой системы API! Теперь все, что нам нужно сделать, это импортировать эту функцию в нашем главном Server.js файл:

const express = require('express');
const app = express();
const port = 3000;

//Import puppeteer function
const searchGoogle = require('./searchGoogle');

//Catches requests made to localhost:3000/search
app.get('/search', (request, response) => {

    //Holds value of the query param 'searchquery'.
    const searchQuery = request.query.searchquery;

    //Do something when the searchQuery is not null.
    if (searchQuery != null) {

        searchGoogle(searchQuery)
            .then(results => {
                //Returns a 200 Status OK with Results JSON back to the client.
                response.status(200);
                response.json(results);
            });
    } else {
        response.end();
    }
});

//Catches requests made to localhost:3000/
app.get('/', (req, res) => res.send('Hello World!'));


//Initialises the express server on the port 30000
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Теперь, если мы начнем наш сервер с NPM начать И пойти в наш браузер и просмотрите:

http://localhost:3000/search?searchquery=cats

Мы получаем JSON! Я использую JSON Viewer Chrome Extension Чтобы быть в состоянии просмотреть JSON в моем браузере

Код для этого проекта можно найти на Гадость

Тем не менее, мы еще не сделаны. На данный момент наша API готова, но это немного медленно. Он также работает на нашей местной машине, поэтому нам нужно где-нибудь развернуть его. Это все будет покрыто частью 3!

Часть 3 будет покрывать:

  • Оптимизация и улучшение производительности

  • Исследование неисправностей Основы

  • Развертывание API.

Это конец этого поста! Я надеюсь, что вам понравилось читать это и нашло это полезным. Оставайтесь настроиться на часть 3!

Если вы заинтересованы в других случаях использования, проверьте Чистый калькулятор доходов , который использует узел/экспресс API для лома информации о состоянии государственных налогов и средней аренды в городах с веб-сайтов. Вы можете проверить это Github repo.

Если вам понравилось прочитать это и хотелось бы предоставить отзыв, вы можете сделать это анонимно здесь Отказ Любая отзыва относительно чего-либо ценится!

Оригинал: “https://dev.to/waqasabbasi/building-a-search-engine-api-with-node-express-and-puppeteer-using-google-search-4m21”