Node.js爬虫只会Cheerio?来试试Puppeteer!

news2024/11/17 5:43:16

简介

上篇文章我们学习了如何通过 Cheerio 来爬取静态页面,但是我们没有办法处理动态渲染页面的数据
关于 Cheerio 的学习请查看 都 2023 年了还不会 Node.js 爬虫?快学起来!

今天我们学习如何使用 Puppeteer 来轻松地完成我们解决不了的爬虫任务

什么是Puppeteer

Puppeteer 是一个由 Google 开发的 Node.js 库,它提供了一组用于控制 Headless Chrome 的 API。

Headless Chrome 是 Chrome 浏览器的无界面版本,可以用于模拟用户行为、测试网站、爬取数据等多种用途。

Puppeteer 提供了一系列的 API,可以让我们控制 Chrome 浏览器的各种行为,例如打开网页、模拟用户操作、获取网页内容等。

总之,Puppeteer 是一个非常强大且易于使用的工具,可以帮助我们轻松地完成各种 Web 自动化和爬虫任务。

Puppeteer的优势

  1. 完全支持最新的 Web 标准和技术:Puppeteer 支持最新的 Web 标准和技术,例如 ES6、Promise、async/await 等。这样,我们可以使用最新的 JavaScript 特性来编写爬虫代码,使代码更加简洁、易读、易维护。
  2. 支持动态网站的爬取:Puppeteer 可以处理 JavaScript 渲染的页面,也就是说它可以爬取动态网站,这是其他爬虫框架所不具备的优势。
  3. 可以模拟真实的用户行为:Puppeteer 可以模拟真实的用户行为,例如点击按钮、填写表单、滚动页面等。
  4. 可以生成 PDF 和截屏等多种格式的输出:Puppeteer 可以生成 PDF 和截屏等多种格式的输出,这对于需要对网站进行分析或者展示结果的情况非常有用。
  5. 可以与其他 Node.js 库和框架无缝集成:Puppeteer 可以与其他 Node.js 库和框架无缝集成,例如 Express、Koa、Mocha、Jest 等。这样,我们可以将 Puppeteer 用于自动化测试、Web 开发等领域。

Puppeteer基本用法

安装配置

使用 npm 安装 Puppeteer:

npm install puppeteer

Puppeteer 默认会从 Google 的服务器下载 Chrome 浏览器二进制文件,如果您的网络环境无法访问 Google 的服务器,可以通过以下方式配置 Puppeteer:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true, // 是否启用无头模式
    executablePath: '/path/to/Chrome', // Chrome 浏览器可执行文件路径
    args: ['--no-sandbox'], // Chrome 启动参数
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');
  // ...
  await browser.close();
})();

我们通过 executablePath 和 args 选项来指定 Chrome 浏览器的可执行文件路径和启动参数

这样,我们就可以在没有网络的情况下使用 Puppeteer

打开网页

使用 puppeteer.launch() 方法启动 Chrome 浏览器,并创建一个新的页面对象:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch(); // 启动 Chrome 浏览器
  const page = await browser.newPage(); // 创建一个新的页面对象
  await page.goto('https://example.com'); // 打开一个网页
  // ...
  await browser.close();
})();

在上面的代码中,我们使用 puppeteer.launch() 方法启动 Chrome 浏览器,并使用 browser.newPage() 方法创建一个新的页面对象,然后,我们使用 page.goto() 方法打开一个网页。

launch配置

在使用 puppeteer.launch() 方法启动 Chrome 浏览器时,可以通过传递一些配置选项来控制 Chrome 浏览器的启动行为。下面是常用的配置选项:

  • headless:是否启用无头模式,默认为 true
  • executablePath:Chrome 浏览器可执行文件路径。
  • args:Chrome 启动参数。
  • defaultViewport:默认的浏览器窗口大小。
  • timeout:超时时间,单位为毫秒。

下面是一个使用 puppeteer.launch() 方法的示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    executablePath: '/path/to/Chrome',
    args: ['--no-sandbox'],
    defaultViewport: {
      width: 1280,
      height: 800,
    },
    timeout: 30000,
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');
  // ...
  await browser.close();
})();

在上面的代码中,我们使用了常用的配置选项:

  • headless 选项设置为 true,启用无头模式。
  • executablePath 选项指定 Chrome 浏览器的可执行文件路径。
  • args 选项指定 Chrome 启动参数,例如 --no-sandbox 表示禁用沙盒模式。
  • defaultViewport 选项指定默认的浏览器窗口大小。
  • timeout 选项指定超时时间为 30 秒。

