Веб-скраппинг фотографий Google Maps с помощью Nodejs


Что будет соскабливаться

Подготовка

Во-первых, нам нужно создать проект Node.js* и добавить npm пакеты puppeteer, puppeteer-extra и puppeteer-extra-plugin-stealth для управления Chromium (или Chrome, или Firefox, но сейчас мы работаем только с Chromium, который используется по умолчанию) через протокол DevTools в режиме headless или non-headless.

Для этого в директории с нашим проектом откройте командную строку и введите npm init -y, а затем npm i puppeteer puppeteer-extra puppeteer-extra-plugin-stealth.

*Если у вас не установлен Node.js, вы можете загрузить его с сайта nodejs.org и следовать документации по установке.

📌Примечание: также вы можете использовать puppeteer без каких-либо расширений, но я настоятельно рекомендую использовать его с puppeteer-extra с puppeteer-extra-plugin-stealth для предотвращения обнаружения веб-сайтом того, что вы используете безголовый Chromium или что вы используете веб-драйвер. Вы можете проверить это на сайте тестов безголового Chrome. На скриншоте ниже показана разница.

Процесс

Расширение SelectorGadget Chrome использовалось для захвата селекторов CSS при нажатии на нужный элемент в браузере. Если у вас возникли трудности с пониманием этого, у нас есть специальная статья в блоге SerpApi, посвященная веб-скрапингу с CSS-селекторами.

Приведенный ниже рисунок иллюстрирует подход к выбору различных частей результатов.

Полный код

📌Примечание: для получения URL места вы можете использовать руководство из моей статьи в блоге Web Scraping Google Maps Places with Nodejs.

const puppeteer = require("puppeteer-extra");
const StealthPlugin = require("puppeteer-extra-plugin-stealth");

puppeteer.use(StealthPlugin());

const placeUrl =
  "https://www.google.com/maps/place/Starbucks/data=!4m7!3m6!1s0x549069a98254bd17:0xb2f64f75b3edf4c3!8m2!3d47.5319688!4d-122.1942498!16s%2Fg%2F1tdfmzpb!19sChIJF71UgqlpkFQRw_Tts3VP9rI?authuser=0&hl=en&rclk=1";

async function scrollPage(page) {
  let iterationsLength = 0;
  while (true) {
    let photosLength = await page.evaluate(() => {
      return document.querySelectorAll(".U39Pmb").length;
    });
    for (; iterationsLength < photosLength; iterationsLength++) {
      await page.waitForTimeout(200)
      await page.evaluate((iterationsLength) => {
        document.querySelectorAll(".U39Pmb")[iterationsLength].scrollIntoView()
      }, iterationsLength);
    }
    await page.waitForTimeout(5000)
    let newPhotosLength = await page.evaluate(() => {
      return document.querySelectorAll(".U39Pmb").length;
    });
    if (newPhotosLength === photosLength) break
  }
}

async function getPhotosLinks(page) {
  const photos = await page.evaluate(() => {
    return Array.from(document.querySelectorAll(".U39Pmb")).map((el) => {
      return {
        thumbnail: getComputedStyle(el).backgroundImage.slice(5, -2),
      };
    });
  });
  const scripts = await page.evaluate(() => {
    return Array.from(document.querySelectorAll("script")).map(el => el.outerHTML).join()
  })
  return {photos, scripts};
}

