Как создать веб-парсер? — Scraping-bot.io
В эру больших данных, веб-парсинг является настоящим спасителем. Чтобы еще больше сэкономить время, вы можете использовать ScrapingBot вместе с веб-парсером.
Что такое веб-парсер?
Парсер, или паук, - это интернет-бот, который индексирует и посещает каждый URL-адрес, с которым он сталкивается. Его целью является посещение веб-сайта от начала до конца, определение содержимого каждой веб-страницы и возможность нахождения местоположения любой информации. Самые известные веб-парсеры - это поисковые системы, например, GoogleBot. Когда веб-сайт находится в сети, эти парсеры посещают его и считывают его содержимое, чтобы отобразить его на соответствующих страницах результатов поиска.
Как работает веб-паук?
Начиная с корневого URL или набора записей, паук получает веб-страницы и находит другие URL-адреса для посещения, называемые сидами, на этой странице. Все найденные сиды добавляются в список URL-адресов для посещения. Этот список называется горизонтом. Паук организует ссылки в две нити: ссылки для посещения и уже посещенные ссылки. Он будет продолжать посещать ссылки, пока горизонт не станет пустым.
Поскольку список сидов может быть очень длинным, паук должен организовывать их, руководствуясь несколькими критериями, и определять приоритеты, какие из них посещать первыми и повторно. Чтобы узнать, какие страницы являются более важными для парсинга, бот будет учитывать, сколько ссылок ведет на этот URL и как часто его посещают обычные пользователи.
В чем разница между веб-скрейпером и веб-краулером?
Краулинг, по определению, всегда подразумевает веб. Цель краулера - следовать по ссылкам для достижения множества страниц и анализировать их метаданные и содержимое.
Парсинг возможен и вне веба. Например, вы можете извлечь некоторую информацию из базы данных. Парсинг - это извлечение данных из веба или базы данных.
Зачем нужен веб-краулер?
С помощью веб-парсинга вы экономите огромное количество времени, автоматически получая нужную информацию, а не ища и копируя ее вручную. Однако вам все равно нужно парсить страницу за страницей. Веб-краулинг позволяет собирать, организовывать и посещать все страницы, присутствующие на корневой странице, с возможностью исключить некоторые ссылки. Корневой страницей может быть страница с результатами поиска или категория.
Например, вы можете выбрать категорию товаров или страницу с результатами поиска на Amazon в качестве входных данных и просканировать ее, чтобы извлечь все детали о продукте и ограничиться первыми 10 страницами с рекомендуемыми продуктами.
Как создать веб-сканер?
Первое, что вам нужно сделать, это потоки:
- Посещенные URL-адреса
- URL-адреса для посещения (очередь)
Чтобы избежать повторного сканирования одной и той же страницы, URL-адрес должен автоматически перемещаться в поток посещенных URL-адресов, как только вы закончили его сканировать. На каждой веб-странице вы найдете новые URL-адреса. Большинство из них будут добавлены в очередь, но некоторые из них могут не добавлять никакой ценности для вашей цели. Поэтому вам также нужно установить правила для URL-адресов, которые вас не интересуют.
Удаление дубликатов является важной частью сканирования веб-страниц. На некоторых веб-сайтах, особенно на электронной коммерции, одна веб-страница может иметь несколько URL-адресов. Чтобы вы могли спарсить эту страницу только один раз, лучший способ сделать это - найти канонический тег в коде. Все страницы с одинаковым содержимым будут иметь общий канонический URL-адрес, и это единственная ссылка, которую вам нужно будет сканировать и парсить.
Вот пример канонического тега в HTML:
Вот основные шаги для создания сканера:
- Шаг 1: Добавьте один или несколько URL-адресов для посещения.
- Шаг 2: Извлеките ссылку из URL-адресов для посещения и добавьте ее в поток посещенных URL-адресов.
- Шаг 3: Получите содержимое страницы и спарсите интересующие вас данные с помощью ScrapingBot API.
- Шаг 4: Разберите все URL-адреса, присутствующие на странице, и добавьте их в URL-адреса для посещения, если они соответствуют установленным вами правилам и не соответствуют ни одному из посещенных URL-адресов.
- Шаг 5: Повторите шаги с 2 по 4, пока список URL-адресов для посещения не станет пустым.
NB: Шаги 1 и 2 должны быть синхронизированы.
Аналогично веб-парсингу, есть некоторые правила, которые нужно соблюдать, когда сканируется веб-сайт. Файл robots.txt указывает, какие области карты сайта не должны посещаться сканером. Кроме того, сканер должен избегать перегрузки веб-сайта, ограничивая скорость сканирования, чтобы сохранить хороший опыт для пользователей. В противном случае веб-сайт, с которого происходит парсинг, может решить заблокировать IP сканера или принять другие меры.
Вот пример сканера с использованием ScrapingBot API с двумя зависимостями: request и cheerio. Вам нужно использовать как минимум nodeJs 8 из-за использования await/async.
const request = require("request");
const util = require("util");
const rp = util.promisify(request);
const sleep = util.promisify(setTimeout);
const cheerio = require('cheerio');
const { URL } = require('url');
let seenLinks = {};
let rootNode = {};
let currentNode = {};
let linksQueue = [];
let printList = [];
let previousDepth = 0;
let maxCrawlingDepth = 5;
let options = null;
let mainDomain = null;
let mainParsedUrl = null;
class CreateLink {
constructor(linkURL, depth, parent) {
this.url = linkURL;
this.depth = depth;
this.parent = parent;
this.children = [];
}
}
// ваши учетные данные для ScrapingBot
let username = "yourUsername",
apiKey = "yourApiKey",
apiEndPoint = "http://api.scraping-bot.io/scrape/raw-html",
auth = "Basic " + Buffer.from(username + ":" + apiKey).toString("base64");
let requestOptions = {
method: 'POST',
url: apiEndPoint,
json: {
url: "this will be replaced in the findLinks function", // опции ScrapingBot
options: {
useChrome: false, // если вы хотите использовать безголовый Chrome, ВНИМАНИЕ, для этой опции будет использоваться два вызова API
premiumProxy: false, // если вы хотите использовать премиум-прокси для разблокировки Amazon, LinkedIn (потребуется 10 вызовов)
}
},
headers: {
Accept: 'application/json',
Authorization: auth
}
}
// Запуск приложения, здесь вы должны указать адрес, с которого хотите начать сканирование
// второй параметр - это глубина, с 1 он будет сканировать все ссылки, найденные на первой странице, но не находить ссылки на других страницах
// если вы установите 2, он будет сканировать все ссылки на первой странице и все ссылки на страницах второго уровня, будьте осторожны с этим на огромном веб-сайте, это будет представлять огромное количество страниц для сканирования
// рекомендуется ограничить до 5 уровней
crawlBFS("https://www.scraping-bot.io/", 1);
async function crawlBFS(startURL, maxDepth = 5) {
try {
mainParsedUrl = new URL(startURL);
} catch (e) {
console.log("URL is not valid", e);
return;
}
mainDomain = mainParsedUrl.hostname;
maxCrawlingDepth = maxDepth;
startLinkObj = new CreateLink(startURL, 0, null);
rootNode = currentNode = startLinkObj;
addToLinkQueue(currentNode);
await findLinks(currentNode);
}
async function findLinks(linkObj) {
requestOptions.json.url = linkObj.url;
console.log("Scraping URL : " + linkObj.url);
let response;
try {
response = await rp(requestOptions);
if (response.statusCode !== 200) {
if (response.statusCode === 401 || response.statusCode === 405) {
console.log("autentication failed check your credentials");
} else {
console.log("an error occurred check the URL" + response.statusCode, response.body);
}
return;
}
let $ = cheerio.load(response.body);
let links = $('body').find('a').filter(function (i, el) {
return $(this).attr('href') != null;
}).map(function (i, x) {
return $(this).attr('href');
});
if (links.length > 0) {
links.map(function (i, x) {
let reqLink = checkDomain(x);
if (reqLink) {
if (reqLink != linkObj.url) {
newLinkObj = new CreateLink(reqLink, linkObj.depth + 1, linkObj);
addToLinkQueue(newLinkObj);
}
}
});
} else {
console.log("No more links found for " + requestOptions.url);
}
let nextLinkObj = getNextInQueue();
if (nextLinkObj && nextLinkObj.depth <= maxCrawlingDepth) {
let minimumWaitTime = 500; // полсекунды
let maximumWaitTime = 5000; // максимум пять секунд
let waitTime = Math.round(minimumWaitTime + (Math.random() * (maximumWaitTime - minimumWaitTime)));
console.log("wait for " + waitTime + " milliseconds");
await sleep(waitTime);
await crawl(nextLinkObj);
} else {
setRootNode();
printTree();
}
} catch (err) {
console.log("Something Went Wrong...", err);
}
}
function setRootNode() {
while (currentNode.parent != null) {
currentNode = currentNode.parent;
}
rootNode = currentNode;
}
function printTree() {
addToPrintDFS(rootNode);
console.log(printList.join("\n|"));
}
function addToPrintDFS(node) {
let spaces = Array(node.depth * 3).join("-");
printList.push(spaces + node.url);
if (node.children) {
node.children.map(function (i, x) {
{
addToPrintDFS(i);
}
});
}
}
function checkDomain(linkURL) {
let parsedUrl;
let fullUrl = true;
try {
parsedUrl = new URL(linkURL);
} catch (error) {
fullUrl = false;
}
if (fullUrl === false) {
if (linkURL.indexOf("/") === 0) {
return mainParsedUrl.protocol + "//" + mainParsedUrl.hostname + linkURL.split("#")[0];
} else if (linkURL.indexOf("#") === 0) {
return;
} else {
let path = currentNode.url.match('.*\/')[0];
return path + linkURL;
}
}
let mainHostDomain = parsedUrl.hostname;
if (mainDomain == mainHostDomain) {
parsedUrl.hash = "";
return parsedUrl.href;
} else {
return;
}
}
function addToLinkQueue(linkobj) {
if (!linkInSeenListExists(linkobj)) {
if (linkobj.parent != null) {
linkobj.parent.children.push(linkobj);
}
linksQueue.push(linkobj);
addToSeen(linkobj);
}
}
function getNextInQueue() {
let nextLink = linksQueue.shift();
if (nextLink && nextLink.depth > previousDepth) {
previousDepth = nextLink.depth;
console.log(`------- CRAWLING ON DEPTH LEVEL ${previousDepth} --------`);
}
return nextLink;
}
function peekInQueue() {
return linksQueue[0];
}
function addToSeen(linkObj) {
seenLinks[linkObj.url] = linkObj;
}
function linkInSeenListExists(linkObj) {
return seenLinks[linkObj.url] == null ? false : true;
}
Проверьте наш API Store ⬇️https://www.scraping-bot.io/web-scraping-api-store/