【问题标题】:Getting all images from a webpage and save the to disk programmatically (NodeJS & Javascript)从网页获取所有图像并以编程方式保存到磁盘(NodeJS 和 Javascript)
【发布时间】:2019-01-30 14:31:00
【问题描述】:

我需要从几个网站获取大量图像并将它们下载到我的磁盘上以便我可以使用它们(将它们上传到 blob (azure) 然后将链接保存到我的数据库)。

获取图像

我知道如何使用 JS 从 html 中获取图像,例如其中一个我会创建一个 for 循环并执行:

document.getElementsByClassName('person')[i].querySelector('div').querySelector('img').getAttribute('src')

然后我会得到所有图片的链接。

保存图像

我还看到我可以使用 node 和 fs 模块将文件保存到磁盘,方法是:

function saveImageToDisk(url, localPath) {var fullUrl = url;
var file = fs.createWriteStream(localPath);
var request = https.get(url, function(response) {
response.pipe(file);
});

}

如何将它们放在一起

这是我卡住的地方,我不知道如何连接这两个部分(脚本和 nodejs 代码),我想获取图像以及图像名称(在这种情况下为 alt 标记)和然后在节点中使用它们将图像上传到 blob,并将它们的名称和图像 blob url 放入我的数据库中。

我以为我可以下载 html 页面,然后将 JS 脚本放在正文的底部,但是我不知道如何将 url 传递给 nodejs 代码。

我该怎么做?

我不太习惯使用脚本,我主要使用没有它们的节点,我对它们的交互以及如何将 js 脚本连接到我的代码感到有些困惑。

这也是解决此问题的最佳方法,还是我没有看到更简单/更好的方法?

【问题讨论】:

  • 看看谷歌的 Puppeteer,你可以通过节点github.com/GoogleChrome/puppeteer从网站访问和获取数据
  • 使用ajax将url数组从客户端发送到节点服务器

标签: javascript node.js web-scraping


【解决方案1】:

感觉你应该使用爬虫。以下代码应该可以工作(使用 npm 模块crawler):

const Crawler = require("crawler")

const c = new Crawler({
    callback: function(error, res, done) {
        if (error) {
            console.log({error})
        } else {
            const images = res.$('.person div img')
            images.each(index => {
                // here you can save the file or save them in an array to download them later
                console.log({
                    src: images[index].attribs.src,
                    alt: images[index].attribs.alt,
                })
            })
        }
    }
})

c.queue('https://www.yoursite.com')

【讨论】:

  • 非常感谢这篇文章中的所有精彩答案让我感到非常震惊,我最终选择了 Crawler,因为它看起来很容易实现。爬虫 npm 还在维护吗?将来可能会出现吗?
  • @Jack 我不知道。老实说,我从未使用过基于节点的爬虫。我只是在谷歌上搜索了一个,然后做了这个小演示。但是,由于它每周仍然有很多下载量,并且最后一次提交也不远,因此使用起来似乎是安全的。
【解决方案2】:

您需要在 Web API(用于 DOM 解析等)和 Node.js API 之间架起一座桥梁。例如,一些用于 Node.js 的无头浏览器管理工具。比如说,你可以在这个脚本中使用puppeteer

'use strict';

const puppeteer = require('puppeteer');
const https = require('https');
const fs = require('fs');

(async function main() {
  try {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    await page.goto('https://en.wikipedia.org/wiki/Image');

    const imgURLs = await page.evaluate(() =>
      Array.from(
        document.querySelectorAll('#mw-content-text img.thumbimage'),
        ({ src }) => src,
      )
    );
    console.log(imgURLs);
    await browser.close();

    imgURLs.forEach((imgURL, i) => {
      https.get(imgURL, (response) => {
        response.pipe(fs.createWriteStream(`${i++}.${imgURL.slice(-3)}`));
      });
    });
  } catch (err) {
    console.error(err);
  }
})();

您甚至可以使用浏览器已下载的图片只下载一次图片。此脚本保存相同的图像,但只有一个请求会话,而不使用 https Node.js 模块(这样可以节省时间、网络流量和服务器工作量):

'use strict';

const puppeteer = require('puppeteer');
const fs = require('fs');

