伪指纹浏览器开发的那些事

什么是伪指纹浏览器开发

就是通过开源的chromium浏览器进行二次简单的封装不涉及到重新编译chromium,配合puppeteer进行轻微的指纹修改开发

一、如何操作

本次操作客户端以前端擅长的electron来举例子,至于electron是什么,打开文心一言看看...

第一步下载chromium到本地客户端

登录官网,看到如下界面

可以发现箭头处指定是浏览器对应的版本buildId和系统,这里可以直接手动点击下载到本地,也可以通过@puppeteer/browsers这个库使用js代码去下载。这里说说如何使用它下载

javascript 复制代码
const { app } = require('electron')
const browserApi = require('@puppeteer/browsers')
const axios = require('axios')

// browser缓存路径,避免和electron一起打包占用安装包体积和打包时间
const cacheDir = `${app.getPath('cache')}/myBrowser`

browserApi.install({
  cacheDir, // 自己想要下载的路径,用来给puppeteer去调用
  browser: browserApi.Browser.CHROMIUM,
  // buildId: '1247373',
  // baseUrl: 'https://commondatastorage.googleapis.com/chromium-browser-snapshots'
})

耐心的小伙伴肯定发现了这里buildId版本号和baseUrl下载url我打了注释,是因为@puppeteer/browsers默认下载的chromium版本比较旧,那么我们怎么获取这个最新版本buildId和baseUrl呢,还是官网那个界面打开控制台,可以看到如下请求链接

然后看到请求结果 这就是最新的buildId了,然后封装成函数调用

javascript 复制代码
// 获取最新的chromium构建ID
function getLastBuildId(platform) {
  return axios
    .get(
      `https://download-chromium.appspot.com/rev/${browserApi.BrowserPlatform.MAC}?type=snapshots`
    )
    .then((res) => res.data.content)
}

baseUrl可以在界面点击下载时候,看到控制台有一个请求,那就是baseUrl了

下载好后,可以去我们定义的下载保存地址,通过终端去打开就可以看到了

二、第二步启动chromium

使用puppeteer-core这个库,启动我们下好的chromium

javascript 复制代码
const puppeteer = require('puppeteer-core')
const browserApi = require('@puppeteer/browsers')

// browser缓存路径
const cacheDir = `${app.getPath('cache')}/myBrowser`

// 获取安装的浏览器路径
function getBrowserPath() {
  return browserApi
    .getInstalledBrowsers({ cacheDir })
    .then((list) => list[0]?.executablePath)
}

// 浏览器生成
const createBrowser = async (proxyServer, userAgent) => {
  const browser = await puppeteer.launch({
    args: [
      `--proxy-server=${proxyServer}`,
      `--user-agent="${userAgent}"`,
      '--no-first-run',
      '--no-zygote',
      '--disable-extensions',
      '--disable-infobars',
      '--disable-automation',
      '--no-default-browser-check',
      '--disable-device-orientation',
      '--disable-metrics-reporting',
      '--disable-logging'
    ],
    headless: false,
    defaultViewport: null,
    ignoreHTTPSErrors: true,
    ignoreDefaultArgs: [
      '--enable-infobars',
      '--enable-features=NetworkService,NetworkServiceInProcess',
      '--enable-automation',
      'about:blank'
    ],
    executablePath: await getBrowserPath()
  })

  return browser
}

通过puppeteer.launch启动一个浏览器,至于启动参数这里我只说指纹相关的两个参数--proxy-server--user-agent,其他AI一下。

--proxy-server代理服务,浏览器访问的出口IP,即你用自己启动的浏览器访问google时候,那边服务端获取的ip就是你的代理ip,测试时候可以自己在另外一台机器上装个Squid测试。--user-agent即浏览器的window.navigator.userAgent,简单指纹一般都是依赖于它生成

三、开发过程中用到的功能点

看完puppeteer官网,我们知道操作chromium依赖于一套协议chromedevtools.github.io/devtools-pr...

3.1 更换dock图标

比如多开浏览器,我如何更换chromium的桌面dock图标,去标识这是我启动的第几个浏览器。我们可以使用Browser.setDockTile去操作浏览器更换dock图标

csharp 复制代码
const pages = await browser.pages()
const page = pages[0]
const session = await pages[0].target().createCDPSession()
await session.send('Browser.setDockTile', {
    image: new Buffer.from(fs.readFileSync(file)).toString('base64')
})

效果如下:

更多的协议操作需要自己摸索了,提示下,AI搜索chrome cdp协议

3.2 增加默认书签

这里我没找到协议,直接通过类似爬虫的方式,先进入标签管理页面,直接操作js新增,也算是一个技巧性的骚操作

csharp 复制代码
await page.goto('chrome://bookmarks/') // 进入标签管理页面
await page.evaluate(async () => {
  // 类似在控制台直接操作一样,下面的代码控制台一样可以达到效果
  const defaultBookmarks = [
    {
      title: "文心一言",
      url: "https://yiyan.baidu.com/",
    },
    {
      title: "掘金",
      url: "https://juejin.cn/",
    },
  ];

  defaultBookmarks.forEach((item) => {
    chrome.bookmarks.create({
      parentId: "1",
      ...item,
    });
  });
});
await page.goto('自己的本来要跳的首页')

