当页面上的元素更改时刷新 [英] Refresh when an element changes on page

查看:53
本文介绍了当页面上的元素更改时刷新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在网站上废弃一个元素并使用 Puppeteer (1) 在本地主机上显示它.但是当这个元素发生变化时,我想刷新数据而不用 Puppeteer 打开一个新的浏览器/页面,并且只有当元素发生变化时 (2).

在我的示例中,我使用 www.timeanddate.com 并且元素是时间(小时和分钟).目前,只有第一部分有效.我没有第二个的解决方案.

请在下面找到我的代码.

app.js

var app = require('express')();var server = require('http').createServer(app);var io = require('socket.io').listen(server);var puppeteer = require('puppeteer');app.get('/', function(req, res) {res.render('main.ejs');});服务器.听(8080);让刮=异步()=>{var browser = await puppeteer.launch({headless: true});var page = await browser.newPage();await page.goto('https://www.timeanddate.com/worldclock/personal.html');等待页面.waitFor(300);//await page.click('#mpo > div > div > div > div.modal-body > div.form-submit-row > button.submit.round.modal-privacy__btn');var 结果 = 等待 page.evaluate(() => {返回 document.getElementsByClassName('c-city__hrMin')[0].innerText;});返回结果;};io.sockets.on('connection', function (socket) {scrape().then((value) => {//页面加载需要时间,几秒钟.控制台日志(值);socket.emit('刷新', 值);});});

main.ejs

<头><meta charset="utf-8"/><title>现在几点了?</title><风格>一个{文本装饰:无;颜色:黑色;}</风格><身体><h1>欢迎您!</h1><div id="time">加载中</div><script src="http://code.jquery.com/jquery-1.10.1.min.js"></script><script src="/socket.io/socket.io.js"></script><脚本>var socket = io.connect('http://localhost:8080');socket.on('刷新', 函数(值){$('#time').html(value);});</html>

我尝试过 Fiverr,但体验很糟糕.我希望这里会更好:)

谢谢你帮助我.

解决方案

您希望在数据更改时发出事件.有多种方法可以做到这一点,例如,

  • 尝试按时间间隔获取新数据
  • 在浏览器中查找更改并发出

更新代码可读性

我将讨论它们.但首先,让我们拆分代码以获得更好的可用性.这完全是可选的,但你应该这样做.