(async function main() {
  try {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    const allImgResponses = {};
    page.on('response', (response) => {
      if (response.request().resourceType() === 'image') {
        allImgResponses[response.url()] = response;
      }
    });

    await page.goto('https://en.wikipedia.org/wiki/Image');

    const selecedImgURLs = await page.evaluate(() =>
      Array.from(
        document.querySelectorAll('#mw-content-text img.thumbimage'),
        ({ src }) => src,
      )
    );
    console.log(selecedImgURLs);

    let i = 0;
    for (const imgURL of selecedImgURLs) {
      fs.writeFileSync(
        `${i++}.${imgURL.slice(-3)}`,
        await allImgResponses[imgURL].buffer(),
      );
    }

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

【讨论】:

  • 我添加了更高效的方法。
  • 您好,非常感谢您的回答。我对这篇文章的所有答案感到震惊。虽然我选择了爬虫选项,Pupetteer 绝对看起来是更受欢迎和更常用的软件包,但除了你的帖子之外,我无法找到任何关于如何快速使用 pupette 进行此操作的信息,因为我想做一点不同。我一定会进一步研究 pupeteer 并努力学习并实施它。非常感谢您提供此信息!
  • @Jack 谢谢你,编码愉快)
【解决方案3】:

我建议您使用dom-parser 模块。见这里:https://www.npmjs.com/package/dom-parser

通过这样做,您可以使用http.get() 下载整个html 文件并使用dom-parser 对其进行解析。然后从 HTML 文件中提取您需要的所有信息。对于图像 URL,使用您的 saveImageToDisk() 函数。

按照您的想法,您必须将 JS 脚本添加到您提到的 html 文件中。但除此之外,您还必须使用 Ajax (xmlHttpRequest) 将 URL 发布到 nodeJS-Server。

【讨论】:

  • 您好,非常感谢您的出色回答,我对所有出色的答案和选项感到震惊,这次我使用爬虫,因为我觉得它可能更有效率,但谢谢您非常感谢 dom-parser 让我大开眼界,我会记住的。
【解决方案4】:

您可以使用Promise & 在其中完成获取所有图像并将图像 url 放入数组中的工作。然后在 then 方法中,您可以迭代数组并每次调用saveImageToDisk,或者您可以通过幻灯片修改将数组发送到中间层。第二种选择更好,因为它只会进行一次网络调用

function getImages() {
  return new Promise((resolve, reject) => {
     // Array.from will create an array
     // map will return a new array with all the image url
    let k = Array.from(document.getElementsByClassName('person')[0].querySelector('div')
        .querySelectorAll('img'))
      .map((item) => {
        return item.getAttribute('src')
      })
    resolve(k)
  })
}
getImages().then((d) => {
 // it will work only after the promise is resolved
  console.log('****', d);
  (item => {
    // call saveImageToDisk function

  })

})

function saveImageToDisk(url, localPath) {
  var fullUrl = url;
  var file = fs.createWriteStream(localPath);
  var request = https.get(url, function(response) {
    response.pipe(file);
  });
<div class='person'>
  <div>
    <img src='https://www.fast-growing-trees.com/images/P/Leyland-Cypress-450-MAIN.jpg'>
    <img src='http://cdn.shopify.com/s/files/1/2473/3486/products/Cypress_Leyland_2_Horticopia_d1b5b63a-8bf7-4897-96fb-05320bf3d81b_grande.jpg?v=1532991076'>
    <img src='https://www.fast-growing-trees.com/images/P/Live-Oak-Tree-450w.jpg'>
    <img src='https://www.greatgardenplants.com/images/uploads/452_1262_popup.jpg'>
    <img src='https://shop.arborday.org/data/default/images/catalog/600/Turnkey/1/Leyland-Cypress_3-828.jpg'>


    <img src='https://images-na.ssl-images-amazon.com/images/I/51RZkKnrlSL._SX425_.jpg'>

    <img src='https://thumbs-prod.si-cdn.com/Z3JYiuJ96ReLq04NCT1B94sTd4E=/800x600/filters:no_upscale()/https://public-media.si-cdn.com/filer/06/9c/069cfb16-c46c-4742-85f0-3c7e45fa139d/mar2018_a05_talkingtrees.jpg'>


  </div>

【讨论】:

  • 我可以只在浏览器控制台中运行它,还是可以在我的节点应用程序中运行它,以便我可以将图像和名称从 alt 标签发送到我的数据库???
猜你喜欢
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多