下面是更详细的配置列表:

puppeteer.launch({
  headless: true, // 是否以无头模式运行浏览器,默认为true
  executablePath: '', // 可执行文件路径,如果不指定则自动下载
  args: [], // 命令行参数数组
  ignoreDefaultArgs: false, // 是否忽略默认的命令行参数
  defaultViewport: null, // 默认视窗大小,null表示自动设置
  slowMo: 0, // 延迟毫秒数,用于调试
  timeout: 30000, // 超时时间,单位为毫秒
  devtools: false, // 是否打开DevTools面板,默认为false
  pipe: false, // 是否将浏览器启动的I/O连接通过管道传递,默认为false
  handleSIGINT: true, // 是否在收到SIGINT信号时关闭浏览器,默认为true
  handleSIGTERM: true, // 是否在收到SIGTERM信号时关闭浏览器,默认为true
  handleSIGHUP: true, // 是否在收到SIGHUP信号时关闭浏览器,默认为true
  env: {}, // 环境变量对象
  userDataDir: '', // 用户数据目录路径
  dumpio: false, // 是否将浏览器I/O输出到进程的stdout和stderr中,默认为false
  executablePath: '', // 可执行文件路径,如果不指定则自动下载
  ignoreHTTPSErrors: false, // 是否忽略HTTPS错误,默认为false
  ignoreCertificateErrors: false // 是否忽略SSL证书错误,默认为false
});

获取元素

使用 page.$() 方法获取一个元素:

const element = await page.$('selector');

在上面的代码中,我们使用 page.$() 方法获取一个元素。

其中,selector 参数可以是 CSS 选择器、XPath 表达式或者其他选择器。

点击按钮

使用 element.click() 方法点击一个按钮:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await element.click();
  await browser.close();
})();

在上面的代码中,我们使用 element.click() 方法点击一个按钮。

填写表单

使用 element.type() 方法填写表单:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await element.type('text');
  await browser.close();
})();

在上面的代码中,我们使用 element.type() 方法填写表单。其中,text 参数是要填写的文本内容。

截屏

使用 page.screenshot() 方法截屏:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'example.png' });
  await browser.close();
})();

在上面的代码中,我们使用 page.screenshot() 方法截屏,并将截屏保存为 PNG 格式的文件。

还有很多常见的puppeteer基本用法,这里不一一介绍,用到的时候去官方文档查看对应的API即可

Puppeteer进阶用法

模拟用户行为

点击元素

使用 element.click() 方法模拟点击元素:

await element.click();

在上面的代码中,我们使用 element.click() 方法模拟点击元素。

输入文本

使用 element.type() 方法输入文本:

await element.type('text');

在上面的代码中,我们使用 element.type() 方法输入文本。其中,text 参数是要输入的文本。

选择选项

使用 element.select() 方法选择选项:

await element.select('value');

在上面的代码中,我们使用 element.select() 方法选择选项。其中,value 参数是要选择的选项值。

滚动页面

使用 page.evaluate() 方法执行 JavaScript 代码来滚动页面:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');

  let previousHeight;
  while (true) {
    const currentHeight = await page.evaluate(() => {
      return document.documentElement.scrollHeight;
    });
    if (currentHeight === previousHeight) {
      break;
    }
    previousHeight = currentHeight;
    await page.evaluate(() => {
      window.scrollBy(0, window.innerHeight);
    });
    await page.waitForTimeout(1000);
  }

  const content = await page.$('#content');
  console.log(await content.textContent());

  await browser.close();
})();

在上面的代码中,我们使用 Puppeteer 打开一个需要滚动的网页,并模拟滚动操作,直到页面滚动到底部,然后获取页面中的内容。

处理动态内容

等待元素出现

使用 page.waitForSelector() 方法等待元素出现:

await page.waitForSelector('selector');

在上面的代码中,我们使用 page.waitForSelector() 方法等待元素出现。

其中,selector 参数可以是 CSS 选择器、XPath 表达式或者其他选择器。

等待页面加载完成

使用 page.waitForNavigation() 方法等待页面加载完成:

await page.goto('https://example.com');
await page.waitForNavigation();

在上面的代码中,我们使用 page.goto() 方法打开一个网页,并使用 page.waitForNavigation() 方法等待页面加载完成。

