lucas 发表于 2022-8-9 18:06:21

获取iframe中元素,提示跨域访问出错

站内王一之大佬写的解决跨域问题的两个方法,都看了,不知道具体怎么才能解决在iframe的页面上执行脚本,并返回页码数,目前的想法是设置全局变量n,在iframe界面运行脚本,改变变量n
[基本油猴编程的一些常见误区](https://bbs.tampermonkey.net.cn/thread-835-1-1.html)
stackOverflow上有个相关回答,采用postMessage()进行跨域通信,实在搞不动怎么用到我这个脚本
(https://stackoverflow.com/questions/25098021/securityerror-blocked-a-frame-with-origin-from-accessing-a-cross-origin-frame)

针对全国招投标公告打印不全,写了一个脚本,PDF的页数的ID = ‘numPages’,用document.getElementById('numPages')定位或者document.querySelector('#numpages'),元素都获取不到,提示Blocked a frame with origin from accessing a cross-origin frame,
目前采用的是弹窗输入,解决页数的问题。但是发现很多页面都是有iframe,所以想搞清楚到底要怎样才能自由的获取iframe中的元素

下面是需要脚本运行的示例界面
`https://ctbpsp.com/#/bulletinDetail?uuid=0f54093b-3869-4611-b2b5-71fd465e2670&inpvalue=2022%E5%B9%B4%E6%B9%96%E5%8D%97%E4%B8%AD%E7%83%9F%E7%A7%91%E7%A0%94%E8%AE%BE%E5%A4%87&dataSource=1`

```// ==UserScript==
// @name      调整打印文件
// @namespace   print adjust
// @match       https://ctbpsp.com/#/bulletinDetail?uuid=*
// @match       https://bulletin.cebpubservice.com/qualifyBulletin/*
// @match       https://bulletin.cebpubservice.com/biddingBulletin/*
// @match       https://bulletin.cebpubservice.com/candidateBulletin/*
// @match       https://bulletin.cebpubservice.com/resultBulletin/*
// @run-at      document-end
// @grant       unsafeWindow
// @version   1.2.2
// @author      我爱小熊啊
// @description cebpubservice.com 文件加载时间较长,遇到未加载完成的,页码输入界面点击取消,文件加载完后,记住页码,刷新输入页码,等待加载完成
// @description 将 ctbpsp.com 与 cebpubservice.com 的等待时间做区分
// @description 自动隐藏 cebpubservice.com 上的二维码广告
// @description 增加对 cebpubservice.com的支持
// @description 中国招标投标公共服务平台公告文件打印调整
// @description 2022/8/3 08:33:33
// @license   MIT
// ==/UserScript==

// 设置延时定时器,避免网页未加载完,弹出页码输入框
setTimeout(function(){
var weburl = window.location.href;
var n = 1;
// ctbpsp.com 网页打印
if(weburl.indexOf('ctbpsp.com')!=-1){
    // var windowAttribute = $('iframe');??? 引入 jQuery 库也不生效
    var windowAttribute = document.getElementsByTagName('iframe');
    n = prompt('请输入文件页码','');
   if(n){    // 如果输入框点击取消,则 prompt 返回的是 null,则取消更改,否则网页会出错
   windowAttribute.width = 900;
   windowAttribute.height = 1150 * n;
   // alert("Hello");
   }
}

// cebpubservice.com 网页打印
if(weburl.indexOf('cebpubservice.com')!=-1){
    setTimeout(function(){
      var p = document.getElementsByClassName('pdf_wraper');
      var b = document.getElementsByClassName('PublicAddress');
      b.style.display = 'none';
      // var n = prompt('请输入文件页码','');
      n = prompt('请输入文件页码','');
      if(n){
      p.style.width = '900px';
      var h = n * 1150;
      var gaodu = h + 'px';
      p.style.height = gaodu;
      }
    }, 6000);
}
}, 3000);
```

cxxjackie 发表于 2022-8-9 18:06:22

iframe内部可以理解为另一个网页,如果网页跨域,那无论如何也是不能直接操作其中的元素的。跨域页面可以通过postMessage互相发送数据,根据数据在对应环境中执行对应操作,这需要双方的配合。比如A可以给B发送一个指令showMessage,B接收到以后展示相应元素,但A不能直接操作B内的元素。全局变量是没用的,代码虽然看起来在同一个脚本,但实际运行在不同的环境中。下面是实现你需求的一个示例:
// ==UserScript==
// @name         跨域交互
// @description...
// @namespace    ...
// @author       ...
// @version      1.0
// @match      https://ctbpsp.com/*
// @match      https://custominfo.cebpubservice.com/*
// @grant      none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';
    // 主页面
    if (location.hostname === 'ctbpsp.com') {
      // 监听message事件,取得页码数后调整iframe大小
      window.addEventListener('message', e => {
            if ('numPages' in e.data) {
                const iframe = document.querySelector('.pdf-viewer');
                iframe.width = 900;
                iframe.height = 1150 * e.data.numPages;
            }
      });
    }
    // iframe页面
    if (location.hostname === 'custominfo.cebpubservice.com') {
      // iframe内有一个全局对象PDFViewerApplication,可对其进行劫持来判断pdf加载完毕,也可以用其他方法。
      const _load = window.PDFViewerApplication.load;
      window.PDFViewerApplication.load = function(pdfDocument) {
            // 获取页码数,发送给主页面
            window.top.postMessage({
                numPages: pdfDocument._pdfInfo.numPages
            }, 'https://ctbpsp.com');
            return _load.call(this, pdfDocument);
      };
    }
})();

