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

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

Я получил самые тревожные ответы: «Вы можете использовать инструмент API парсера для этого, это то, что я использовал в своем проекте».

Такие услуги, как

Еще несколько…..

Пока однажды я не встретил ангела-хранителя и познакомился с протоколом открытого графа.

Спасибо, Эмма 🤗.

К вашему сведению. Правильное слово для обозначения того, что мы делаем, называется парсинг веб-страниц.

Что такое протокол открытого графа?

Протокол Open Graph позволяет любой веб-странице стать многофункциональным объектом в социальном графе. Например, это используется в Facebook, чтобы любая веб-страница имела те же функции, что и любой другой объект на Facebook.

~ Кто-то из https://ogp.me/

Короче говоря, он описывает веб-сайт с такими объектами, как заголовок, описание, изображения и т. Д. С тегами <meta>.

Я здесь не для того, чтобы говорить о протоколе открытого графа, я здесь, чтобы показать вам, как получить эти данные для предварительного просмотра ссылок, поэтому, если вы хотите узнать больше о OGP, вот пара ссылок.

К вашему сведению - у Twitter есть собственный метатег, но они используют префикс «twitter» вместо «og».









Как мы это делаем ?

Это простой процесс, не требующий особого труда. Мы загрузим веб-страницу в виде текста в нашем приложении Node.js. Затем мы выберем нужные нам HTML-элементы и получим данные / текст, которые они содержат, сохраним их в файл JSON, а затем отправим данные обратно.

"Но как мы можем выбрать дом на заднем конце Адель?"

Простая, с помощью cheerio и других подобных модулей, cheerio - это Быстрая, гибкая и экономичная реализация основного jQuery, разработанная специально для сервера.

Можем ли мы сделать это на передней панели?

Насколько я знаю, вы не можете, это невозможно сделать во внешнем скрипте, когда вы попытаетесь получить, например, мое портфолио или любой другой сайт в консоли Chrome, он выдаст ошибку cors (Cross-Origin Resource Sharing).

Чтобы обойти эту проблему, мы отправим URL-адрес на внутренний сервер, обработаем запрос, а затем отправим обратно данные, которые мы удалили.

Предпосылка

  • JQuery, если вы знаете, как выбрать элемент и получить его значения, все хорошо.
  • Асинхронный / Ожидание
  • NodeJS / ExpressJS

ОК ДАВАЕМ КОД !!!

Если вы хотите присоединиться к нам, у меня есть стартовые файлы, которые вы можете клонировать / скачать, и я тоже буду добавлять готовые файлы.



1 - Познакомьтесь со скриптом внешнего интерфейса

В нашем внешнем скрипте, расположенном в папке public / javascript, есть довольно небольшой объем кода, у нас есть прослушиватель событий щелчка на нашей кнопке добавления, который будет

  • вставьте карточку предварительного просмотра загрузки с идентификатором.
  • отправьте почтовый запрос на бэкэнд с URL-ссылкой и идентификатором карты, которая была добавлена ​​на страницу.
  • подождите, пока данные вернутся, затем добавьте данные в правильную карточку предварительного просмотра, но ищите идентификатор карточки.
const addButton = document.querySelector('button');
addButton.addEventListener('click', async (e) => {
e.preventDefault();
try {
    let urlInput = document.querySelector('input');
    const id = await uuid();
    const fetchData = {
             method: 'POST',
             headers: {
                 'Content-Type': 'application/json;charset=utf-8'
             },
            body: JSON.stringify({ previewUrl: urlInput.value, id })
    };
urlInput.value = '';
prependLoadingPreview(id);
const data = await fetch('/get-preview', fetchData)
     .then(res => res.json());
     
     addDataToPreview(data);
} catch (err) {
console.log(err);
}
});

Эта функция принимает идентификатор и добавит предварительный просмотр загрузки в неупорядоченный список,

function prependLoadingPreview(id) {
document.querySelector(`ul`).prepend(document.createRange()
  .createContextualFragment(`<li class="preview-container loading" data-id="${id}"></li>`,'text/html'));
}

Эта функция получит объект, она получит список загрузки по его идентификатору, который был добавлен ранее, удалит имя класса загрузки, затем добавит данные