处理验证码

Puppeteer 可以通过打码平台来识别验证码。以下是一个使用打码平台的示例:

const antiCaptcha = require('anticaptcha');
const client = new antiCaptcha('API_KEY');

const image = await page.screenshot({ encoding: 'base64' });
const taskId = await client.createTask({
  type: 'ImageToTextTask',
  body: image,
});

const solution = await client.getTaskSolution(taskId);
await page.type('#captcha', solution.text);

在上面的代码中,我们使用 page.screenshot() 方法获取验证码图片,并使用打码平台识别验证码,最后将识别结果输入到验证码输入框中。

处理登录

以下是一个使用 Puppeteer 处理登录的示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com/login');

  await page.type('#username', 'your-username');
  await page.type('#password', 'your-password');
  await page.click('#login-button');

  await page.waitForNavigation();

  const content = await page.$('#content');
  console.log(await content.textContent());

  await browser.close();
})();

在上面的代码中,我们使用 page.goto() 方法打开登录页面,并使用 page.type() 方法输入用户名和密码,最后使用 page.click() 方法提交登录表单,然后获取页面中的内容。

定时爬取

puppeteer可以实现定时爬取数据,以下是一个使用 Puppeteer 配合 node-schedule 实现定时爬取的示例:

const puppeteer = require('puppeteer');
const schedule = require('node-schedule');

// 每隔一段时间执行一次爬取数据函数
const job = schedule.scheduleJob('自动爬虫任务', '30 20 * * * *', async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com');
  
  // 模拟滚动到页面底部
  await page.evaluate(async () => {
    await new Promise(resolve => {
      let totalHeight = 0;
      const distance = 100;
      const timer = setInterval(() => {
        const scrollHeight = document.body.scrollHeight;
        window.scrollBy(0, distance);
        totalHeight += distance;

        if (totalHeight >= scrollHeight) {
          clearInterval(timer);
          resolve();
        }
      }, 100);
    });
  });
  
  // 等待列表加载完成
  await page.waitForSelector('.list-item');
  
  // 获取列表数据
  const listItems = await page.$$('.list-item');
  const data = await Promise.all(listItems.map(async listItem => {
    const title = await listItem.$eval('.title', el => el.textContent);
    const description = await listItem.$eval('.description', el => el.textContent);
    return { title, description };
  }));
  
  console.log(data);
  
  await browser.close();
});

// 取消定时任务
// job.cancel();

在这个示例中,我们使用node-schedule库创建了一个定时任务,每小时的0分执行一次。在任务中包含了获取数据的代码,与前面的示例类似。如果需要取消定时任务,可以使用job.cancel()方法。

自动化测试

Puppeteer 可以用于自动化测试,以下是一个使用 Puppeteer 进行自动化测试的示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');

  const title = await page.title();
  if (title !== 'Example Domain') {
    console.error('Title is not correct');
  }

  const url = await page.url();
  if (url !== 'https://example.com/') {
    console.error('URL is not correct');
  }

  await browser.close();
})();

在上面的代码中,我们使用 Puppeteer 打开一个网页,并检查页面标题和 URL 是否正确。

实战案例

CSDN作者榜

我们打开网址https://blog.csdn.net/rank/list/total来看看CSDN作者总榜
在这里插入图片描述

还是和静态页面一样,我们来分析一下页面结构,找到我们想要拿到的数据:

在这里插入图片描述

下面我们来用puppeteer来实现爬取这个作者榜:

// 无头浏览器模块
const puppeteer = require("puppeteer");
const fs = require('fs');

// 目标页面
const crawlPage = "https://blog.csdn.net/rank/list/total";

// 网页爬虫
(async function crawler() {
    //创建实例
    const browser = await puppeteer.launch({
        //无浏览器界面启动
        headless: "new",
        //放慢浏览器执行速度,方便测试观察
        slowMo: 100,
        // 设置打开的浏览器窗口尺寸
        defaultViewport: { width: 960, height: 540 },
    });

    // 新开一个tab页面
    const page = await browser.newPage();
    // 加载目标页,在 500ms 内没有任何网络请求才算加载完
    await page.goto(crawlPage, { waitUntil: "networkidle0" });

    // 在无头浏览器页面dom环境,获取页面数据
    const authorList = await page.evaluate(() => {
        const list = [];
        document.querySelectorAll(".floor-rank-total .floor-rank-total-item").forEach((ele) => {
            const rank = ele.querySelector(".total-content .number").innerText;
            const title = ele.querySelector(".total-box dd a").innerText;
            const fans = ele.querySelector(".total-box dt span:nth-child(1)").innerText;
            list.push({
                '排名': rank,
                '作者': title,
                '粉丝': fans,
            });
        });
        return list;
    });

    // console.log(authorList);
    // 将数据写入文件中
    fs.writeFile('./csdnAuthor.json', JSON.stringify(authorList), function (err, data) {
        if (err) {
            throw err
        }
        console.log('文件保存成功');
    })

    // 关闭tab页
    await page.close();
    // 关闭实例
    await browser.close();
})();