async function getLocalPlacePhotos() {
  const browser = await puppeteer.launch({
    headless: false,
    args: ["--no-sandbox", "--disable-setuid-sandbox"],
  });

  const page = await browser.newPage();

  await page.setDefaultNavigationTimeout(60000);
  await page.goto(placeUrl);
  await page.waitForNavigation();

  await page.click(".Dx2nRe");
  await page.waitForTimeout(2000);
  await page.waitForSelector(".U39Pmb");

  await scrollPage(page);

  const {photos, scripts} = await getPhotosLinks(page);

  await browser.close();

  const validPhotos = photos.filter((el) => el.thumbnail.includes('https://lh5.googleusercontent.com/p'))

  const photoSizePattern = /"https://lh5.googleusercontent.com/p/(?<id>[^\]+).+?[(?<resolution>d{2,},d{2,})/gm; // https://regex101.com/r/zgxNOb/2
  const fullSizeData = [...scripts.matchAll(photoSizePattern)].map(({ groups }) => ({id: groups.id, resolution: groups.resolution}));

  validPhotos.forEach(el => {
    const idPattern = /https://lh5.googleusercontent.com/p/(?<id>[^=]+)/gm;  // https://regex101.com/r/XxS3QC/1
    const id = [...el.thumbnail.matchAll(idPattern)].map(({ groups }) => groups.id)[0];
    const resolution = fullSizeData.find((dataEl) => dataEl.id === id)?.resolution.split(',')
    if (resolution) el.image = `https://lh5.googleusercontent.com/p/${id}=w${resolution[1]}-h${resolution[0]}-k-no`
  })

  return validPhotos;
}

getLocalPlacePhotos().then(console.log);
Вход в полноэкранный режим Выход из полноэкранного режима

Объяснение кода

Объявите константы из необходимых библиотек:

const puppeteer = require("puppeteer-extra");
const StealthPlugin = require("puppeteer-extra-plugin-stealth");
Вход в полноэкранный режим Выйти из полноэкранного режима
Код Пояснение
puppeteer Библиотека управления Chromium
StealthPlugin библиотека для предотвращения обнаружения веб-сайтом того, что вы используете веб-драйвер

Далее мы «скажем» puppeteer использовать StealthPlugin и напишем URL места:

puppeteer.use(StealthPlugin());

const placeUrl =
  "https://www.google.com/maps/place/Starbucks/data=!4m7!3m6!1s0x549069a98254bd17:0xb2f64f75b3edf4c3!8m2!3d47.5319688!4d-122.1942498!16s%2Fg%2F1tdfmzpb!19sChIJF71UgqlpkFQRw_Tts3VP9rI?authuser=0&hl=en&rclk=1";
Войти в полноэкранный режим Выйти из полноэкранного режима

Далее запишем функцию для прокрутки фотоконтейнера на странице:

