>  기사  >  웹 프론트엔드  >  Node.js를 사용하여 웹에서 데이터를 효율적으로 스크랩하는 방법은 무엇입니까?

Node.js를 사용하여 웹에서 데이터를 효율적으로 스크랩하는 방법은 무엇입니까?

青灯夜游
青灯夜游앞으로
2021-01-30 18:22:343357검색

Node.js를 사용하여 웹에서 데이터를 효율적으로 스크랩하는 방법은 무엇입니까?

관련 권장사항: "nodejs Tutorial"

Javascript의 엄청난 발전과 NodeJS라는 런타임의 도입으로 인해 가장 인기 있고 널리 사용되는 언어 중 하나가 되었습니다. 웹 애플리케이션이든 모바일 애플리케이션이든 이제 Javascript에는 올바른 도구가 있습니다. 이 문서에서는 Node.js를 사용하여 웹에서 데이터를 효율적으로 크롤링하는 방법을 설명합니다.

사전 요구 사항

이 문서는 주로 JavaScript 경험이 있는 프로그래머를 대상으로 합니다. 웹 스크래핑에 대해 잘 알고 있지만 JavaScript를 처음 사용하는 경우에도 이 문서가 도움이 될 것입니다.

  • ✅ JavaScript를 알아보세요
  • ✅ DevTools를 사용하여 요소 선택기를 추출하는 방법을 알아보세요
  • ✅ 일부 ES6을 알아보세요(선택 사항)

배우게 될 내용

이 기사를 통해 배울 내용:

  • Node에 대해 자세히 알아보세요. js 항목
  • 여러 HTTP 클라이언트를 사용하여 웹 스크래핑 프로세스 지원
  • 시험되고 테스트된 여러 라이브러리를 활용하여 웹 크롤링

Node.js에 대해 알아보기

Javascript는 원래 동적 효과를 추가하기 위해 개발된 간단한 최신 프로그래밍 언어입니다. 브라우저의 웹 페이지. 웹사이트가 로드되면 Javascript 코드는 브라우저의 Javascript 엔진에 의해 실행됩니다. Javascript가 브라우저와 상호 작용할 수 있도록 브라우저는 런타임 환경(문서, 창 등)도 제공합니다.

이는 Javascript가 컴퓨터 리소스와 직접 상호 작용하거나 작동할 수 없음을 의미합니다. 예를 들어, 웹 서버에서 서버는 파일을 읽고 쓸 수 있도록 파일 시스템과 상호 작용할 수 있어야 합니다.

Node.js를 사용하면 Javascript를 클라이언트 측뿐만 아니라 서버 측에서도 실행할 수 있습니다. 이를 위해 창립자 Ryan Dahl은 Google Chrome 브라우저의 v8 Javascript 엔진을 선택하고 이를 C++로 개발된 Node 프로그램에 내장했습니다. 따라서 Node.js는 Javascript 코드가 서버에서도 실행될 수 있도록 하는 런타임 환경입니다.

다중 스레드를 통해 동시성을 처리하는 C나 C++와 같은 다른 언어와 달리 Node.js는 단일 메인 스레드를 활용하고 이벤트 루프의 도움으로 비차단 방식으로 작업을 수행합니다.

간단한 웹 서버를 만드는 방법은 아래와 같이 매우 간단합니다.

const http = require('http');
const PORT = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, () => {
  console.log(`Server running at PORT:${port}/`);
});

Node.js가 설치되어 있으면 위 코드를 실행해 볼 수 있습니다. Node.js는 I/O 집약적인 프로그램에 적합합니다.

HTTP 클라이언트: 웹에 액세스

HTTP 클라이언트는 서버에 요청을 보낸 다음 서버로부터 응답을 받을 수 있는 도구입니다. 아래에 언급된 모든 도구의 기본 계층은 HTTP 클라이언트를 사용하여 크롤링하려는 웹 사이트에 액세스하는 것입니다.

Request