看看json文件里有什么吧:

在这里插入图片描述

没有问题,成功拿到了我们想要的CSDN作者总榜页面的排名、作者和粉丝数量!

掘金小册

掘金小册的内容质量非常高,推荐大家可以去看一看,接下来我们来爬取一下目前发布所有的掘金小册

我们来分析一下掘金小册的页面结构,我们想要拿到的数据有:小册名称、小册简介、小册作者、小册价格

在这里插入图片描述

我们来用puppeteer来实现爬取,要注意的是掘金小册页面不会全部加载所有小册,因此我们需要用puppeteer模拟用户行为,将页面列表滚动到最底部,加载完所有的小册列表数据后再进行爬取:

// 无头浏览器模块
const puppeteer = require("puppeteer");
const fs = require('fs')

// 目标页面
const crawlPage = "https://juejin.cn/course";

// 网页爬虫
(async function crawler() {
    //创建实例
    const browser = await puppeteer.launch({
        //无浏览器界面启动
        headless: "new",
        //放慢浏览器执行速度,方便测试观察
        // slowMo: 100,
        // 设置打开的浏览器窗口尺寸
        defaultViewport: { width: 960, height: 540 },
    });

    // 新开一个tab页面
    const page = await browser.newPage();
    // 加载目标页,在 500ms 内没有任何网络请求才算加载完
    await page.goto(crawlPage, { waitUntil: "networkidle0" });

    // 模拟滚动到页面底部
    await page.evaluate(async () => {
        await new Promise((resolve) => {
          let totalHeight = 0;
          const distance = 200;
          const timer = setInterval(() => {
            const scrollHeight = document.body.scrollHeight;
            window.scrollBy(0, distance);
            totalHeight += distance;
    
            if (totalHeight >= scrollHeight) {
              clearInterval(timer);
              resolve();
            }
          }, 200);
        });
    });

    // 等待列表加载完成
    await page.waitForSelector('.books-view');

    // 在无头浏览器页面dom环境,获取页面数据
    const articleList = await page.evaluate(() => {
        const list = [];
        const itemSelector = ".books-view > div:last-child > div > div > a";
        document.querySelectorAll(itemSelector).forEach(async(ele) => {
            const title = await ele.querySelector(".book-info .text-highlight").innerText;
            const desc = await ele.querySelector(".book-info .text-highlight.desc").innerText;
            const author = await ele.querySelector(".book-info .author .name").innerText;
            const price = await ele.querySelector(".book-info .other .origin-price").innerText;
            list.push({
                '小册名称': title,
                '小册简介': desc,
                '小册作者': author,
                '小册价格': price,
            });
        });
        return list;
    });

    // console.log(articleList);
    // 将数据写入文件中
    fs.writeFile('./xiaoce.json', JSON.stringify(articleList), function (err, data) {
        if (err) {
            throw err
        }
        console.log('文件保存成功');
    })

    // 关闭tab页
    await page.close();
    // 关闭实例
    await browser.close();
})();

保存的JSON文件数据如下,可以看到成功获取了所有小册数据:

在这里插入图片描述

模拟登录

我们通过 cookie 模拟登录环境,我们去谷歌应用商店下载一个插件 “Export cookie JSON file for Puppeteer”

这个 chrome 插件直接获取 cookie 信息

在这里插入图片描述

然后运行这个插件,下载CSDN cookie的json文件,代码很简单:

const puppeteer = require("puppeteer");
// 通过 插件 获取的 JSON化 cookie
const cookieObjects = require("./csdn.net.cookies.json");

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.setViewport({ width: 1400, height: 1080 });

  cookieObjects.forEach((cookie) => {
    page.setCookie(cookie);
  });
  await page.goto("https://www.csdn.net/");
})()