3.3 如何使用已经打开的浏览器

c 复制代码
const browserWSEndpoint = browser.wsEndpoint() // 获取本次打开的浏览器链接,留作下一次使用
// 保存下来, 比如直接存在一个变量map中,给它定义一个唯一的browserId,下一次好直接获取
browserMap.set(browserId, browserWSEndpoint)

...
// 再次打开新页面,要用到上一次打开的浏览器
const browser = puppeteer.connect({
   ...launchOptions, // 和自己首次打开浏览器的配置一样
   browserWSEndpoint: browserMap.set(browserId)
})

这样就可以使用之前打开的浏览器打开网页了

3.4 如何把浏览器的信息显示在网页上

比如代理、userAgent、地区、浏览器名称等信息,先写个页面,然后轮询从localStorage直到获取信息为止。

javascript 复制代码
// 浏览器代理信息页
await page.goto('浏览器信息页')
// 设置localStorage
await page.evaluate(
  (values) => {
    window.localStorage.setItem('browserInfo', values)
  },
  JSON.stringify(browserData)
)

3.5 校验代理

一般的代理服务为了不让别人也能用都会加上账密校验,所以我们还需要在启动后,调用方法去校验

php 复制代码
// 校验proxy
if (proxyData.proxyServer) {
  await page.authenticate({
    username: proxyData.proxyUser,
    password: proxyData.proxyPwd
  })
}

page在打开页面后,并不会在页面中马上能获取到这里注入的browserInfo,可以通过轮询方式去扫描localStorage中是否存在我们注入的变量,这里举个react中的例子,在页面ready后去轮询处理

scss 复制代码
useEffect(() => {
  let loopId = null
  const clearLoop = () => {
    loopId && clearTimeout(loopId)
  }

  // 轮询直到获取browserInfo
  const loop = () => {
    loopId = setTimeout(() => {
      const localData = window.localStorage.getItem('browserInfo')
      if (localData) {
        Promise.resolve()
          .then(() => {
            setInfo(JSON.parse(localData))
          })
          .catch(() => {
            message.error('获取浏览器信息失败')
          })
      } else {
        loop()
      }
    }, 1500)
  }

  loop()

  return () => {
    clearLoop()
  }
})

四、遇到了哪一些问题

4.1 mac下关闭浏览器关不掉

当我们点击左上角关闭浏览器按钮或者是关闭所有页面时候,底部的dock中依旧存在着,我们不希望像mac其他软件一样保留在dock中,不然下一次打开浏览器时候,会出现相同标识的浏览器,可以这么解决

dart 复制代码
// 每次页面关闭时候,查看浏览器是不是还有页面了,没有就关闭
browser.on('targetdestroyed', async () => {
  const pages = await browser.pages()
  if (!pages.length) {
    await browser.close()
  }
})

4.2 当我们之间关闭电脑屏幕时候,比如盖上电脑,再次打开时候,关闭不了浏览器

打上log,可以发现熄屏时候,会触发puppeteer定义的browser的disconnected事件,但是再次打开电脑时候浏览器是可以正常使用的,也就是说,puppeteer和我们打开的chromium断连了,所以我们需要在disconnected事件里再此尝试链接下chromium,如果不行才认为是浏览器被关闭了

typescript 复制代码
browser.on('disconnected', () => {
  const cacheData = browserMap.get(browserId)
  puppeteer
    .connect({
      ...launchOptions,
      browserWSEndpoint: cacheData.browserWSEndpoint
    })
    .then((newBrowser) => {
      browser = newBrowser
      log.info(
        'browser disconnected but browser is exist',
      )
      initEvent()
    })
    .catch((err) => {
      log.info(
        'browser disconnected success',
      )
    })
})

结语

puppeteer很强大,chromium也强大,就是那个官网文档啊,写的真是让人...,所以多问问AI吧

相关推荐
续亮~21 分钟前
6、Redis系统-数据结构-05-整数
java·前端·数据结构·redis·算法
顶顶年华正版软件官方2 小时前
剪辑抽帧技巧有哪些 剪辑抽帧怎么做视频 剪辑抽帧补帧怎么操作 剪辑抽帧有什么用 视频剪辑哪个软件好用在哪里学
前端·音视频·视频·会声会影·视频剪辑软件·视频剪辑教程·剪辑抽帧技巧
托尼沙滩裤3 小时前
【js面试题】js的数据结构
前端·javascript·数据结构
不熬夜的臭宝3 小时前
每天10个vue面试题(一)
前端·vue.js·面试
不如喫茶去3 小时前
VUE自定义新增、复制、删除dom元素
前端·javascript·vue.js
长而不宰3 小时前
vue3+electron项目搭建,遇到的坑
前端·vue.js·electron
阿垚啊4 小时前
vue事件参数
前端·javascript·vue.js
过去式的美好5 小时前
vue前端通过sessionStorage缓存字典
前端·vue.js·缓存
Simaoya5 小时前
vue判断元素滚动到底部后加载更多
前端·javascript·vue.js
头顶一只喵喵5 小时前
Vue基础知识:Vue3.3出现的defineOptions,如何使用,解决了什么问题?
前端·javascript·vue.js·vue3