Request는 Javascript 생태계에서 가장 널리 사용되는 HTTP 클라이언트 중 하나이지만 Request 라이브러리의 작성자는 공식적으로 이 클라이언트가 더 이상 사용되지 않는다고 선언했습니다. 하지만 이것이 더 이상 사용할 수 없다는 의미는 아니며 꽤 많은 도서관에서 여전히 이를 사용하고 있으며 사용하기 매우 쉽습니다. Request를 사용하여 HTTP 요청을 하는 것은 매우 간단합니다.

const request = require('request')
request('https://www.reddit.com/r/programming.json', function (
  error,
  response,
  body
) {
  console.error('error:', error)
  console.log('body:', body)
})

Github에서 Request 라이브러리를 찾을 수 있으며 설치도 매우 간단합니다. 또한 https://github.com/request/re...에서 지원 중단 공지와 그 의미를 확인할 수 있습니다.

Axios

Axios는 브라우저와 Node.js에서 실행되는 Promise 기반 HTTP 클라이언트입니다. Typescript를 사용하면 axios가 내장 유형을 재정의합니다. Axios를 통해 HTTP 요청을 수행하는 것은 매우 간단합니다. 요청에서 콜백을 사용하는 대신 기본적으로 Promise 지원이 제공됩니다.

const axios = require('axios')

axios
    .get('https://www.reddit.com/r/programming.json')
    .then((response) => {
        console.log(response)
    })
    .catch((error) => {
        console.error(error)
    });

Promises API의 async/await 구문 설탕이 마음에 든다면 이를 사용할 수도 있지만 최상위 레벨 Wait는 아직 stage 3이므로 먼저 비동기 함수로 교체해야 합니다.

async function getForum() {
    try {
        const response = await axios.get(
            'https://www.reddit.com/r/programming.json'
        )
        console.log(response)
    } catch (error) {
        console.error(error)
    }
}

해야 할 일은 호출getForum뿐입니다! Axios 라이브러리는 https://github.com/axisios/axios에서 찾을 수 있습니다.

Superagent

Axios와 마찬가지로 Superagent는 Promise 및 async/await 구문 설탕을 지원하는 또 다른 강력한 HTTP 클라이언트입니다. Axios와 같은 매우 간단한 API가 있지만 Superagent는 종속성이 많아 인기가 떨어집니다.

Promise, async/await 또는 콜백을 사용하여 슈퍼에이전트에 대한 HTTP 요청은 다음과 같습니다.

const superagent = require("superagent")
const forumURL = "https://www.reddit.com/r/programming.json"

// callbacks
superagent
    .get(forumURL)
    .end((error, response) => {
        console.log(response)
    })

// promises
superagent
    .get(forumURL)
    .then((response) => {
        console.log(response)
    })
    .catch((error) => {
        console.error(error)
    })

// promises with async/await
async function getForum() {
    try {
        const response = await superagent.get(forumURL)
        console.log(response)
    } catch (error) {
        console.error(error)
    }
}

슈퍼에이전트는 https://github.com/visionmedi...에서 찾을 수 있습니다.

正则表达式:艰难的路

在没有任何依赖性的情况下,最简单的进行网络抓取的方法是,使用 HTTP 客户端查询网页时,在收到的 HTML 字符串上使用一堆正则表达式。正则表达式不那么灵活,而且很多专业人士和业余爱好者都难以编写正确的正则表达式。

让我们试一试,假设其中有一个带有用户名的标签,我们需要该用户名,这类似于你依赖正则表达式时必须执行的操作

const htmlString = &#39;<label>Username: John Doe</label>&#39;
const result = htmlString.match(/<label>(.+)<\/label>/)

console.log(result[1], result[1].split(": ")[1])
// Username: John Doe, John Doe

在 Javascript 中,match()  通常返回一个数组,该数组包含与正则表达式匹配的所有内容。第二个元素(在索引1中)将找到我们想要的 <label></label> 标记的 textContentinnerHTML。但是结果中包含一些不需要的文本( “Username: “),必须将其删除。

如你所见,对于一个非常简单的用例,步骤和要做的工作都很多。这就是为什么应该依赖 HTML 解析器的原因,我们将在后面讨论。

Cheerio:用于遍历 DOM 的核心 JQuery