async function scrollPage(page) {
  let iterationsLength = 0;
  while (true) {
    let photosLength = await page.evaluate(() => {
      return document.querySelectorAll(".U39Pmb").length;
    });
    for (; iterationsLength < photosLength; iterationsLength++) {
      await page.waitForTimeout(200)
      await page.evaluate((iterationsLength) => {
        document.querySelectorAll(".U39Pmb")[iterationsLength].scrollIntoView()
      }, iterationsLength);
    }
    await page.waitForTimeout(5000)
    let newPhotosLength = await page.evaluate(() => {
      return document.querySelectorAll(".U39Pmb").length;
    });
    if (newPhotosLength === photosLength) break
  }
}
Вход в полноэкранный режим Выйти из полноэкранного режима
Код Объяснение
photosLength количество фотографий на странице перед прокруткой
page.evaluate( выполняет код из скобок в консоли браузера и возвращает результат
document.querySelectorAll(".U39Pmb") возвращает статический NodeList, представляющий список элементов документа, которые соответствуют селекторам css с именем класса U39Pmb
page.waitForTimeout(200) ожидание 200 мс перед продолжением
newPhotosLength количество фотографий на странице после прокрутки

Далее мы запишем функцию для получения ссылок на миниатюры со страницы:

async function getPhotosLinks(page) {
  const photos = await page.evaluate(() => {
    return Array.from(document.querySelectorAll(".U39Pmb")).map((el) => {
      return {
        thumbnail: getComputedStyle(el).backgroundImage.slice(5, -2),
      };
    });
  });
  const scripts = await page.evaluate(() => {
    return Array.from(document.querySelectorAll("script")).map(el => el.outerHTML).join()
  })
  return {photos, scripts};
}
Вход в полноэкранный режим Выход из полноэкранного режима
Код Пояснение
getComputedStyle(el).backgroundImage
.slice(5, -2) этот метод сохраняет все от 5-го символа от начала до 2-го (включительно) символа от конца и удаляет остальные

И, наконец, функция для управления браузером и получения информации:

async function getLocalPlacePhotos() {
  const browser = await puppeteer.launch({
    headless: false,
    args: ["--no-sandbox", "--disable-setuid-sandbox"],
  });

  const page = await browser.newPage();

  await page.setDefaultNavigationTimeout(60000);
  await page.goto(placeUrl);
  await page.waitForNavigation();

  await page.click(".Dx2nRe");
  await page.waitForTimeout(2000);
  await page.waitForSelector(".U39Pmb");

  await scrollPage(page);

  const {photos, scripts} = await getPhotosLinks(page);

  await browser.close();

  const validPhotos = photos.filter((el) => el.thumbnail.includes('https://lh5.googleusercontent.com/p'))

  const photoSizePattern = /"https://lh5.googleusercontent.com/p/(?<id>[^\]+).+?[(?<resolution>d{2,},d{2,})/gm; // https://regex101.com/r/zgxNOb/2
  const fullSizeData = [...scripts.matchAll(photoSizePattern)].map(({ groups }) => ({id: groups.id, resolution: groups.resolution}));

  validPhotos.forEach(el => {
    const idPattern = /https://lh5.googleusercontent.com/p/(?<id>[^=]+)/gm;  // https://regex101.com/r/XxS3QC/1
    const id = [...el.thumbnail.matchAll(idPattern)].map(({ groups }) => groups.id)[0];
    const resolution = fullSizeData.find((dataEl) => dataEl.id === id)?.resolution.split(',')
    if (resolution) el.image = `https://lh5.googleusercontent.com/p/${id}=w${resolution[1]}-h${resolution[0]}-k-no`
  })

  return validPhotos;
}

getLocalPlacePhotos().then(console.log);
Войти в полноэкранный режим Выйти из полноэкранного режима
Код Пояснение
puppeteer.launch({options}) этот метод запускает новый экземпляр браузера Chromium с текущими опциями
headless определяет, какой режим использовать: безголовый (по умолчанию) или безголовый
args массив с аргументами, который используется с Chromium
["--no-sandbox", "--disable-setuid-sandbox"] эти аргументы мы используем для разрешения запуска процесса браузера в онлайн IDE
browser.newPage() этот метод запускает новую страницу
page.setDefaultNavigationTimeout(60000) изменение времени ожидания селекторов по умолчанию (30 сек) на 60000 мс (1 мин) для медленного интернет соединения
page.goto(URL) навигация к URL, который определен выше
page.click(".Dx2nRe") этот метод эмулирует щелчок мыши на html-элементе с селектором .Dx2nRe
browser.close() после этого мы закрываем экземпляр браузера
photoSizePattern шаблон RegEx для поиска и определения идентификатора. Посмотрите, что он позволяет найти
[...scripts.matchAll(photoSizePattern)] в этом коде мы используем синтаксис spread для создания массива из итератора, который был возвращен из метода matchAll (в данном случае эта запись равна Array.from(scripts.matchAll(photoSizePattern)))
idPattern шаблон RegEx для поиска и определения id и полного разрешения изображения. Посмотрите, что это позволит вам найти

Теперь мы можем запустить наш парсер. Для этого введите node YOUR_FILE_NAME в командной строке. Где YOUR_FILE_NAME — это имя вашего файла .js.

Выход

📌Примечание: Я специально показываю полный вывод, потому что не все ссылки на полные изображения доступны на странице. Если я найду решение в будущем, я обновлю этот пост.

[
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipM4gn5qR89yKQiYbf2v8V2Mt-u27-8xlwgzbG3J=w203-h152-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipM4gn5qR89yKQiYbf2v8V2Mt-u27-8xlwgzbG3J=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOBX97ObGx9e0AhlwystTXlMKC7YaIfiEXzrj_N=w203-h114-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipOBX97ObGx9e0AhlwystTXlMKC7YaIfiEXzrj_N=w289-h512-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipM8KZ731HUrAb6Ow6b6tvaaG1SZibLWHlUG0B7I=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipM8KZ731HUrAb6Ow6b6tvaaG1SZibLWHlUG0B7I=w4032-h3024-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNGkMDnc3haeI6zEkJHTaYO3NL7kQU08HDDj-Bg=w203-h152-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNGkMDnc3haeI6zEkJHTaYO3NL7kQU08HDDj-Bg=w3120-h4160-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNWlgCSV9T03azM-aCjgoqHBkCTVvAUp5hV-FEW=w203-h220-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNWlgCSV9T03azM-aCjgoqHBkCTVvAUp5hV-FEW=w546-h502-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNl409pGQ2GeJ4UGLoCEFE2tYP7KyAFABGYtCqW=w203-h184-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMzdzL2c833XHkjyKCAZA_oIpG7sWzev14BIZqY=w203-h203-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOXC7UsM4ytw-Qdo9BqQPgdu7hpOFkrb8oeXXcD=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipPQRAYxiLusjrzSeqS8mc23V5u_fv26RobHwvpL=w205-h100-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipP27I9yad0JARrUosmPe2Cl8rrf5FfLI9u3ZsLe=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipP2A8D2I1d1gHgtqEBNMWiHm2jb7Dtd-p76FZS_=w203-h360-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipP2A8D2I1d1gHgtqEBNMWiHm2jb7Dtd-p76FZS_=w1920-h1080-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOCI7c9c2HSYM18cjd52ITmt2S-pkysyGoXAaEy=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNspVD1U4OKrDH5ZYVYwbazgE5amUtTRh54soV-=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNZeKVSAkS5IJtH_HTYenfFSrz6pgSwp4aM-1qv=w203-h164-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMoV7jsVrAVqTWWS3Qs7ouAJfPoi8MBIW0aOm_z=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNGOf2IWJ3Dmk_MLbhlcHAoMracP-o81WAre-51=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOn4frZ--0ZzrbAdDQoRPrtSZO5auLIVz76ju0H=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipN_LkT7MCwx-oaf1yXkMnc_D-gm6HrWa7Kqoep8=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipN_LkT7MCwx-oaf1yXkMnc_D-gm6HrWa7Kqoep8=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNTPaghKZbvv5aouQjzCq7no46UiiCa8IbsNmCZ=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNTPaghKZbvv5aouQjzCq7no46UiiCa8IbsNmCZ=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMXpF0BcYK9v4_AqjXWj1R_OHT3opK5y8h3lwxG=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMYK-cBn6_JUYTVbPkvAFTnb_cxWYI4B8aBDwGV=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipPcTFJIW9JAZxZ0PU0WC2U5rPnESv7OnrnSANwV=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipPcTFJIW9JAZxZ0PU0WC2U5rPnESv7OnrnSANwV=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOk5SXZXLYdZrliZhbEB-fuzpwX4AIiiIjC7Ehi=w203-h114-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipN8ncHBXGgaTyw8K3zlVlKz2lns8H5CiGszE8RL=w203-h360-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipN8ncHBXGgaTyw8K3zlVlKz2lns8H5CiGszE8RL=w1920-h1080-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMpGGf0g2dlvO0o9gaz_KJW3lIJpLOEHHOprabC=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNPGlc3kl2VVWxVoaVarj767h12q9Dn5dDMpLY6=w203-h360-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOFr1BaDsgzpJZ75-keeWMcucSsY9ooOc2eYbg3=w203-h262-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipPmujcLdxq_1ykKBzaBVMFDsvUNa7qlujezz6kP=w203-h360-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNKcskE9z5R_qZRvE9OmfC-XFtqDotXM_dynsyH=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOOBmi7BSryS_eU-DTAj5C5vR0CEqlSp-LvbxwB=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMENxPMlida3xNw7aOFPdw5UysR8KvDwPMbYZs4=w203-h203-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMek3ZenfuNiPxPO5N9xQ2sd-ZmrPJAEbwJiIsZ=w203-h114-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOS9SWb1Me927ABd1G6Ykf0emdLVxodfIucaEYz=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNXrAp9R2-kA0XuDooVR7_ep_jL-zLN8CziOyBU=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOL-FC0pQMTTS2uMjL39BgwZHKtlxC7g4QFztBI=w203-h360-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOnmfuOu_9FFp3Ee0-zLFNFmrM6wU2O9PZK4Zm0=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOrktO0O66qVKhpxWh02BHe2jxJZgtAZB34c_nJ=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMLKR4zHY3bEzI1EUnRB8j5ku1MeDI7xv7UNgAR=w203-h360-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipPgiZFDf_Xyje716A6MMAPQs_XF8yvVq_BtxQZc=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMZ_dmYNhLoOGp57DYCQa3Q_XWDae84e4Hdf1rj=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMJiZA1oAjzFuU4fBbp_ihe4UPSSpq5T1sXfufA=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNIUP-aOWRElmfVOjnf5lJJYFiLKBaSx7MSkhg8=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNIUP-aOWRElmfVOjnf5lJJYFiLKBaSx7MSkhg8=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNolq_OR7cT2d3ayKRLkl2mb9s-mv0mqAJPLHX1=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOuAg_x3ITU1I32KiWZGBzgwQAU4UXf4GB5Z9PS=w203-h270-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipMtjVGORN2eq-6kIjCrkW3biGo3cMVazFNBfz2r=w203-h152-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNsjaDdvAQrRboCbdVmcRA83c6DUBZ4YZvTDa8d=w203-h270-k-no"
   }
]
Вход в полноэкранный режим Выход из полноэкранного режима