Object example
{
  id: "a60d491d-8c70-4620-aa18-111ae1abaea9", 
  url: "https://www.adelak.me", 
  img:"https://www.npmjs.com/package/mongoose", 
  title: "Mongoose", 
  description: "Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.", 
  domain: "npmjs.com"
}
function addDataToPreview({ id, url, img, title, description, domain }) {
const li = document.querySelector(`li[data-id="${id}"]`);
li.classList.remove('loading');
li.innerHTML =
`<svg class="delete-button" viewBox="0 0 24 24" onClick="removePreview(this)"><polygon points="17.8,16.7 16.6,17.9 12,13.3 7.4,17.9 6.2,16.7 10.8,12.1 6.2,7.5 7.4,6.3 12,11 16.6,6.4 17.8,7.6 13.2,12.2"></polygon></svg>
<a href="${url}" target="_blank"><img class="preview-image" src="${img}" alt="preview image" onError="imgError(this)"/><div class="preview-info"><h5 class="preview-title">${title}</h5><p class="preview-description">${description}</p><span class="preview-url">${domain}</span></div></a>`;
}

Эта функция удаляет карту предварительного просмотра

const removePreview = async (e) => {
  try{
let li = e.parentElement;
li.parentElement.removeChild(li);
const id = li.getAttribute('data-id');
await fetch(`/remove/${id}`, { method: 'POST' });
  
  }catch(err){
console.log(err);
}
}

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

function uuid(){
let date = new Date().getTime();
const randomStrings = (c) => {
const r = (date + Math.random()*16)%16 | 0;
date = Math.floor(date/16);
return (c=='x' ? r :(r&0x3|0x8)).toString(16);
}
const id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, randomStrings);
return id;
}

2 - Установка модулей

Нам нужно установить несколько модулей.

Cheerio, чтобы загрузить исходный код веб-страницы, которую мы хотим просканировать.

ExpressJS для создания нашего HTTP-сервера.

Express-handlebars - шаблонизатор, который упрощает написание HTML-кода и отображает страницу.

Node-fetch, чтобы сделать наш HTTP-запрос в node.js.

Я добавил эти модули в зависимости, просто установите их, запустив npm i в командной строке.

3 - Создание нашего сервера

В app.js мы запросили все наши модули, настроили механизм просмотра и промежуточное ПО.

Мы можем начать с создания домашнего маршрута, который будет отображать домашний храм и передать ему данные в data.json (в настоящее время data,json пусто).

app.get('/', function(req, res) {
   res.render('home.handlebars', { data });
});

Откройте командную строку и запустите npm start, затем в браузере откройте localhost: // 3000.

Вы должны получить пустую домашнюю страницу с полем ввода.

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

В теле запроса мы ожидаем значение от previewUrl и id.

app.post('/get-preview',(req, res) => {
  const { previewUrl, id } = req.body;
  console.log(previewUrl); //https://medium.com/@adoolak/how-to-deploy-a-reactjs-and-express-app-to-heroku-afb5b117e0eb
  console.log(id);//"a60d491d-8c70-4620-aa18-111ae1abaea9"
});

Давайте поработаем над получением HTML-страницы из моего последнего сообщения среднего размера.

Превратите анонимную функцию в функцию async / await и используйте fetch API из модуля node-fetch, затем создайте переменную с именем html и присвойте ей значение метода fetch (убедитесь, что вы используете ключевое слово await, чтобы дождаться результат выборки), передайте ему значение previewUrl из тела запроса, а затем привяжите к нему .then(res => res.text()).

Затем мы используем cheerio, помните, что cheerio - это реализация основного jquery для серверной части.

Создайте переменную со знаком $ и присвойте ей значение cheerio.load(), pass переменной html в метод загрузки, теперь вы можете попробовать выбрать элемент html с помощью знака $.

app.post('/get-preview',async (req, res) => {
  const { previewUrl, id } = req.body;
  const html = await fetch(previewUrl).then(res => res.text());
  const $ = cheerio.load(html);
  console.log($('h1').text()) //How to deploy a Reactjs and Express App to Heroku
});

Теперь мы можем начать получать нужные метатеги, создать переменную с именем metaTagData, которая будет содержать объект данных,

id - мы передадим объекту id от req.body,

url - URL-адрес веб-сайта для ключа URL-адреса.

домен - для домена нам просто нужно доменное имя previewUrl, мы можем использовать модуль url из nodejs, чтобы получить имя хоста.

title - используйте cheerio, чтобы выбрать метатег с атрибутом name="title"

img - используйте cheerio, чтобы выбрать метатег с атрибутом name="title"

описание - используйте cheerio, чтобы выбрать метатег с атрибутом name="description" и получить атрибут content.