运行程序,自动打开浏览器,模拟登录成功:

在这里插入图片描述

总结

Puppeteer 是一个功能强大的 Node.js 库,可以用于控制 Chrome 或者 Chromium 浏览器,实现自动化测试、爬虫、网页截图等功能。以下是 Puppeteer 的一些优点和缺点:

优点

  • 功能强大:Puppeteer 支持大部分 Chrome DevTools 协议中的功能,包括页面截图、模拟用户操作、处理动态内容等。
  • 易于使用:Puppeteer 的 API 设计简单易用,上手容易。
  • 可扩展性强:Puppeteer 可以与其他 Node.js 库和工具集成,扩展其功能。
  • 社区活跃:Puppeteer 有一个活跃的社区,可以获取到大量的文档、教程和示例。

缺点

  • 依赖 Chrome 或者 Chromium 浏览器:Puppeteer 需要依赖 Chrome 或者 Chromium 浏览器才能运行,这会导致一定的资源占用。
  • 不支持其他浏览器:Puppeteer 只支持 Chrome 和 Chromium 浏览器,不支持其他浏览器。
  • 可能被网站检测到:由于 Puppeteer 可以模拟用户行为,因此有些网站可能会检测到 Puppeteer 的使用并阻止其访问。

总的来说,Puppeteer 是一个非常有用的工具,可以帮助开发人员和测试人员提高工作效率。

如果需要进行自动化测试、爬虫或者网页截图等操作,Puppeteer 是一个值得尝试的工具。

未来发展

随着互联网技术的发展和应用场景的不断扩大,爬虫技术将会得到更广泛的应用和更深入的研究。

以下是爬虫技术未来的一些发展方向:

  • 智能化:随着人工智能技术的发展,我们可以将人工智能技术应用到爬虫技术中,使得爬虫可以自主学习和适应不同的网站结构。
  • 安全性:随着网站安全性的提升,一些网站可能会采取更加复杂和严格的反爬虫机制。因此,未来的爬虫技术需要更加注重安全性和隐私保护。
  • 大数据:随着大数据技术的发展,我们可以将爬虫技术与大数据技术相结合,用于数据分析和挖掘。
  • 分布式:随着云计算技术的发展,分布式爬虫将会成为未来的趋势,可以更加高效地抓取大量数据。

总之,爬虫技术在未来将会得到更广泛和深入的应用,并且将会不断地发展和完善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/838219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

webpack基础知识六:说说webpack的热更新是如何做到的?原理是什么?

一、是什么 HMR全称 Hot Module Replacement,可以理解为模块热替换,指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个应用 例如,我们在应用运行过程中修改了某个模块,通过自动刷新会导致…

(三)Node.js - 模块化

1. Node.js中的模块化 Node.js中根据模块来源不同,将模块分为了3大类,分别是: 内置模块:内置模块由Node.js官方提供的,例如fs、path、http等自定义模块:用户创建的每个.js文件,都是自定义模块…

Godot 4 练习 - 制作粒子

演示项目dodge_the_creeps中,有一个Trail,具体运行效果 想要看看咋实现的,看完也不清晰,感觉是要设置某些关键的属性 ChatGPT说:以下是一些重要的属性: texture:用于渲染粒子的纹理。您可以使用…

windows docker部署

windows docker部署 使用 Docker Desktop for Windows安装Docker问题:Update the WSL kernel by running "wsl --update" or follow instructions at https://docs.microsoft.com/windows/wsl/wsl2-kernel.问题描述:解决办法,升级W…

【小沐学前端】GitBook制作在线电子书、技术文档(gitbook + Markdown + node)

文章目录 1、简介1.1 工具简介1.2 使用费用 2、安装2.1 安装node2.2 安装gitbook 3、测试3.1 编辑文档3.2 编译工程3.3 预览工程 结语 1、简介 官网地址: https://www.gitbook.com/1.1 工具简介 什么是 GitBook? GitBook 是一个现代文档平台&#xff…

微信小程序真机防盗链referer问题处理

公司使用百度云存储一些资源,然后现在要做防盗链,在CDN加入Referer白名单后发现PC是正常的,微信小程序无法正常访问资源了。然后是各种查啊,然后发现是微信小程序不支持Referer的修改,且在小程序开发工具是Referer是固…

WIN大恒工业相机SDK开发