API Google Maps Photos

В качестве альтернативы вы можете использовать API Google Maps Photos от SerpApi. SerpApi — это бесплатный API со 100 поисками в месяц. Если вам нужно больше поисковых запросов, существуют платные тарифные планы.

Разница в том, что вы можете получить все полные ссылки на изображения, и вам не придется писать код с нуля и поддерживать его. Вы также можете столкнуться с блокировкой от Google и изменением селекторов, что приведет к поломке парсера. Вместо этого вам просто нужно итерировать структурированный JSON и получать нужные данные. Загляните на игровую площадку.

Сначала нам нужно установить google-search-results-nodejs. Для этого в консоли нужно ввести: npm i google-search-results-nodejs.

📌Примечание: Для осуществления поиска нам нужен параметр data_id. Вы можете взять его, используя руководство из моей статьи в блоге Web Scraping Google Maps Places with Nodejs.

const SerpApi = require("google-search-results-nodejs");
const search = new SerpApi.GoogleSearch(process.env.API_KEY);     //your API key from serpapi.com

const dataId = "0x549069a98254bd17:0xb2f64f75b3edf4c3";           // data ID parameter

const params = {
  engine: "google_maps_photos",                                   // search engine
  hl: "en",                                                       // parameter defines the language to use for the Google search
  data_id: dataId,                                                // parameter defines the Google Maps data ID
};

