js提取指定网站内容(HTTP客户端/awaitawait//async/)

优采云 发布时间: 2021-12-20 00:24

  js提取指定网站内容(HTTP客户端/awaitawait//async/)

  HTTP 客户端是一种工具,可以向服务器发送请求,然后接收服务器的响应。下面提到的所有工具的底层都是使用一个HTTP客户端来访问你想要抓取的网站。

  要求

  Request 是 Javascript 生态系统中使用最广泛的 HTTP 客户端之一,但 Request 库的作者已正式声明它已被弃用。但这并不意味着它不可用,相当多的库仍在使用它,并且非常易于使用。使用 Request 发出 HTTP 请求非常简单:

  1const request = require('request')

2request('https://www.reddit.com/r/programming.json', function (

3 error,

4 response,

5 body

6) {

7 console.error('error:', error)

8 console.log('body:', body)

9})

  你可以在 Github 上找到 Request 库,安装非常简单。您还可以找到弃用通知及其含义。

  阿克西奥斯

  Axios 是一个基于 Promise 的 HTTP 客户端,可以在浏览器和 Node.js 中运行。如果您使用 Typescript,那么 axios 将为您覆盖内置类型。通过 Axios 发起 HTTP 请求非常简单。默认情况下,它带有 Promise 支持,而不是在请求中使用回调:

   1const axios = require('axios')

2

3axios

4 .get('https://www.reddit.com/r/programming.json')

5 .then((response) => {

6 console.log(response)

7 })

8 .catch((error) => {

9 console.error(error)

10 });

  如果你喜欢 Promises API 的 async/await 语法糖,你也可以使用它,但由于顶级 await 仍处于第 3 阶段,我们不得不使用异步函数来代替:

   1async function getForum() {

2 try {

3 const response = await axios.get(

4 'https://www.reddit.com/r/programming.json'

5 )

6 console.log(response)

7 } catch (error) {

8 console.error(error)

9 }

10}

  您所要做的就是致电 getForum!可以在 Axios 库上找到。

  超级代理

  与 Axios 一样,Superagent 是另一个强大的 HTTP 客户端,支持 Promise 和 async/await 语法糖。它有一个类似 Axios 的相当简单的 API,但由于更多的依赖关系,Superagent 不太受欢迎。

  使用 promise、async/await 或回调向 Superagent 发出 HTTP 请求如下所示:

   1const superagent = require("superagent")

2const forumURL = "https://www.reddit.com/r/programming.json"

3

4// callbacks

5superagent

6 .get(forumURL)

7 .end((error, response) => {

8 console.log(response)

9 })

10

11// promises

12superagent

13 .get(forumURL)

14 .then((response) => {

15 console.log(response)

16 })

17 .catch((error) => {

18 console.error(error)

19 })

20

21// promises with async/await

22async function getForum() {

23 try {

24 const response = await superagent.get(forumURL)

25 console.log(response)

26 } catch (error) {

27 console.error(error)

28 }

29}

  您可以在以下位置找到 Superagent。

  正则表达式:艰难的方式

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

  试试看,假设有一个带有用户名的标签,而我们需要那个用户名,这类似于你依赖正则表达式时必须做的

  1const htmlString = 'Username: John Doe'

2const result = htmlString.match(/(.+)/)

3

4console.log(result[1], result[1].split(": ")[1])

5// Username: John Doe, John Doe

  在 Javascript 中,match() 通常返回一个数组,其中收录与正则表达式匹配的所有内容。第二个元素(在索引 1 中)将找到我们想要的标记的 textContent 或 innerHTML。但结果收录一些不必要的文本(“用户名:”),必须删除。

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

  Cheerio:用于遍历 DOM 的核心 JQuery

  Cheerio 是一个高效且可移植的库,它允许您在服务器端使用 JQuery 丰富而强大的 API。如果您之前使用过 JQuery,您就会熟悉 Cheerio。它消除了 DOM 的所有不一致和浏览器相关功能,并公开了一个有效的 API 来解析和操作 DOM。

  1const cheerio = require('cheerio')

2const $ = cheerio.load('Hello world')

3

4$('h2.title').text('Hello there!')

5$('h2').addClass('welcome')

6

7$.html()

8// Hello there!

  如您所见,Cheerio 与 JQuery 非常相似。

  但是,即使它的工作方式与 Web 浏览器不同,这也意味着它不能:

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

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

  首先,通过运行以下命令安装 Cheerio 和 axios:npm installcheerio axios。

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

   1const axios = require('axios');

2const cheerio = require('cheerio');

3

4const getPostTitles = async () => {

5 try {

6 const { data } = await axios.get(

7 'https://old.reddit.com/r/programming/'

8 );

9 const $ = cheerio.load(data);

10 const postTitles = [];

11

12 $('div > p.title > a').each((_idx, el) => {

13 const postTitle = $(el).text()

14 postTitles.push(postTitle)

15 });

16

17 return postTitles;

18 } catch (error) {

19 throw error;

20 }

21};

22

23getPostTitles()

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

  getPostTitles() 是一个异步函数,它将抓取旧的 reddit r/编程论坛。首先通过axios HTTP客户端库使用简单的HTTP GET请求获取网站的HTML,然后使用cheerio.load()函数将html数据输入到Cheerio中。

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

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

  现在,打开终端,运行node crawler.js,你会看到一个近似titles的数组,这个数组会很长。虽然这是一个非常简单的用例,但它展示了 Cheerio 提供的 API 的简单本质。

  如果您的用例需要执行 Javascript 并加载外部源,那么以下选项会有所帮助。

  JSDOM:节点的 DOM

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

  因为DOM是创建的,所以你可以通过编程与web应用或者你想爬取的网站进行交互,也可以模拟点击一个按钮。如果你熟悉 DOM 操作,使用 JSDOM 会非常简单。

   1const { JSDOM } = require('jsdom')

2const { document } = new JSDOM(

3 'Hello world'

4).window

5const heading = document.querySelector('.title')

6heading.textContent = 'Hello there!'

7heading.classList.add('welcome')

8

9heading.innerHTML

10// Hello there!

  在代码中用JSDOM创建一个DOM,然后你就可以用和浏览器DOM一样的方法和属性来操作这个DOM了。

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

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

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

   1const { JSDOM } = require("jsdom")

2const axios = require('axios')

3

4const upvoteFirstPost = async () => {

5 try {

6 const { data } = await axios.get("https://old.reddit.com/r/programming/");

7 const dom = new JSDOM(data, {

8 runScripts: "dangerously",

9 resources: "usable"

10 });

11 const { document } = dom.window;

12 const firstPost = document.querySelector("div > div.midcol > div.arrow");

13 firstPost.click();

14 const isUpvoted = firstPost.classList.contains("upmod");

15 const msg = isUpvoted

16 ? "Post has been upvoted successfully!"

17 : "The post has not been upvoted!";

18

19 return msg;

20 } catch (error) {

21 throw error;

22 }

23};

24

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

  upvoteFirstPost() 是一个异步函数,它将获得 r/programming 中的第一篇文章,然后对其进行投票。axios 发送 HTTP GET 请求以获取指定 URL 的 HTML。然后从之前获得的 HTML 创建一个新的 DOM。JSDOM 构造函数将 HTML 作为第一个参数,将选项作为第二个参数。添加的两个选项执行以下功能:

  创建DOM后,使用相同的DOM方法获取第一篇文章文章的upvote按钮,然后点击。要验证它是否真的被点击,您可以检查 classList 中是否有名为 upmod 的类。如果它存在于 classList 中,则返回一条消息。

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

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

  Puppeteer:无头浏览器

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

  摘自 Puppeter Docs Puppeteer 比上述工具更有用,因为它允许您像真人与浏览器交互一样抓取网络。这开辟了一些以前不可用的可能性:

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

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

  这将下载 Chromium 的捆绑版本,大约 180 MB 到 300 MB,具体取决于操作系统。如果要禁用此功能。

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

   1const puppeteer = require('puppeteer')

2

3async function getVisual() {

4 try {

5 const URL = 'https://www.reddit.com/r/programming/'

6 const browser = await puppeteer.launch()

7 const page = await browser.newPage()

8

9 await page.goto(URL)

10 await page.screenshot({ path: 'screenshot.png' })

11 await page.pdf({ path: 'page.pdf' })

12

13 await browser.close()

14 } catch (error) {

15 console.error(error)

16 }

17}

18

19getVisual()

  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 的替代品

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

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

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

   1const Nightmare = require('nightmare')

2const nightmare = Nightmare()

3

4nightmare

5 .goto('https://www.google.com/')

6 .type("input[title='Search']", 'ScrapingBee')

7 .click("input[value='Google Search']")

8 .wait('#rso > div:nth-child(1) > div > div > div.r > a')

9 .evaluate(

10 () =>

11 document.querySelector(

12 '#rso > div:nth-child(1) > div > div > div.r > a'

13 ).href

14 )

15 .end()

16 .then((link) => {

17 console.log('Scraping Bee Web Link': link)

18 })

19 .catch((error) => {

20 console.error('Search failed:', error)

21 })

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

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

  总结

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线