Maison > Article > interface Web > Comment récupérer efficacement les données du Web avec Node.js ?
Recommandations associées : "Tutoriel nodejs"
En raison des énormes améliorations de Javascript et de l'introduction Un environnement d'exécution appelé NodeJS est devenu l'un des langages les plus populaires et les plus utilisés. Qu'il s'agisse d'une application web ou d'une application mobile, Javascript dispose désormais des bons outils. Cet article explique comment utiliser Node.js pour explorer efficacement les données du Web.
Cet article s'adresse principalement aux programmeurs ayant une certaine expérience de JavaScript. Si vous avez une bonne compréhension du web scraping mais que vous êtes nouveau dans JavaScript, cet article vous sera toujours utile.
Grâce à cet article, vous apprendrez :
Javascript est un langage de programmation simple et moderne, développé à l'origine pour ajouter des effets dynamiques aux pages Web dans le navigateur. Lorsqu'un site Web est chargé, le code Javascript est exécuté par le moteur Javascript du navigateur. Pour que Javascript interagisse avec votre navigateur, celui-ci fournit également un environnement d'exécution (document, fenêtre, etc.).
Cela signifie que Javascript ne peut pas interagir directement avec ou fonctionner sur les ressources informatiques. Par exemple, dans un serveur Web, le serveur doit pouvoir interagir avec le système de fichiers afin de pouvoir lire et écrire des fichiers.
Node.js permet à Javascript de s'exécuter non seulement côté client, mais également côté serveur. Pour ce faire, son fondateur Ryan Dahl a choisi le moteur Javascript v8 du navigateur Google Chrome et l'a intégré dans un programme Node développé en C++. Node.js est donc un environnement d'exécution qui permet également au code Javascript de s'exécuter sur le serveur.
Contrairement à d'autres langages tels que C ou C++ qui gèrent la concurrence via plusieurs threads, Node.js utilise un seul thread principal et exécute des tâches de manière non bloquante à l'aide d'une boucle d'événements.
Créer un serveur Web simple est très simple, comme indiqué ci-dessous :
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}/`); });
Si Node.js est installé, vous pouvez essayer d'exécuter le code ci-dessus. Node.js est idéal pour les programmes gourmands en E/S.
Un client HTTP est un outil qui peut envoyer des requêtes à un serveur puis recevoir des réponses du serveur. La couche sous-jacente de tous les outils mentionnés ci-dessous consiste à utiliser un client HTTP pour accéder au site Web que vous souhaitez explorer.
Request est l'un des clients HTTP les plus utilisés dans l'écosystème Javascript, mais l'auteur de la bibliothèque Request a officiellement déclaré qu'il était obsolète. Mais cela ne veut pas dire qu’il n’est plus disponible, de nombreuses bibliothèques l’utilisent encore et il est très simple à utiliser. Faire des requêtes HTTP avec Request est très simple :
const request = require('request') request('https://www.reddit.com/r/programming.json', function ( error, response, body ) { console.error('error:', error) console.log('body:', body) })
Vous pouvez trouver la bibliothèque Request sur Github et l'installer est très simple. Vous pouvez également trouver l'avis de dépréciation et sa signification sur https://github.com/request/re...
Axios est un client HTTP basé sur des promesses qui s'exécute dans le navigateur et Node.js. Si vous utilisez Typescript, axios remplacera les types intégrés pour vous. Faire des requêtes HTTP via Axios est très simple. Il est livré avec la prise en charge de Promise par défaut au lieu d'utiliser des rappels dans Request :
const axios = require('axios') axios .get('https://www.reddit.com/r/programming.json') .then((response) => { console.log(response) }) .catch((error) => { console.error(error) });
Si vous aimez le sucre syntaxique async/await de l'API Promises, vous pouvez également utiliser , mais comme l'attente de haut niveau est encore au étape 3, nous devons d'abord la remplacer par une fonction asynchrone :
async function getForum() { try { const response = await axios.get( 'https://www.reddit.com/r/programming.json' ) console.log(response) } catch (error) { console.error(error) } }
Tout ce que vous avez à faire est d'appeler getForum
! La bibliothèque Axios peut être trouvée sur https://github.com/axios/axios.
Comme Axios, Superagent est un autre client HTTP puissant qui prend en charge Promise et async/wait le sucre syntaxique. Il possède une API assez simple comme Axios, mais Superagent est moins populaire en raison de plus de dépendances.
L'utilisation de promesses, d'async/await ou de rappels pour effectuer une requête HTTP au superagent ressemble à ceci :
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) } }
Peut être trouvé sur https://github.com/visionmedi. .. Trouvez le Superagent.
在没有任何依赖性的情况下,最简单的进行网络抓取的方法是,使用 HTTP 客户端查询网页时,在收到的 HTML 字符串上使用一堆正则表达式。正则表达式不那么灵活,而且很多专业人士和业余爱好者都难以编写正确的正则表达式。
让我们试一试,假设其中有一个带有用户名的标签,我们需要该用户名,这类似于你依赖正则表达式时必须执行的操作
const htmlString = '<label>Username: John Doe</label>' const result = htmlString.match(/<label>(.+)<\/label>/) console.log(result[1], result[1].split(": ")[1]) // Username: John Doe, John Doe
在 Javascript 中,match()
通常返回一个数组,该数组包含与正则表达式匹配的所有内容。第二个元素(在索引1中)将找到我们想要的 <label></label>
标记的 textContent
或 innerHTML
。但是结果中包含一些不需要的文本( “Username: “),必须将其删除。
如你所见,对于一个非常简单的用例,步骤和要做的工作都很多。这就是为什么应该依赖 HTML 解析器的原因,我们将在后面讨论。
Cheerio 是一个高效轻便的库,它使你可以在服务器端使用 JQuery 的丰富而强大的 API。如果你以前用过 JQuery,那么将会对 Cheerio 感到很熟悉,它消除了 DOM 所有不一致和与浏览器相关的功能,并公开了一种有效的 API 来解析和操作 DOM。
const cheerio = require('cheerio') const $ = cheerio.load('<h2 class="title">Hello world</h2>') $('h2.title').text('Hello there!') $('h2').addClass('welcome') $.html() // <h2 class="title welcome">Hello there!</h2>
如你所见,Cheerio 与 JQuery 用起来非常相似。
但是,尽管它的工作方式不同于网络浏览器,也就这意味着它不能:
因此,如果你尝试爬取的网站或 Web 应用是严重依赖 Javascript 的(例如“单页应用”),那么 Cheerio 并不是最佳选择,你可能不得不依赖稍后讨论的其他选项。
为了展示 Cheerio 的强大功能,我们将尝试在 Reddit 中抓取 r/programming 论坛,尝试获取帖子名称列表。
首先,通过运行以下命令来安装 Cheerio 和 axios:npm install cheerio axios
。
然后创建一个名为 crawler.js
的新文件,并复制粘贴以下代码:
const axios = require('axios'); const cheerio = require('cheerio'); const getPostTitles = async () => { try { const { data } = await axios.get( 'https://old.reddit.com/r/programming/' ); const $ = cheerio.load(data); const postTitles = []; $('div > p.title > a').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.js 中使用的文档对象模型的纯 Javascript 实现,如前所述,DOM 对 Node 不可用,但是 JSDOM 是最接近的。它或多或少地模仿了浏览器。
由于创建了 DOM,所以可以通过编程与要爬取的 Web 应用或网站进行交互,也可以模拟单击按钮。如果你熟悉 DOM 操作,那么使用 JSDOM 将会非常简单。
const { JSDOM } = require('jsdom') const { document } = new JSDOM( '<h2 class="title">Hello world</h2>' ).window const heading = document.querySelector('.title') heading.textContent = 'Hello there!' heading.classList.add('welcome') 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('axios') 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 项执行以下功能:
dangerously
时允许执行事件 handler 和任何 Javascript 代码。如果你不清楚将要运行的脚本的安全性,则最好将 runScripts 设置为“outside-only”,这会把所有提供的 Javascript 规范附加到 “window” 对象,从而阻止在 inside 上执行的任何脚本。<script></script>
标记声明的任何外部脚本(例如:从 CDN 提取的 JQuery 库)创建 DOM 后,用相同的 DOM 方法得到第一篇文章的 upvote 按钮,然后单击。要验证是否确实单击了它,可以检查 classList
中是否有一个名为 upmod
的类。如果存在于 classList
中,则返回一条消息。
打开终端并运行 node crawler.js
,然后会看到一个整洁的字符串,该字符串将表明帖子是否被赞过。尽管这个例子很简单,但你可以在这个基础上构建功能强大的东西,例如,一个围绕特定用户的帖子进行投票的机器人。
如果你不喜欢缺乏表达能力的 JSDOM ,并且实践中要依赖于许多此类操作,或者需要重新创建许多不同的 DOM,那么下面将是更好的选择。
顾名思义,Puppeteer 允许你以编程方式操纵浏览器,就像操纵木偶一样。它通过为开发人员提供高级 API 来默认控制无头版本的 Chrome。
Puppeteer 比上述工具更有用,因为它可以使你像真正的人在与浏览器进行交互一样对网络进行爬取。这就具备了一些以前没有的可能性:
它还可以在 Web 爬取之外的其他任务中发挥重要作用,例如 UI 测试、辅助性能优化等。
通常你会想要截取网站的屏幕截图,也许是为了了解竞争对手的产品目录,可以用 puppeteer 来做到。首先运行以下命令安装 puppeteer,:npm install puppeteer
这将下载 Chromium 的 bundle 版本,根据操作系统的不同,该版本大约 180 MB 至 300 MB。如果你要禁用此功能。
让我们尝试在 Reddit 中获取 r/programming 论坛的屏幕截图和 PDF,创建一个名为 crawler.js
的新文件,然后复制粘贴以下代码:
const puppeteer = require('puppeteer') async function getVisual() { try { const URL = 'https://www.reddit.com/r/programming/' const browser = await puppeteer.launch() const page = await browser.newPage() await page.goto(URL) await page.screenshot({ path: 'screenshot.png' }) await page.pdf({ path: 'page.pdf' }) 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.jpg
和 page.pdf
。
Nightmare 是类似 Puppeteer 的高级浏览器自动化库,该库使用 Electron,但据说速度是其前身 PhantomJS 的两倍。
如果你在某种程度上不喜欢 Puppeteer 或对 Chromium 捆绑包的大小感到沮丧,那么 nightmare 是一个理想的选择。首先,运行以下命令安装 nightmare 库:npm install nightmare
然后,一旦下载了 nightmare,我们将用它通过 Google 搜索引擎找到 ScrapingBee 的网站。创建一个名为crawler.js
的文件,然后将以下代码复制粘贴到其中:
const Nightmare = require('nightmare') const nightmare = Nightmare() nightmare .goto('https://www.google.com/') .type("input[title='Search']", 'ScrapingBee') .click("input[value='Google Search']") .wait('#rso > div:nth-child(1) > div > div > div.r > a') .evaluate( () => document.querySelector( '#rso > div:nth-child(1) > div > div > div.r > a' ).href ) .end() .then((link) => { console.log('Scraping Bee Web Link': link) }) .catch((error) => { console.error('Search failed:', error) })
首先创建一个 Nighmare 实例,然后通过调用 goto()
将该实例定向到 Google 搜索引擎,加载后,使用其选择器获取搜索框,然后使用搜索框的值(输入标签)更改为“ScrapingBee”。完成后,通过单击 “Google搜索” 按钮提交搜索表单。然后告诉 Nightmare 等到第一个链接加载完毕,一旦完成,它将使用 DOM 方法来获取包含该链接的定位标记的 href
属性的值。
最后,完成所有操作后,链接将打印到控制台。
原文地址:https://www.scrapingbee.com/blog/web-scraping-javascript/
作者:Shenesh Perera
更多编程相关知识,请访问:编程教学!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!