предисловие
Знакомые со мной друзья могут знать, что я никогда не писал на злободневные темы. Почему бы не написать это? Это потому, что я не слежу за горячими точками? Не совсем. Я до сих пор очень обеспокоен некоторыми событиями, и у меня действительно много мыслей и мнений. Но я всегда придерживался одного принципа, а именно:сделать контент живым.
Содержание, представленное в этой статье, основано на предыдущих исследованиях и разработках автора.Платформа управления краулером, специально выделил относительно независимый функциональный модуль, чтобы объяснить, как использоватьnodejsРазработайте собственную платформу для поисковых роботов. В этой статье рассматриваются дополнительные вопросы, в том числеnodejs, гусеничный фреймворк, Родительские и дочерние процессы и их взаимодействие, reactа такжеumiЯ познакомлю вас с ним максимально простым языком.
ты получишь
- ApifyВведение в фреймворк и базовое использование
- Как создатьродительско-дочерний процесстак же какКоммуникация процесса родитель-потомок
- использоватьjavascriptВручную контролируйте максимальное количество одновременных поисковых роботов
- Схема реализации перехвата всей картинки веб-страницы
- nodejsИспользование сторонних библиотек и модулей
- использоватьumi3 + antd4.0Создайте внешний интерфейс сканера
Предварительный просмотр платформы
На приведенном выше рисунке показана платформа сканера, которую мы хотим внедрить. Мы можем ввести указанный URL-адрес для сканирования данных под веб-сайтом и создать снимок всей веб-страницы. После сканирования мы можем загрузить данные и изображения. С правой стороны веб-страница — это пользователь. Захваченные записи удобны для вторичного использования или резервного копирования.текст
Прежде чем приступить к статье, нам необходимо понять некоторые области применения сканеров.Сканеры, которые мы обычно знаем, в основном используются для сканирования данных веб-страниц, сбора информации о запросах, снимков экрана веб-страниц и т. д., как показано ниже:
Конечно, приложение сканера — это гораздо больше, мы также можем использовать библиотеку сканера, чтобы сделатьавтоматизированный тест, рендеринг на стороне сервера, Автоматизировать отправку формы, проверить расширение гугл, диагностика производительностиЖдать. Принципы фреймворка сканера, реализованного на любом языке, часто схожи.nodejsРеализована структура сканераApifyКак и использование, так и через практический случай, каждому удобно быстро приступить к разработке краулеров.Введение и базовое использование фреймворка Apify
apifyэто дляJavaScriptмасштабируемыйwebБиблиотека рептилий. через безголового(headless)Chromeа такжеPuppeteerПозволяет разрабатывать задания по извлечению данных и веб-автоматизации. Он обеспечивает управление и автоматическое масштабирование без головыChrome / PuppeteerИнструмент для пулов экземпляров, который поддерживает ведение очереди запросов для целевых URL-адресов и может сохранять результаты сканирования в локальной файловой системе или в облаке.
Для нас очень просто установить и использовать его.На официальном сайте также есть много примеров для справки.Конкретные шаги по установке и использованию следующие:
Установить
npm install apify --save
Начните свое первое дело с Apify
const Apify = require('apify');
Apify.main(async () => {
const requestQueue = await Apify.openRequestQueue();
await requestQueue.addRequest({ url: 'https://www.iana.org/' });
const pseudoUrls = [new Apify.PseudoUrl('https://www.iana.org/[.*]')];
const crawler = new Apify.PuppeteerCrawler({
requestQueue,
handlePageFunction: async ({ request, page }) => {
const title = await page.title();
console.log(`Title of ${request.url}: ${title}`);
await Apify.utils.enqueueLinks({
page,
selector: 'a',
pseudoUrls,
requestQueue,
});
},
maxRequestsPerCrawl: 100,
maxConcurrency: 10,
});
await crawler.run();
});
Может возникнуть после использования интерфейса узла для выполнения следующего:
Программа автоматически откроет браузер и откроет страницу URL, которая соответствует условиям. Мы также можем использовать предоставляемый ею инструмент cli для более удобного управления службой поиска и другими функциями. Заинтересованные друзья могут попробовать это.apifyПредоставляет множество полезных API для разработчиков. Если вы хотите достичь более сложных возможностей, вы можете изучить его. На следующем рисунке показан скриншот API официального сайта:Краулер, который автор хочет реализовать, в основном используетApifyИнтегрированныйPuppeteerспособность, если правильноPuppeteerЕсли вы не знакомы, вы можете перейти на официальный сайт, чтобы узнать об этом, Этот модуль будет перечислять адреса документов технической основы, используемой проектом, один за другим.Как создать родительско-дочерний процесс и связь между родительско-дочерними процессами
Если мы хотим внедрить платформу сканера, ключевой вопрос, который следует учитывать, — это время выполнения задач сканера и способ их выполнения, поскольку сканирование веб-страниц и снимков экрана требует ожидания загрузки всех веб-страниц перед обработкой, чтобы обеспечить целостность данных, поэтому мы можем рассматривать его кактрудоемкая задача.
когда мы используемnodejsВ качестве фонового сервера, из-заnodejsОн сам по себе однопоточный, поэтому, когда запрос на сканирование передается вnodejsВремя,nodejsпришлось ждать этого"трудоемкая задача«Другие запросы могут быть обработаны только после завершения, что приведет к тому, что другие запросы на странице будут ждать завершения задачи, прежде чем продолжить, поэтому для лучшего взаимодействия с пользователем и плавного ответа мы не рассматриваем многопроцессную обработку. К счастью,nodejsРазработанный для поддержки подпроцессов, мы можемПомещайте трудоемкие задачи, такие как поисковые роботы, в дочерний процесс для обработки и уведомляйте основной процесс о завершении дочернего процесса.Весь процесс показан на следующем рисунке:
Nodejs имеет 3 способа создания дочерних процессов, здесь мы используем fork для обработки, конкретная реализация выглядит следующим образом:
// child.js
function computedTotal(arr, cb) {
// 耗时计算任务
}
// 与主进程通信
// 监听主进程信号
process.on('message', (msg) => {
computedTotal(bigDataArr, (flag) => {
// 向主进程发送完成信号
process.send(flag);
})
});
// main.js
const { fork } = require('child_process');
app.use(async (ctx, next) => {
if(ctx.url === '/fetch') {
const data = ctx.request.body;
// 通知子进程开始执行任务,并传入数据
const res = await createPromisefork('./child.js', data)
}
// 创建异步线程
function createPromisefork(childUrl, data) {
// 加载子进程
const res = fork(childUrl)
// 通知子进程开始work
data && res.send(data)
return new Promise(reslove => {
res.on('message', f => {
reslove(f)
})
})
}
await next()
})
Выше приведен простой пример реализации связи между родительскими и дочерними процессами, и наша служба сканирования также будет использовать этот шаблон для реализации.
использоватьjavascriptВручную контролируйте максимальное количество одновременных поисковых роботов
Выше описаны технические вопросы, которые необходимо учитывать для реализации нашего приложения для искателя.Затем мы приступаем к формальной реализации бизнес-функции.Поскольку задача искателя выполняется в дочернем процессе, мы реализуем нашу функцию искателя в код дочернего процесса Давайте сначала разберемся с конкретными бизнес-требованиями, как показано ниже:
j' Далее я решу это первымКонтролируйте максимальное количество одновременных поисковых роботовПричина решения этой проблемы заключается в рассмотрении производительности сканера.Мы не можем позволить сканеру сканировать все веб-страницы за один раз, что откроет много параллельных процессов для его обработки, поэтому нам нужно разработать дросселирующее устройство для управления каждый Число одновременных раз, и следующий пакет обработки сканирования страницы будет выполняться после завершения текущего. Конкретная реализация кода выглядит следующим образом:
// 异步队列
const queue = []
// 最大并发数
const max_parallel = 6
// 开始指针
let start = 0
for(let i = 0; i < urls.length; i++) {
// 添加异步队列
queue.push(fetchPage(browser, i, urls[i]))
if(i &&
(i+1) % max_parallel === 0
|| i === (urls.length - 1)) {
// 每隔6条执行一次, 实现异步分流执行, 控制并发数
await Promise.all(queue.slice(start, i+1))
start = i
}
}
Приведенный выше код может выполнять сканирование 6 веб-страниц одновременно каждый раз, и следующий пакет задач будет выполняться после завершения первой задачи.urlsОтносится к набору URL-адресов, введенных пользователем,fetchPageЧтобы просканировать логику сканера страницы, автор инкапсулирует ее какpromise.
Как сделать снимок всей веб-страницы
мы все знаемpuppeteerПерехват изображений веб-страниц будет перехватывать только загруженную часть, что не является проблемой для обычных статических веб-сайтов, но для веб-сайтов на основе контента или веб-сайтов электронной коммерции с большим количеством содержимого страницы в основном используется режим загрузки по запросу, поэтому общий метод Перехватывается только часть страницы, или перехватываемый заполнитель — это еще не загруженное изображение, как показано на следующем рисунке:
Поэтому для перехвата всей веб-страницы требуется вмешательство человека. Здесь автор предлагает простую идею реализации, которая может решить эту проблему. Основная идея заключается в следующем.использоватьpuppeteerAPI вручную прокручивает браузер вниз, прокручивая каждый раз один экран, пока высота прокрутки страницы не останется неизменной, она считается прокрученной вниз, Конкретная реализация выглядит следующим образом:// 滚动高度
let scrollStep = 1080;
// 最大滚动高度, 防止无限加载的页面导致长效耗时任务
let max_height = 30000;
let m = {prevScroll: -1, curScroll: 0}
while (m.prevScroll !== m.curScroll && m.curScroll < max_height) {
// 如果上一次滚动和本次滚动高度一样, 或者滚动高度大于设置的最高高度, 则停止截取
m = await page.evaluate((scrollStep) => {
if (document.scrollingElement) {
let prevScroll = document.scrollingElement.scrollTop;
document.scrollingElement.scrollTop = prevScroll + scrollStep;
let curScroll = document.scrollingElement.scrollTop
return {prevScroll, curScroll}
}
}, scrollStep);
// 等待3秒后继续滚动页面, 为了让页面加载充分
await sleep(3000);
}
// 其他业务代码...
// 截取网页快照,并设置图片质量和保存路径
const screenshot = await page.screenshot({path: `static/${uid}.jpg`, fullPage: true, quality: 70});
Поскольку другие части кода краулера не являются основной задачей, я не буду приводить здесь примеры, я выложил их на гитхаб, и каждый может обменяться исследованиями.
Что касается того, как извлечь текст веб-страницы, есть также готовые API, которые можно вызвать.Вы можете выбрать API, который подходит для вашего бизнеса, чтобы применить.Автор берет его здесь.puppeteerизpage.$evalНапример:
const txt = await page.$eval('body', el => {
// el即为dom节点, 可以对body的子节点进行提取,分析
return {...}
})
nodejsИспользование сторонних библиотек и модулей
Чтобы создать полную платформу обслуживания узлов, автор принимает
- koa — это легкий и расширяемый фреймворк узлов.
- glob просматривает файлы, используя мощные регулярные шаблоны сопоставления
- koa2-cors решает проблемы с междоменным доступом
- koa-static создает статический сервисный каталог
- koa-body Получить данные тела запроса Что касается того, как использовать эти модули для реализации полного серверного приложения, автор сделал подробное описание в коде, и я не буду обсуждать их здесь по одному, конкретный код выглядит следующим образом:
const Koa = require('koa');
const { resolve } = require('path');
const staticServer = require('koa-static');
const koaBody = require('koa-body');
const cors = require('koa2-cors');
const logger = require('koa-logger');
const glob = require('glob');
const { fork } = require('child_process');
const app = new Koa();
// 创建静态目录
app.use(staticServer(resolve(__dirname, './static')));
app.use(staticServer(resolve(__dirname, './db')));
app.use(koaBody());
app.use(logger());
const config = {
imgPath: resolve('./', 'static'),
txtPath: resolve('./', 'db')
}
// 设置跨域
app.use(cors({
origin: function (ctx) {
if (ctx.url.indexOf('fetch') > -1) {
return '*'; // 允许来自所有域名请求
}
return ''; // 这样就能只允许 http://localhost 这个域名的请求了
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒
credentials: true,
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'x-requested-with'],
}))
// 创建异步线程
function createPromisefork(childUrl, data) {
const res = fork(childUrl)
data && res.send(data)
return new Promise(reslove => {
res.on('message', f => {
reslove(f)
})
})
}
app.use(async (ctx, next) => {
if(ctx.url === '/fetch') {
const data = ctx.request.body;
const res = await createPromisefork('./child.js', data)
// 获取文件路径
const txtUrls = [];
let reg = /.*?(\d+)\.\w*$/;
glob.sync(`${config.txtPath}/*.*`).forEach(item => {
if(reg.test(item)) {
txtUrls.push(item.replace(reg, '$1'))
}
})
ctx.body = {
state: res,
data: txtUrls,
msg: res ? '抓取完成' : '抓取失败,原因可能是非法的url或者请求超时或者服务器内部错误'
}
}
await next()
})
app.listen(80)
использоватьumi3 + antd4.0Создайте внешний интерфейс сканера
Внешний интерфейс платформы сканера разработан umi3+antd4.0, потому что antd4.0 действительно улучшил объем и производительность по сравнению с предыдущей версией, а также сделал более разумное разделение компонентов. -конечная страница реализована Относительно просто, весь интерфейсный код используетhooksНапишите менее 200 строк, и я не буду вводить их здесь по одной, вы можете изучать и исследовать на гитхабе автора.
- адрес проекта на гитхабе:Интересная краулер-платформа на основе Apify+node+react.
Интерфейс выглядит следующим образом:
Вы можете клонировать и запускать его локально, или вы можете разработать свой собственный на основе этого.приложение-краулер.Техническая документация Используемый адрес проекта
- apifyодин дляJavaScriptмасштабируемыйwebБиблиотека рептилий
- Puppeteer
- koa-- Фреймворк для веб-разработки следующего поколения, основанный на платформе nodejs.
наконец
Если вы хотите узнать большеигра Н5, webpack,node,gulp,css3,javascript,nodeJS,визуализация данных холстаВ ожидании передовых знаний и реальных сражений, добро пожаловать в нашу техническую группу в общедоступном аккаунте «Интересный передний конец», чтобы вместе учиться и обсуждать, а также вместе исследовать границы переднего плана.
больше рекомендаций
- Разработать приложение для круга друзей для программистов на основе react/vue
- Создайте общую платформу конфигурации управления формами на основе реакции (то же самое)
- Краткое изложение нескольких распространенных алгоритмов сортировки и алгоритмов поиска, необходимых программистам
- Резюме нескольких очень интересных моментов знания javascript
- Передовая расширенная реализация одностороннего и двустороннего связанного списка от нуля до единицы
- Предварительное исследование архитектуры микро-интерфейса и моего инвентаря технологий фронт-энда.
- Используйте nodeJs для разработки собственного приложения для создания изображения.
- Реализовать проект полного стека CMS от 0 до 1 на основе nodeJS (включено)
- Реализовать проект полного стека CMS от 0 до 1 на основе nodeJS (средний) (включая исходный код)
- Vue и React для проекта полного стека CMS (часть 2) (включая исходный код)
- 5 минут, чтобы научить вас писать фиктивный сервер данных с помощью nodeJS
- Научу разрабатывать библиотеку компонентов на основе vue с нуля до единицы
- Научу строить компонентную систему фронтенд команды от 0 до 1 (обязательно для продвинутых и продвинутых)
- 10 минут, чтобы научить вас 8 часто используемым кастомным крючкам вручную
- 15 минут, чтобы понять шаблоны проектирования JavaScript, которые должны знать фронтенд-инженеры (с подробной картой ума и исходным кодом)
- «Внешняя сводка боя» с использованием postMessage для реализации подключаемого робота междоменного чата.