/*** 刮板* 使用这个代替刮变量*/让浏览器,页面;常量刮刀 = {异步打开(){browser = await puppeteer.launch({ headless: true });page = 等待 browser.newPage();const url = "https://www.timeanddate.com/worldclock/personal.html";等待 page.goto(url);等待页面.waitFor(300);},异步获取时间(){返回 page.evaluate(() => {返回 document.querySelector(".c-city__digitalClock").innerText;//时间以秒为单位 5:43:22am});}};

如果需要,我们可以稍后向该对象添加其他方法.这不是最好的格式,但是这将有助于我们此时更好地理解代码.

方法一、设置间隔

让我们修改连接,我们只需要打开页面一次,每隔一段时间轮询一次新数据.

/*** 套接字连接监视器*/io.sockets.on(连接",异步函数(套接字){//打开页面一次等待scraper.open();//开始间隔循环setInterval(async() => {//获取每一秒的时间const time = await scraper.getTime();//发出更新的时间socket.emit("刷新", 时间);}, 1000);//我们想要多少毫秒});

方法 2. 向浏览器本身添加事件.

这是高级且复杂得多,但非常准确.

您可以将其添加到 scraper 对象中.

//<-- 传递套接字以便它可以使用它异步运行事件(套接字){//在 puppeteer 上创建一个 Shadow 事件跟踪器等待 page.exposeFunction("emitter", (...data) => {socket.emit(...数据)});等待 page.evaluate(函数observeDom(){//暴露将要观察的观察者//更多详情 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver//选择目标节点var target = document.querySelector(".c-city__digitalClock");//创建一个观察者实例var 观察者 = 新的 MutationObserver(function(mutations) {//做一些改变发射器(刷新",target.innerText);//<-- 每当有变化时触发事件});//观察者的配置:var config = { childList: true, subtree: true };//传入目标节点,以及观察者选项观察者.观察(目标,配置);});}

然后你的连接看起来像,

io.sockets.on("connection", async function(socket) {等待scraper.open();等待 scraper.runEvents(socket);//<-- 传递套接字});

它是如何工作的,

  • 当套接字打开时,
  • 我们打开浏览器和页面
  • 我们负责举办活动.
    • 我们设置了一个自定义事件,该事件将使用它获取的任何数据运行 socket.emit
    • 我们在 page 上公开自定义事件.
    • 我们从那时开始观察 dom 元素,
    • 只要有一点变化,我们就会触发我们制作的自定义事件

这是两者之间的视觉差异:

(我使用了 500 毫秒的间隔,它是每秒 60 帧,所以动画没有捕捉到所有内容,但它就在那里,链接到

区别

setInterval 和事件的区别在于,setInterval 会在一定时间后进行检查,而观察者会持续观察变化并在发生变化时触发.

选择哪个:

  • 如果您喜欢简单,请使用 setInterval 版本.
  • 如果您需要精度,请使用 observer 版本.

I try to scrap an element on a website and display it on localhost with Puppeteer (1). But when this element changes, I would like to refresh data without opening a new browser/page with Puppeteer and only when element changes (2).

For my example, I use www.timeanddate.com and the element is time (hours and minutes). For moment, only first part works. I don't have solution for second one.

Please find below, my code.

app.js

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var puppeteer = require('puppeteer');

app.get('/', function(req, res) { 
    res.render('main.ejs');
});

server.listen(8080);

let scrape = async () => {
    var browser = await puppeteer.launch({headless: true});
    var page = await browser.newPage();
    await page.goto('https://www.timeanddate.com/worldclock/personal.html');
    await page.waitFor(300);
    //await page.click('#mpo > div > div > div > div.modal-body > div.form-submit-row > button.submit.round.modal-privacy__btn');

    var result = await page.evaluate(() => {
        return document.getElementsByClassName('c-city__hrMin')[0].innerText;
    });

    return result;
};

io.sockets.on('connection', function (socket) {
    scrape().then((value) => { // it tooks time, a few seconds while page is loading.
        console.log(value);
        socket.emit('refresh', value);
    });
});

main.ejs

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>What time is it?</title>
        <style>
            a {text-decoration: none; color: black;}
        </style>
    </head>

    <body>
        <h1>Welcome !</h1>

        <div id="time">loading</div>

        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
            var socket = io.connect('http://localhost:8080');

            socket.on('refresh', function (value) {
                $('#time').html(value);
            });         
        </script>
    </body>
</html>

I try Fiverr but awful experience. I hope it will better here :)

Thank you for helping me.

解决方案

You want to emit event when the data changes. There are multiple ways to do that, such as,

  • Try to get new data on interval
  • Look for the change and emit from within browser

Update Code Readability

I will discuss both of them. But first, lets split the code for a better usability. It's completely optional but you should do it.

/**
 * Scraper
 * Use this instead of scrape variable
 */
let browser, page;
const scraper = {
  async open() {
    browser = await puppeteer.launch({ headless: true });
    page = await browser.newPage();
    const url = "https://www.timeanddate.com/worldclock/personal.html";
    await page.goto(url);
    await page.waitFor(300);
  },
  async getTime() {
    return page.evaluate(() => {
      return document.querySelector(".c-city__digitalClock").innerText; // time with seconds 5:43:22am
    });
  }
};

We can add other methods to this object later if we need. This is not the best format, but this will help us understand the code better at this point.

Method 1. Set Interval

Let's modify the connection, we just need to open the page once and poll new data on some interval.

/**
 * Socket Connection Monitor
 */
io.sockets.on("connection", async function(socket) {
  // open the page once
  await scraper.open();

  // start the interval loop
  setInterval(async () => {

    // get the time every second
    const time = await scraper.getTime();

    // emit the updated time
    socket.emit("refresh", time);
  }, 1000); // how many millisecond we want
});

Method 2. Add events to the browser itself.

This is advanced and much more complex, however very accurate.

You can add this inside scraper object.

// <-- Pass the socket so it can use it
async runEvents(socket) {
    // Create a Shadow event tracker on puppeteer
    await page.exposeFunction("emitter", (...data) => {
      socket.emit(...data)
    });
    await page.evaluate(function observeDom() {
      // expose the observer which will watch
      //More Details https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
      // select the target node
      var target = document.querySelector(".c-city__digitalClock");
      // create an observer instance
      var observer = new MutationObserver(function(mutations) {
        // Do something on change
        emitter("refresh", target.innerText); // <-- trigger the event whenever there is a change
      });

      // configuration of the observer:
      var config = { childList: true, subtree: true };
      // pass in the target node, as well as the observer options
      observer.observe(target, config);
    });
  }

And then your connection will look like,

io.sockets.on("connection", async function(socket) {
  await scraper.open();
  await scraper.runEvents(socket); // <-- Pass the socket
});

How it works,

  • When the socket is open,
  • We open the browser and page
  • We run the events.
    • We setup a custom event which will run socket.emit with whatever data it gets
    • We expose the custom event on the page.
    • We observe the dom element from then,
    • Whenever there is a little change, we trigger the custom event we made

Here is a visual difference between these two:

(I used 500ms interval and it's 60 frames per second, so the animation is not catching everything, but it's there, link to repo.)

Difference

The difference between setInterval and the event is, setInterval will check after certain amount of time, while the observer will continuously observe the changes and trigger whenever there is a change.

Which to choose:

  • If you like simplicity, go with setInterval version.
  • If you need precision, go with observer version.

这篇关于当页面上的元素更改时刷新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