const getJson = () => {
  return new Promise((resolve) => {
    search.json(params, resolve);
  });
};

const getResults = async () => {
  const allPhotos = [];
  while (true) {
    const json = await getJson();
    if (json.photos) {
      allPhotos.push(...json.photos);
    } else break;
    if (json.serpapi_pagination?.next_page_token) {
      params.next_page_token = json.serpapi_pagination?.next_page_token;
    } else break;
  }
  return allPhotos;
};

getResults.then(console.log);
Вход в полноэкранный режим Выход из полноэкранного режима

Объяснение кода

Объявите константы из необходимых библиотек:

const SerpApi = require("google-search-results-nodejs");
const search = new SerpApi.GoogleSearch(API_KEY);
Вход в полноэкранный режим Выйти из полноэкранного режима
Код Объяснение
SerpApi Библиотека SerpApi Node.js
search новый экземпляр класса GoogleSearch
API_KEY ваш API-ключ от SerpApi

Далее мы напишем, что мы хотим искать, и необходимые параметры для выполнения запроса:

const dataId = "0x549069a98254bd17:0xb2f64f75b3edf4c3";

const params = {
  engine: "google_maps_photos", // search engine
  hl: "en",
  data_id: dataId,
};
Войти в полноэкранный режим Выйти из полноэкранного режима
Код Объяснение
dataId параметр идентификатор данных
engine поисковая система
hl параметр определяет язык, который будет использоваться для поиска в Google Scholar

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

