Puppeteer使用过程中的若干问题记录

在最近一些辅助工具的开发中,我选择了使用puppeteer来进行快速开发,puppeteer很好用很强大,使用过程中也总是问题不断,现就一些问题做一个记录,希望大家以后在使用puppeteer的过程中,遇到问题能快速找到应对的方案。

一、如何在puppeteer中使用代理

想要在puppeteer中设置代理我们可以通过两种方式实现:

1、修改启动参数

在启动参数args中传递--proxy-server,示例代码如下:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    args: [
      '--proxy-server=127.0.0.1:8080' // 将代理地址和端口号传递给 Chrome
    ]
  });
  const page = await browser.newPage();
  await page.goto('https://www.example.com');
  await browser.close();
})();

不带协议默认的是http代理,也可以使用socks代理,比如:--proxy-server=socks5://127.0.0.1:8080

2、使用 setRequestInterception 方法拦截请求并设置代理

使用setRequestInterception方法来拦截请求并设置代理。在这个方法中,我们可以检查每个请求的 URL,并使用request.continue方法继续请求,同时设置代理,示例代码如下:

await page.setRequestInterception(true);

page.on('request', request => {
  const proxyUrl = 'http://127.0.0.1:8080';
  const proxyUrlObj = new URL(proxyUrl);
  
  const requestOptions = {
    host: proxyUrlObj.hostname,
    port: proxyUrlObj.port,
    auth: 'username:password', // 如果需要的话,设置代理的用户名和密码
    headers: request.headers
  };
  
  request.continue({
    proxy: requestOptions // 设置代理
  });
});

如果我们需要在页面内多次切换代理使用上面的方式就会比较麻烦,所以更推荐大家使用puppeteer-page-proxy这个npm包,其实现原理就是我们上面提到的方式,使用方式如下:

const puppeteer = require('puppeteer')
const useProxy = require('puppeteer-page-proxy')

puppeteer.launch({
    headless: false
}).then(async browser => {
    console.log('start...');
    const page = await browser.newPage();

    await useProxy(page, 'http://账号:密码@115.156.133.84:21477');
    console.log('change proxy success, start to visit url');
    let resp = await page.goto('http://httpbin.org/ip');
    console.log(await resp.text())

    await useProxy(page, 'http://账号:密码@115.156.133.85:56214');
    console.log('change proxy success, start to visit url');
    resp = await page.goto('http://httpbin.org/ip');
    console.log(await resp.text())
});

二、如何处理输入框无法模拟输入文本的情况

正常情况下,我们可以直接通过page.type向页面中的某个节点发送内容,但是在部分网页中会发现通过这种方式无法发送内容,这个时候我们可以考虑先点击该节点,然后再发送内容,我们可以提取一个公用的文本输入方法:

async function input(page, selector, value) {
    await page.waitForSelector(selector, { timeout: 0 });
    await page.tap(selector);
    await page.type(selector, value, { delay: 100 });
}

三、如何通过显示标签来选中select中的option

我们在使用puppeteer做选择框模拟选择的时候,可以会遇到值和显示标签不一致的情况,比如:

<select id="select">
    <option value="1">One<option>
    <option value="2">Two<option>
    <option value="3">Three<option>
</select>

要实现模拟选择,只能在page.select方法中传入“1”,“2”,“3”这三个值才能被正确接受,如果我们直接传入“One”,“Two”,“Three”则是不行的,我们需要先将显示标签换算成对应的value,实现代码如下:

async function getOptionValueByText(page, selector, text) {
    return await page.evaluate((selector, text) => {
        function getOptionValueByText(text) {
            var select = document.querySelector(selector);
            var options = select.options;
            for (var i = 0; i < options.length; i++) {
                if (options[i].text == text) {
                    return String(options[i].value);
                }
            }
            return text;
        }
        return getOptionValueByText(text);
    }, selector, text);
}

使用方式如下:

const selectVal = await getOptionValueByText(page, '#select', 'Three');
await page.select('#select', selectVal);

四、如何为日期选择器赋值

当我第一次看到日期选择器的时候,我就觉得这玩意儿可能不太好搞,然后我问了chatgpt,得到的答案是这个东西比较繁琐,而且每个日期选择器的实现方式都不太一样,经过一番思考,我尝试使用最原始的方式,因为日期选择器必定对应了一个输入框,我们可以直接找到这个输入框,然后按照日期选择器要求的格式往里面发送内容就行了,代码如下:

await page.evaluate(dateStr => {
    document.querySelector('#date').value = dateStr;
}, `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`);

这里使用了dom的方式,你也可以选择使用page.type来实现。

五、如果为日期选择器获取未来几天的日期

严格来说这个问题并不是使用puppeteer特有的问题,我们在工作中也可能会遇到,获取未来几天后的日期这个需求看似简单实则还是有一些东西需要考虑的,因为每个月的天数可能不一致,比如2月有28天,如果当前日期刚好是2月28日,后一天的日期是3月1日,但是如果我们不经过仔细考虑可能程序算出来的就是2月29日,这里我们封装了一个简单的方法:

function getFutureDate(days, date) {
    const today = date ? new Date(date) : new Date();
    let futureDate = new Date(today.getTime() + (days * 24 * 60 * 60 * 1000));
    const daysInMonth = new Date(futureDate.getFullYear(), futureDate.getMonth() + 1, 0).getDate();
  
    if (futureDate.getDate() > daysInMonth) {
      futureDate.setDate(daysInMonth);
    }
  
    return futureDate;
}

写在最后

如果你也对各类插件、辅助工具感兴趣,欢迎加入本站技术交流群我们一起交流。如果你有什么想法想要实现,但是自己却不会,也可以私加我。

  • 支付宝二维码 支付宝
  • 微信二维码 微信

本文地址: /puppeteer-tips-2.html

版权声明: 本文为原创文章,版权归 逐梦个人博客 所有,欢迎分享本文,转载请保留出处!

相关文章