Что будет соскабливаться
Подготовка
Во-первых, нам нужно создать проект 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🐞