const getJson = () => {
  return new Promise((resolve) => {
    search.json(params, resolve);
  })
}
Войти в полноэкранный режим Выйти из полноэкранного режима

И, наконец, мы объявляем и запускаем функцию getResult, которая получает ссылки на фотографии со всех страниц и возвращает их:

const getResults = async () => {
  const allPhotos = [];
  while (true) {
    const json = await getJson();
    if (json.photos) {
      allPhotos.push(...json.photos);
    } else break;
    if (json.serpapi_pagination?.next_page_token) {
      params.next_page_token = json.serpapi_pagination?.next_page_token;
    } else break;
  }
  return allPhotos;
};

getResults().then(console.log)
Войти в полноэкранный режим Выход из полноэкранного режима
Код Пояснение
allPhotos массив с фотоссылками со всех страниц
allPhotos.push(...json.photos) в этом коде мы используем синтаксис spread, чтобы разделить массив photos из результата, возвращенного функцией getJson, на элементы и добавить их в конец массива allPhotos.

Выведите

[
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipM4gn5qR89yKQiYbf2v8V2Mt-u27-8xlwgzbG3J=w203-h152-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipM4gn5qR89yKQiYbf2v8V2Mt-u27-8xlwgzbG3J=w4032-h3024-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipOBX97ObGx9e0AhlwystTXlMKC7YaIfiEXzrj_N=w203-h114-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipOBX97ObGx9e0AhlwystTXlMKC7YaIfiEXzrj_N=w512-h289-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipM8KZ731HUrAb6Ow6b6tvaaG1SZibLWHlUG0B7I=w203-h270-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipM8KZ731HUrAb6Ow6b6tvaaG1SZibLWHlUG0B7I=w3024-h4032-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNGkMDnc3haeI6zEkJHTaYO3NL7kQU08HDDj-Bg=w203-h152-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNGkMDnc3haeI6zEkJHTaYO3NL7kQU08HDDj-Bg=w4160-h3120-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNWlgCSV9T03azM-aCjgoqHBkCTVvAUp5hV-FEW=w203-h220-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNWlgCSV9T03azM-aCjgoqHBkCTVvAUp5hV-FEW=w502-h546-k-no"
   },
   {
      "thumbnail":"https://lh5.googleusercontent.com/p/AF1QipNl409pGQ2GeJ4UGLoCEFE2tYP7KyAFABGYtCqW=w203-h184-k-no",
      "image":"https://lh5.googleusercontent.com/p/AF1QipNl409pGQ2GeJ4UGLoCEFE2tYP7KyAFABGYtCqW=w732-h664-k-no"
   },
   ...and other results
]
Вход в полноэкранный режим Выход из полноэкранного режима

Ссылки

  • Код в онлайн IDE
  • Google Maps Photos API

Если вы хотите увидеть некоторые проекты, сделанные с помощью SerpApi, пожалуйста, напишите мне сообщение.


Присоединяйтесь к нам на Twitter | YouTube

Add a Feature Request💫 or a Bug🐞

Оцените статью
devanswers.ru
Добавить комментарий