大恒工业相机SDK开发概览 一、开发环境搭建1、C# 环境配置(VS2019)2、C 环境配置(VS2019)3、python 环境配置(Pycharm) 二、相机二次开发流程三、相机相机属性参数配置四、图像采集单帧采集回调采集 注意事…

基于边缘无线协同感知的低功耗物联网LPIOT技术:赋能智慧园区方案以及数字工厂领域

回到2000年左右,物联网的底层技术支撑还是“ZigBee”,虽然当时ZigBee的终端功耗指标其实也并不庞大,但是,“拓扑复杂导致工程实施难度大”、“网络规模小导致的整体效率低下”都成为限制其发展的主要因素。 LPWAN,新一…

自学(黑客)技术,入门到入狱!

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟入…

【数据结构篇】手写双向链表、单向链表(超详细)

文章目录 链表1、基本介绍2、单向链表2.1 带头节点的单向链表测试类:链表实现类: 2.2 不带头节点的单向链表2.3 练习测试类:链表实现类: 3、双向链表测试类:双向链表实现类: 4、单向环形链表**测试类**&…

初级web前端开发工程师的岗位职责描述(合集)

初级web前端开发工程师的岗位职责描述1 职责: 1. 根据功能需求设计编写页面原型; 2. 前后端联调保证功能流畅; 3. 提高页面易用性、美观提出合理建议。 4、与后台工程师配合开发联调并交付产品; 5、持续优化前端页面体验和访问速度,保证页面精美高效…

命令行快捷键Mac Iterm2

原文:Jump forwards, backwards and delete a word in iTerm2 on Mac OS iTerm2并不允许你使用 ⌥← 或 ⌥→ 来跳过单词。 你也不能使用 ⌥backspace 来删除整个单词。 下面是在Mac OS上如何配置iTerm2以便能做到这一点的方法。 退格键 首先,你需要将你的左侧 ⌥…

kubernetes基于helm部署gitlab

kubernetes基于helm部署gitlab 这篇博文介绍如何在 Kubernetes 中使用helm部署 GitLab。 先决条件 已运行的 Kubernetes 集群负载均衡器,为ingress-nginx控制器提供EXTERNAL-IP,本示例使用metallb默认存储类,为gitlab pods提供持久化存储&…

python#django数据库一对一/一对多/多对多

一对一OneToOneField 用户和用户信息 搭建 # 一对一 class TestUser(models.Model): usernamemodels.CharField(max_length32) password models.CharField(max_length32) class TestInfo(models.Model): mick_namemodels.CharField(max_length32) usermode…

蓝桥杯上岸必刷!!! (进制、数位专题)

蓝桥杯上岸必刷!!!(进制、数位专题) 距离蓝桥杯省赛倒数最后1天 ❗️ ❗️ ❗️ 还没背熟模板的伙伴们背起来 💪 💪 💪 大家好 我是寸铁💪 真题千千万万遍,蓝桥省一自然现&#…

“科创中国”青百会轮值主席吴甜:以大语言模型为代表的AI将引发产业变革

8月1日,“科创中国”青年百人会(后文简称青百会)联合百度举办“青创汇”高端对话,围绕人工智能技术创新与产业发展交流研讨,同时正式成立“科创中国”青年百人会女性工作委员会。该委员会将鼓励更多女性投身科技创新事…

easyExcel如何实现自定义标题,前两个(多个)标题占满一行,最后标题有规律

实现效果 实现方式 1,标题: Data public class ExportPurchaseSortingCustDto {/** 序号 */ExcelProperty(value {"${bigHead}","${dateHead}","序号"},index 0)ApiModelProperty(value "序号")private int…

RAFT:Recurrent All-Pairs Field Transforms for Optical Flow

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Abstract1 Introduction2 Related WorkOptical Flow as Energy Minimization 3 Approach3.1 Feature Extraction3.2 Computing Visual Similarity Abstract 光流学…

RISC-V基础之浮点指令(包含实例)

RISC-V体系结构定义了可选的浮点扩展,分别称为RVF、RVD和RVQ,用于操作单精度、双精度和四倍精度的浮点数。RVF/D/Q定义了32个浮点寄存器,f0到f31,它们的宽度分别为32位、64位或128位。当一个处理器实现了多个浮点扩展时&#xff0…

企业人力资源管理系统servlet+jsp人事招聘培训薪资java源代码mysql

本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 企业人力资源管理系统servletjsp 系统1权限&#xff…