Cheerio 是一个高效轻便的库,它使你可以在服务器端使用 JQuery 的丰富而强大的 API。如果你以前用过 JQuery,那么将会对 Cheerio 感到很熟悉,它消除了 DOM 所有不一致和与浏览器相关的功能,并公开了一种有效的 API 来解析和操作 DOM。

const cheerio = require(&#39;cheerio&#39;)
const $ = cheerio.load(&#39;<h2 class="title">Hello world</h2>&#39;)

$(&#39;h2.title&#39;).text(&#39;Hello there!&#39;)
$(&#39;h2&#39;).addClass(&#39;welcome&#39;)

$.html()
// <h2 class="title welcome">Hello there!</h2>

如你所见,Cheerio 与 JQuery 用起来非常相似。

但是,尽管它的工作方式不同于网络浏览器,也就这意味着它不能:

  • 渲染任何解析的或操纵 DOM 元素
  • 应用 CSS 或加载外部资源
  • 执行 JavaScript

因此,如果你尝试爬取的网站或 Web 应用是严重依赖 Javascript 的(例如“单页应用”),那么 Cheerio 并不是最佳选择,你可能不得不依赖稍后讨论的其他选项。

为了展示 Cheerio 的强大功能,我们将尝试在 Reddit 中抓取 r/programming 论坛,尝试获取帖子名称列表。

首先,通过运行以下命令来安装 Cheerio 和 axios:npm install cheerio axios

然后创建一个名为 crawler.js 的新文件,并复制粘贴以下代码:

const axios = require(&#39;axios&#39;);
const cheerio = require(&#39;cheerio&#39;);

const getPostTitles = async () => {
    try {
        const { data } = await axios.get(
            &#39;https://old.reddit.com/r/programming/&#39;
        );
        const $ = cheerio.load(data);
        const postTitles = [];

        $(&#39;div > p.title > a&#39;).each((_idx, el) => {
            const postTitle = $(el).text()
            postTitles.push(postTitle)
        });

        return postTitles;
    } catch (error) {
        throw error;
    }
};

getPostTitles()
.then((postTitles) => console.log(postTitles));

getPostTitles() 是一个异步函数,将对旧的 reddit 的 r/programming 论坛进行爬取。首先,用带有 axios HTTP 客户端库的简单 HTTP GET 请求获取网站的 HTML,然后用 cheerio.load() 函数将 html 数据输入到 Cheerio 中。

然后在浏览器的 Dev Tools 帮助下,可以获得可以定位所有列表项的选择器。如果你使用过 JQuery,则必须非常熟悉 $('div> p.title> a')。这将得到所有帖子,因为你只希望单独获取每个帖子的标题,所以必须遍历每个帖子,这些操作是在 each() 函数的帮助下完成的。

要从每个标题中提取文本,必须在 Cheerio 的帮助下获取 DOM元素( el 指代当前元素)。然后在每个元素上调用 text() 能够为你提供文本。

现在,打开终端并运行 node crawler.js,然后你将看到大约存有标题的数组,它会很长。尽管这是一个非常简单的用例,但它展示了 Cheerio 提供的 API 的简单性质。

如果你的用例需要执行 Javascript 并加载外部源,那么以下几个选项将很有帮助。

JSDOM:Node 的 DOM

JSDOM 是在 Node.js 中使用的文档对象模型的纯 Javascript 实现,如前所述,DOM 对 Node 不可用,但是 JSDOM 是最接近的。它或多或少地模仿了浏览器。

由于创建了 DOM,所以可以通过编程与要爬取的 Web 应用或网站进行交互,也可以模拟单击按钮。如果你熟悉 DOM 操作,那么使用 JSDOM 将会非常简单。

const { JSDOM } = require(&#39;jsdom&#39;)
const { document } = new JSDOM(
    &#39;<h2 class="title">Hello world</h2>&#39;
).window
const heading = document.querySelector(&#39;.title&#39;)
heading.textContent = &#39;Hello there!&#39;
heading.classList.add(&#39;welcome&#39;)

heading.innerHTML
// <h2 class="title welcome">Hello there!</h2>

代码中用 JSDOM 创建一个 DOM,然后你可以用和操纵浏览器 DOM 相同的方法和属性来操纵该 DOM。

为了演示如何用 JSDOM 与网站进行交互,我们将获得 Reddit r/programming 论坛的第一篇帖子并对其进行投票,然后验证该帖子是否已被投票。

首先运行以下命令来安装 jsdom 和 axios:npm install jsdom axios

然后创建名为 crawler.js的文件,并复制粘贴以下代码:

const { JSDOM } = require("jsdom")
const axios = require(&#39;axios&#39;)

const upvoteFirstPost = async () => {
  try {
    const { data } = await axios.get("https://old.reddit.com/r/programming/");
    const dom = new JSDOM(data, {
      runScripts: "dangerously",
      resources: "usable"
    });
    const { document } = dom.window;
    const firstPost = document.querySelector("div > div.midcol > div.arrow");
    firstPost.click();
    const isUpvoted = firstPost.classList.contains("upmod");
    const msg = isUpvoted
      ? "Post has been upvoted successfully!"
      : "The post has not been upvoted!";

    return msg;
  } catch (error) {
    throw error;
  }
};

upvoteFirstPost().then(msg => console.log(msg));

upvoteFirstPost() 是一个异步函数,它将在 r/programming 中获取第一个帖子,然后对其进行投票。axios 发送 HTTP GET 请求获取指定 URL 的HTML。然后通过先前获取的 HTML 来创建新的 DOM。 JSDOM 构造函数把HTML 作为第一个参数,把 option 作为第二个参数,已添加的 2 个 option 项执行以下功能:

  • runScripts:设置为 dangerously 时允许执行事件 handler 和任何 Javascript 代码。如果你不清楚将要运行的脚本的安全性,则最好将 runScripts 设置为“outside-only”,这会把所有提供的 Javascript 规范附加到 “window” 对象,从而阻止在 inside 上执行的任何脚本。
  • resources:设置为“usable”时,允许加载用 <script></script> 标记声明的任何外部脚本(例如:从 CDN 提取的 JQuery 库)

创建 DOM 后,用相同的 DOM 方法得到第一篇文章的 upvote 按钮,然后单击。要验证是否确实单击了它,可以检查 classList 中是否有一个名为 upmod 的类。如果存在于 classList 中,则返回一条消息。

打开终端并运行 node crawler.js,然后会看到一个整洁的字符串,该字符串将表明帖子是否被赞过。尽管这个例子很简单,但你可以在这个基础上构建功能强大的东西,例如,一个围绕特定用户的帖子进行投票的机器人。

如果你不喜欢缺乏表达能力的 JSDOM ,并且实践中要依赖于许多此类操作,或者需要重新创建许多不同的 DOM,那么下面将是更好的选择。

Puppeteer:无头浏览器

顾名思义,Puppeteer 允许你以编程方式操纵浏览器,就像操纵木偶一样。它通过为开发人员提供高级 API 来默认控制无头版本的 Chrome。

Node.js를 사용하여 웹에서 데이터를 효율적으로 스크랩하는 방법은 무엇입니까?

Puppeteer 比上述工具更有用,因为它可以使你像真正的人在与浏览器进行交互一样对网络进行爬取。这就具备了一些以前没有的可能性:

  • 你可以获取屏幕截图或生成页面 PDF。
  • 可以抓取单页应用并生成预渲染的内容。
  • 自动执行许多不同的用户交互,例如键盘输入、表单提交、导航等。

它还可以在 Web 爬取之外的其他任务中发挥重要作用,例如 UI 测试、辅助性能优化等。

通常你会想要截取网站的屏幕截图,也许是为了了解竞争对手的产品目录,可以用 puppeteer 来做到。首先运行以下命令安装 puppeteer,:npm install puppeteer

这将下载 Chromium 的 bundle 版本,根据操作系统的不同,该版本大约 180 MB 至 300 MB。如果你要禁用此功能。

让我们尝试在 Reddit 中获取 r/programming 论坛的屏幕截图和 PDF,创建一个名为 crawler.js的新文件,然后复制粘贴以下代码:

const puppeteer = require(&#39;puppeteer&#39;)

async function getVisual() {
    try {
        const URL = &#39;https://www.reddit.com/r/programming/&#39;
        const browser = await puppeteer.launch()
        const page = await browser.newPage()

        await page.goto(URL)
        await page.screenshot({ path: &#39;screenshot.png&#39; })
        await page.pdf({ path: &#39;page.pdf&#39; })

        await browser.close()
    } catch (error) {
        console.error(error)
    }
}

getVisual()

getVisual() 是一个异步函数,它将获 URL 变量中 url 对应的屏幕截图和 pdf。首先,通过 puppeteer.launch() 创建浏览器实例,然后创建一个新页面。可以将该页面视为常规浏览器中的选项卡。然后通过以 URL 为参数调用  page.goto() ,将先前创建的页面定向到指定的 URL。最终,浏览器实例与页面一起被销毁。

完成操作并完成页面加载后,将分别使用 page.screenshot() 和  page.pdf() 获取屏幕截图和 pdf。你也可以侦听 javascript load 事件,然后执行这些操作,在生产环境级别下强烈建议这样做。

在终端上运行 node crawler.js  ,几秒钟后,你会注意到已经创建了两个文件,分别名为  screenshot.jpgpage.pdf

Nightmare:Puppeteer 的替代者

Nightmare 是类似 Puppeteer 的高级浏览器自动化库,该库使用 Electron,但据说速度是其前身 PhantomJS 的两倍。

如果你在某种程度上不喜欢 Puppeteer 或对 Chromium 捆绑包的大小感到沮丧,那么 nightmare 是一个理想的选择。首先,运行以下命令安装 nightmare 库:npm install nightmare

然后,一旦下载了 nightmare,我们将用它通过 Google 搜索引擎找到 ScrapingBee 的网站。创建一个名为crawler.js的文件,然后将以下代码复制粘贴到其中:

const Nightmare = require(&#39;nightmare&#39;)
const nightmare = Nightmare()

nightmare
    .goto(&#39;https://www.google.com/&#39;)
    .type("input[title=&#39;Search&#39;]", &#39;ScrapingBee&#39;)
    .click("input[value=&#39;Google Search&#39;]")
    .wait(&#39;#rso > div:nth-child(1) > div > div > div.r > a&#39;)
    .evaluate(
        () =>
            document.querySelector(
                &#39;#rso > div:nth-child(1) > div > div > div.r > a&#39;
            ).href
    )
    .end()
    .then((link) => {
        console.log(&#39;Scraping Bee Web Link&#39;: link)
    })
    .catch((error) => {
        console.error(&#39;Search failed:&#39;, error)
    })

首先创建一个 Nighmare 实例,然后通过调用 goto() 将该实例定向到 Google 搜索引擎,加载后,使用其选择器获取搜索框,然后使用搜索框的值(输入标签)更改为“ScrapingBee”。完成后,通过单击 “Google搜索” 按钮提交搜索表单。然后告诉 Nightmare 等到第一个链接加载完毕,一旦完成,它将使用 DOM 方法来获取包含该链接的定位标记的 href 属性的值。

最后,完成所有操作后,链接将打印到控制台。

总结

  • Node.js 是 Javascript 在服务器端的运行时环境。由于事件循环机制,它具有“非阻塞”性质。
  • HTTP客户端(例如 Axios、Superagent 和 Request)用于将 HTTP 请求发送到服务器并接收响应。
  • Cheerio 把  JQuery 的优点抽出来,在服务器端 进行 Web 爬取是唯一的目的,但不执行 Javascript 代码。
  • JSDOM 根据标准 Javascript规范 从 HTML 字符串中创建一个 DOM,并允许你对其执行DOM操作。
  • Puppeteer and Nightmare  是高级(high-level )浏览器自动化库,可让你以编程方式去操作 Web 应用,就像真实的人正在与之交互一样。

原文地址:https://www.scrapingbee.com/blog/web-scraping-javascript/

作者:Shenesh Perera

更多编程相关知识,请访问:编程教学!!

위 내용은 Node.js를 사용하여 웹에서 데이터를 효율적으로 스크랩하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제