lucas 发表于 2022-8-9 18:17:37

如有哪位大佬能帮我解决这个问题,悬赏20元人民币,QQ:1664307236

李恒道 发表于 2022-8-9 19:43:05

iframe子对父通信使用postmessage思路是对的
可以参考
https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=2267

李恒道 发表于 2022-8-9 19:44:34

还有一个思路就是使用
GM_addValueChangeListener
GM_removeValueChangeListener
GM_setValue
三个api组合

朱焱伟 发表于 2022-8-9 19:50:21

这还是一个关于iframe的异步获取元素的问题,我之前谈过waitForKeyElements这个库遇到的跨域问题,你和我遇到过的问题很像。
cxxjackie专门写了一个库ElementGetter来解决此类问题,我估计cxxjackie大佬看到后会来推广他的ElementGetter。
我先给出使用waitForKeyElements的方法,抛砖引玉。

1.头部没有匹配iframe的网址,需要匹配:
```js
// @match       https://custominfo.cebpubservice.com/*
// @require      https://z.chaoxing.com/js/jquery-3.5.0.min.js
// @require      https://greasyfork.org/scripts/383527-wait-for-key-elements/code/Wait_for_key_elements.js?version=701631
// @grant      none
```
2.使用waitForKeyElements获取numPages的代码:
```js
waitForKeyElements ("span#numPages.toolbarLabel",(jNode)=>{
    console.log(jNode); //输出 <span id=​"numPages" class=​"toolbarLabel">​/ 4​</span>​
    console.log('fuck you')
},'iframe.pdf-viewer');
```
3.相关帖子
- [异步获取元素的脚本库 ElementGetter (更新至1.1.1)](https://bbs.tampermonkey.net.cn/thread-2726-1-1.html)
- [异步获取元素的脚本库waitForKeyElements](https://bbs.tampermonkey.net.cn/thread-2729-1-1.html)

cxxjackie 发表于 2022-8-9 21:37:54

朱焱伟 发表于 2022-8-9 19:50
这还是一个关于iframe的异步获取元素的问题,我之前谈过waitForKeyElements这个库遇到的跨域问题,你和 ...

这其实不是获取元素的问题,跨iframe≠跨域。waitForKeyElements只能获取同源iframe内的元素,ElementGetter也不能解决跨域。

朱焱伟 发表于 2022-8-9 22:00:58

本帖最后由 朱焱伟 于 2022-8-9 22:12 编辑

cxxjackie 发表于 2022-8-9 21:37
这其实不是获取元素的问题,跨iframe≠跨域。waitForKeyElements只能获取同源iframe内的元素,ElementGet ...
看到最新评论的代码了,老铁666

lucas 发表于 2022-8-10 08:04:31

cxxjackie 发表于 2022-8-9 21:46
iframe内部可以理解为另一个网页,如果网页跨域,那无论如何也是不能直接操作其中的元素的。跨域页面可以通 ...

感谢大佬指点,作为一个小白,太感谢了,确实解决了我的困扰,之前悬赏的20元,看怎么给方便点,请大佬喝杯咖啡

lucas 发表于 2022-8-10 08:05:52

李恒道 发表于 2022-8-9 19:44
还有一个思路就是使用
GM_addValueChangeListener
GM_removeValueChangeListener


感谢大佬回复,之前就是在论坛看你的帖子,写的第一个脚本,虽然很简单,但是对我而言,解决了工作中的一个问题,也是很有成就感了
页: [1] 2
查看完整版本: 获取iframe中元素,提示跨域访问出错