У мета-тегов есть еще один атрибут, называемый content, в котором хранятся значения. Чтобы получить значения, вам нужно связать селекторы cheerio с помощью метода attr и передать ему строку content.

У вас должен получиться такой объект.

const metaTagData = {
  id:id,
  url: previewUrl,
  domain: url.parse(previewUrl).hostname,
  title: $('meta[name="title"]').attr('content'),
  img: $('meta[name="image"]').attr('content'),
  description: $('meta[name="description"]').attr('content'),
}

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

Решение

Создание функции, которая вернет первое, что найдет

const getMetaRag = (name) =>  {
  return(
    $(`meta[name=${name}]`).attr('content') ||
    $(`meta[name="og:${name}"]`).attr('content') ||
    $(`meta[name="twitter:${name}"]`).attr('content') ||
    $(`meta[property=${name}]`).attr('content') ||
    $(`meta[property="og:${name}"]`).attr('content') ||
    $(`meta[property="twitter:${name}"]`).attr('content')
  );
}

Теперь мы можем изменить значение title, img и описания нашего объекта metaTagData на функцию getMetaTag и передать ей имя метатега в виде строки.

А что, если веб-страница вообще не использует метатеги?

Мы добавляем резервное значение в наши ключи title, img и description.

const metaTagData = {
  id:id,
  url: previewUrl,
  domain: url.parse(previewUrl).hostname,
  title: getMetaTag('title') || $(`h1`).text(),
  img: getMetaTag('image') || './images/no-image.png',
  description: getMetaTag('description') || $(`p`).text(),
}

title - вернется к первому тегу h1 на странице.

img - вернется к изображению в папке public / images.

описание - вернется к тегу первого абзаца на странице.

Некоторые описания могут быть немного длинными, я решил, что все описания должны содержать не более 200 символов.

let { description } = metaTagData;
if(description.length > 200){
 metaTagData.description = description.substring(0,200) + '...';
}

Затем мы помещаем данные в начало массива данных, используя метод массива unshift, затем записываем их в файл data.json, используя writeFile из модуля nodejs fs (файловая система).

data.unshift(metaTagData);
fs.writeFile("./data.json", JSON.stringify(data, null, 2),()=>{
  res.status(201).json(data.shift());
});

Первый параметр метода writeFile принимает расположение файла, второй параметр, который мы передаем в данные, которые мы хотим записать в файл, поскольку это файл JSON, который нам нужен для строкового преобразования данных с помощью методаJSON.stringify, третий параметр принимает функция обратного вызова, где мы отвечаем JSON и передаем ему данные с помощью метода массива сдвига, а также устанавливаем статус HTTP на 201.

Тестовый запуск!

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

Удаление карты

Чтобы удалить карту, создайте другой почтовый маршрут, который будет принимать идентификатор из параметра URL, создайте переменную с именем indexOfId и значение, которое вы сопоставляете с массивом данных json, и верните только идентификатор каждого объекта, а затем объедините метод массива indexOf() на карту, даст вам точное положение идентификатора, который вы хотите удалить из массива (убедитесь, что вы передали идентификатор из параметра url в метод indexOf).

Затем мы используем метод массива splice для удаления данных из массива данных json и передачи первого параметра переменной indexOfId и второго параметра значения 1, что указывает на то, что мы хотим удалить только объект из массива.

Затем мы используем модуль fs nodejs, чтобы перезаписать новые отредактированные данные в файл data.json, и ответим статусом 200 и используем ответ end() для завершения запроса.

app.post('/remove/:id', (req, res) => {
  const { id } = req.params;
  const indexOfId = data.map(dataId => dataId.id)
.indexOf(id);
  data.splice(indexOfId,1);
  fs.writeFile("./data.json", JSON.stringify(data, null, 2), () => (
  res.status(200).end()
  ));
});

Тестовый запуск!

Если вы попытаетесь удалить карту, а затем обновите страницу, удаленные карты больше не будут там.

Конец пути!!

Вывод

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

Возьмем, к примеру, Adidas, они не доказывают наличие API для своего продукта, изображений, цен и т. Д., И вы хотите создать побочный проект электронной коммерции.

Вы можете перейти на их веб-страницу и начать очистку продуктов, но если веб-страница, такая как adidas.com, использует react, angular или vue, это может стать сложным для веб-мусора.

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

!!! Однако будьте осторожны, удаление некоторых веб-сайтов является незаконным !!!

Ресурсы безголового браузера







Есть вопросы?
Напишите мне в твиттере @Adel_xoxo, и я отвечу, насколько мне известно.

~ Адель ак