Я пытаюсь получить таблицу с этого сайта, используя Cheerio lib в скрипте приложения Google. Я ввожу код ниже этот ответ, но получаю только [] in console.info()
Вот мой код
function test2() {
const url = 'https://github.com/labnol/apps-script-starter/blob/master/scopes.md';
const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true }).getContentText();
const $ = Cheerio.load(res);
var data = $('tbody').find('td').toArray().map((x) => { return $(x).text() });
console.info(data);
}
Я также вижу некоторые ответы:



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Для начала вы можете использовать API GitHub, избегая ошибок, связанных с парсингом веб-страниц.
Если вы хотите придерживаться GAS и избегать API, проблема, похоже, в том, что обслуживаемая страница отличается от страницы в браузере. Я определил это, добавив DriveApp.createFile("test.html", res); для обхода усечения журнала (по-видимому, лучшего способа по мнению TheMaster, видимо, не существует). Из этого выходного HTML-кода видно, что данные доступны только в строке React JSON внутри тега сценария, которую можно извлечь с помощью Cheerio, проанализировать с помощью JSON.parse() и просмотреть.
Однако более простой вариант — запросить необработанную уценку и либо преобразовать ее в HTML с пометкой и продолжить работу с Cheerio, либо проанализировать таблицу вручную. Я воспользуюсь последним вариантом, так как я не слишком знаком с экосистемой пакетов GAS:
function myFunction() { // default GAS function name
const url = "https://raw.githubusercontent.com/labnol/apps-script-starter/master/scopes.md";
const res = UrlFetchApp.fetch(url).getContentText();
const data = [];
for (const line of res.split("\n")) {
const chunks = line
.replace(/[*`]/g, "")
.split("|")
.slice(1, 3)
.filter(e => e !== " -- ")
.map(e => e.trim());
if (chunks.length) {
data.push(chunks);
}
}
console.info(data);
}
Выход:
Logging output too large. Truncating output. [
[ 'Google OAuth API Scope', 'Scope Description' ],
[ 'Cloud SQL Admin API v1beta4', '' ],
[ 'View and manage your data across Google Cloud Platform services',
'https://www.googleapis.com/auth/cloud-platform' ],
[ 'Manage your Google SQL Service instances',
'https://www.googleapis.com/auth/sqlservice.admin' ],
[ '', '' ],
[ 'Android Management API v1', '' ],
// ...
Анализ необработанной уценки немного сложнее, но должен быть достаточно надежным. Если это не так, попробуйте один из других вариантов.
Если вы не хотите использовать GAS, ваш исходный код работает для меня в Node 20.11.1:
const cheerio = require("cheerio"); // ^1.0.0-rc.12 or rc.10
const url = "https://github.com/labnol/apps-script-starter/blob/master/scopes.md";
fetch(url)
.then(res => {
if (!res.ok) {
throw Error(res.statusText);
}
return res.text();
})
.then(html => {
const $ = cheerio.load(html);
const data = $("tbody")
.find("td")
.toArray()
.map(x => $(x).text());
console.info(data);
})
.catch(err => console.error(err));
Выход:
[
'Cloud SQL Admin API v1beta4',
'',
'View and manage your data across Google Cloud Platform services',
'https://www.googleapis.com/auth/cloud-platform',
'Manage your Google SQL Service instances',
'https://www.googleapis.com/auth/sqlservice.admin',
'',
'',
'Android Management API v1',
// ... 1360 total items ...
]
Хотя это работает, показанный выше массив слишком плоский, чтобы его можно было использовать — по сути, это одна гигантская строка. Я бы использовал очистку на основе вложенных строк и ячеек, чтобы сохранить табличный характер данных и избежать их выравнивания.
// ...
const $ = cheerio.load(html);
const data = [...$("tr")].map(e =>
[...$(e).find("td, th")].map(e => $(e).text().slice(0, 25))
);
console.table(data.slice(0, 10));
// ...
Вот результат, аналогичный выводу сценария GAS (удалите вызовы срезов, чтобы увидеть все данные без усечения):
┌─────────┬─────────────────────────────┬─────────────────────────────┐
│ (index) │ 0 │ 1 │
├─────────┼─────────────────────────────┼─────────────────────────────┤
│ 0 │ 'Google OAuth API Scope' │ 'Scope Description' │
│ 1 │ 'Cloud SQL Admin API v1bet' │ '' │
│ 2 │ 'View and manage your data' │ 'https://www.googleapis.co' │
│ 3 │ 'Manage your Google SQL Se' │ 'https://www.googleapis.co' │
│ 4 │ '' │ '' │
│ 5 │ 'Android Management API v1' │ '' │
│ 6 │ 'Manage Android devices an' │ 'https://www.googleapis.co' │
│ 7 │ '' │ '' │
│ 8 │ 'YouTube Data API v3' │ '' │
│ 9 │ 'Manage your YouTube accou' │ 'https://www.googleapis.co' │
└─────────┴─────────────────────────────┴─────────────────────────────┘
Вы можете обработать это дальше, чтобы сгруппировать по подкатегориям. Строки с двумя пустыми ячейками являются разделителем между категориями области (я думаю, я не эксперт в предметной области), а строки с пустой правой ячейкой являются заголовками категорий. Вот пример группировки по подкатегориям и прикрепления заголовков к каждой ячейке:
const grouped = [];
const headers = data[0];
for (const row of data.slice(1)) {
if (row.every(e => e === "")) {
continue;
} else if (row[1] === "") {
grouped.push({title: row[0], items: []});
} else {
grouped
.at(-1)
.items.push(
Object.fromEntries(
row.map((e, i) => [headers[i], e])
)
);
}
}
console.info(JSON.stringify(grouped, null, 2));
Я протестировал этот пример кода обработки как в GAS, так и в Node.
Спасибо за это. Я попробовал 1.0.0-rc.10, и результат у меня тот же, так что проблема, вероятно, не в Cheerio. OP может распечатать свой HTML-код, полученный при выборке, чтобы увидеть, соответствует ли он странице, или он каким-либо образом заблокирован или изменен. Использование API должно обходить любые блокировки, если это происходит.
@TheMaster Думаю, у меня это работает в GAS.
Круто 👍👍👍👍(PS: нет лучшего способа, чем DriveApp.createFile)
Спасибо всем за помощь, я тоже нашел решение своего вопроса, выложу позже.
Мой способ решения этой проблемы немного груб, но вот чего мне удалось достичь. Я пока еще начинающий разработчик-самоучка)
Вот мой код:
function myFunction() {
var [c, arr] = [[], []];
UrlFetchApp.fetch("https://raw.githubusercontent.com/labnol/apps-script-starter/master/scopes.md")
.getContentText()
.split("\n")
.forEach((item, i) => {
c.push(item.replace(/[*`|]/g, " ").trim().split("|||"))
});
var v = c.splice(19);
v.forEach((item, i) => {
if (item.length == 2 && item[0] == '' && item[1] == '') {
v.splice(i, 1);
}
})
v.forEach((item, i) => {
if (item[0].indexOf("API") == -1) {
arr[arr.length - 1].push(item[0]);
} else { arr.push(item); }
})
var g = tesssst(arr);
console.info(g);
}
function tesssst(inputArray) {
var outputArray = [];
inputArray.forEach((row, i) => {
var newRow = [];
newRow.push(row[0]);
newRow.push(row.slice(1).join(','));
outputArray.push(newRow);
});
return outputArray;
}
Вывод будет такой:
[ [ 'Cloud SQL Admin API v1beta4',
'View and manage your data across Google Cloud Platform services https://www.googleapis.com/auth/cloud-platform,Manage your Google SQL Service instances https://www.googleapis.com/auth/sqlservice.admin,' ],
[ 'Android Management API v1',
'Manage Android devices and apps for your customers https://www.googleapis.com/auth/androidmanagement,' ],
[ 'YouTube Data API v3',
'Manage your YouTube account https://www.googleapis.com/auth/youtube,See, edit, and permanently delete your YouTube videos, ratings, comments and captions https://www.googleapis.com/auth/youtube.force-ssl,View your YouTube account https://www.googleapis.com/auth/youtube.readonly,Manage your YouTube videos https://www.googleapis.com/auth/youtube.upload,View and manage your assets and associated content on YouTube https://www.googleapis.com/auth/youtubepartner,View private information of your YouTube channel relevant during the audit process with a YouTube partner https://www.googleapis.com/auth/youtubepartner-channel-audit,' ],
[ 'Cloud Testing API v1',
'View and manage your data across Google Cloud Platform services https://www.googleapis.com/auth/cloud-platform,View your data across Google Cloud Platform services https://www.googleapis.com/auth/cloud-platform.read-only,' ],
[ 'DoubleClick Search API v2',
'View and manage your advertising data in DoubleClick Search https://www.googleapis.com/auth/doubleclicksearch,' ],
[ 'Tasks API v1',
'Create, edit, organize, and delete all your tasks https://www.googleapis.com/auth/tasks,View your tasks https://www.googleapis.com/auth/tasks.readonly,' ],
[ 'Calendar API v3',
'See, edit, share, and permanently delete all the calendars you can access using Google Calendar https://www.googleapis.com/auth/calendar,View and edit events on all your calendars https://www.googleapis.com/auth/calendar.events,View events on all your calendars https://www.googleapis.com/auth/calendar.events.readonly,View your calendars https://www.googleapis.com/auth/calendar.readonly,View your Calendar settings https://www.googleapis.com/auth/calendar.settings.readonly,' ],
[ 'Google Play Custom App Publishing API v1',
'View and manage your Google Play Developer account https://www.googleapis.com/auth/androidpublisher,' ],
[ 'YouTube Analytics API v2',
'Manage your YouTube account https://www.googleapis.com/auth/youtube,View your YouTube account https://www.googleapis.com/auth/youtube.readonly,View and manage your assets and associated content on YouTube https://www.googleapis.com/auth/youtubepartner,View monetary and non-monetary YouTube Analytics reports for your YouTube content https://www.googleapis.com/auth/yt-analytics-monetary.readonly,View YouTube Analytics reports for your YouTube content https://www.googleapis.com/auth/yt-analytics.readonly,' ],
[ 'Cloud Healthcare API v1alpha2',
'View and manage your data across Google Cloud Platform services https://www.googleapis.com/auth/cloud-platform,' ],
[ 'Cloud Shell API v1',
'View and manage your data across Google Cloud Platform services https://www.googleapis.com/auth/cloud-platform,' ],
[ 'Content API for Shopping v2.1',
'Manage your product listings and accounts for Google Shopping https://www.googleapis.com/auth/content,' ]]
Кажется, единственная разница в
1.0.0-rc.10ГАЗе и1.0.0-rc.12. Запрос, похоже, не заблокирован. Однако, если ua действительно является проблемой, его невозможно изменить в GAS