有趣的"getElementById()只需1个参数(给定2个)",有时它会发生.有人可以解释一下吗?

Tre*_* Mi 17 python com getelementbyid win32com

#-*- coding:utf-8 -*-
import win32com.client, pythoncom
import time

ie = win32com.client.DispatchEx('InternetExplorer.Application.1')
ie.Visible = 1
ie.Navigate('http://ieeexplore.ieee.org/xpl/periodicals.jsp')
time.sleep( 5 )

ie.Document.getElementById("browse_keyword").value ="Computer"
ie.Document.getElementsByTagName("input")[24].click()
Run Code Online (Sandbox Code Playgroud)
import win32com.client, pythoncom
import time

ie = win32com.client.DispatchEx('InternetExplorer.Application')
ie.Visible = 1
ie.Navigate('www.baidu.com')
time.sleep(5)

print 'browse_keword'
ie.Document.getElementById("kw").value ="Computer"
ie.Document.getElementById("su").click()
print 'Done!'
Run Code Online (Sandbox Code Playgroud)

当运行第一部分代码时,它将弹出:

ie.Document.getElementById("browse_keyword").value ="Computer"
TypeError: getElementById() takes exactly 1 argument (2 given)
Run Code Online (Sandbox Code Playgroud)

并且第二部分代码运行正常.使结果不同有什么区别?

Dun*_*can 5

这两种情况之间的区别与您指定的COM名称无关:InternetExplorer.Application或者InternetExplorer.Application.1导致完全相同的CLSID,它为您提供了一个IWebBrowser2接口.运行时行为的差异完全取决于您检索的URL.

这里的区别可能是工作的页面是HTML而另一个是XHTML; 或者可能只是失败页面中的错误会阻止DOM正确初始化.无论它看起来是IE9解析器的"功能".

请注意,如果启用兼容模式,则不会发生这种情况(在下面的第二行之后,我单击了地址栏中的兼容模式图标):

(Pdb) ie.Document.DocumentMode
9.0
(Pdb) ie.Document.getElementById("browse_keyword").value
*** TypeError: getElementById() takes exactly 1 argument (2 given)
(Pdb) ie.Document.documentMode
7.0
(Pdb) ie.Document.getElementById("browse_keyword").value
u''
Run Code Online (Sandbox Code Playgroud)

不幸的是我不知道如何从脚本切换兼容模式(该documentMode属性不可设置).也许别人呢?

我认为,错误的参数计数来自COM:Python传入参数,COM对象以误导性错误拒绝调用.


nym*_*ymk 5

作为一种方法COMObject,getElementByIdwin32com动态构建的.
在我的电脑上,如果url是http://ieeexplore.ieee.org/xpl/periodicals.jsp,它几乎等同于

def getElementById(self):
    return self._ApplyTypes_(3000795, 1, (12, 0), (), 'getElementById', None,)
Run Code Online (Sandbox Code Playgroud)

如果网址是www.baidu.com,则几乎相当于

def getElementById(self, v=pythoncom.Missing):
    ret = self._oleobj_.InvokeTypes(1088, LCID, 1, (9, 0), ((8, 1),),v
            )
    if ret is not None:
        ret = Dispatch(ret, 'getElementById', {3050F1FF-98B5-11CF-BB82-00AA00BDCE0B})
    return ret
Run Code Online (Sandbox Code Playgroud)

显然,如果您将参数传递给第一个代码,您将收到一个TypeError.但是如果你试图直接使用它,即调用ie.Document.getElementById(),你将不会收到a TypeError,而是a com_error.

为什么要win32com构建错误的代码?
让我们来看看ieie.Document.它们都是COMObjects,更确切地说,是win32com.client.CDispatch实例.CDispatch只是一个包装类.核心是属性_oleobj_,其类型是PyIDispatch.

>>> ie, ie.Document
(<COMObject InternetExplorer.Application>, <COMObject <unknown>>)
>>> ie.__class__, ie.Document.__class__
(<class win32com.client.CDispatch at 0x02CD00A0>,
 <class win32com.client.CDispatch at 0x02CD00A0>)
>>> oleobj = ie.Document._oleobj_
>>> oleobj
<PyIDispatch at 0x02B37800 with obj at 0x003287D4>
Run Code Online (Sandbox Code Playgroud)

要构建getElementById,win32com需要从中获取getElementById方法的类型信息_oleobj_.粗略地,win32com使用以下过程

typeinfo = oleobj.GetTypeInfo()
typecomp = typeinfo.GetTypeComp()
x, funcdesc = typecomp.Bind('getElementById', pythoncom.INVOKE_FUNC)
......
Run Code Online (Sandbox Code Playgroud)

funcdesc包含几乎所有导入信息,例如参数的数量和类型.
如果url是http://ieeexplore.ieee.org/xpl/periodicals.jsp,funcdesc.args则是(),而correc funcdesc.args应该是((8, 1, None),).

简而言之,win32com已经检索到了错误的类型信息,因此它构建了错误的方法.
我不确定是谁应该责备PyWin32或IE.但根据我的观察,我发现PyWin32的代码没有错.另一方面,以下脚本在Windows脚本宿主中完美运行.

var ie = new ActiveXObject("InternetExplorer.Application");
ie.Visible = 1;
ie.Navigate("http://ieeexplore.ieee.org/xpl/periodicals.jsp");
WScript.sleep(5000);
ie.Document.getElementById("browse_keyword").value = "Computer";
Run Code Online (Sandbox Code Playgroud)

Duncan已经指出IE的兼容模式可以防止这个问题.不幸的是,似乎无法从脚本启用兼容模式.
但我找到了一个技巧,可以帮助我们绕过这个问题.

首先,您需要访问一个好的网站,它会为我们提供一个HTML页面,并Document从中检索一个正确的对象.

ie = win32com.client.DispatchEx('InternetExplorer.Application')
ie.Visible = 1
ie.Navigate('http://www.haskell.org/arrows')
time.sleep(5)
document = ie.Document
Run Code Online (Sandbox Code Playgroud)

然后跳转到不起作用的页面

ie.Navigate('http://ieeexplore.ieee.org/xpl/periodicals.jsp')
time.sleep(5)
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过旧Document对象访问第二页的DOM .

document.getElementById('browse_keyword').value = "Computer"
Run Code Online (Sandbox Code Playgroud)

如果您使用新Document对象,您将TypeError再次获得.

>>> ie.Document.getElementById('browse_keyword')
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: getElementById() takes exactly 1 argument (2 given)
Run Code Online (Sandbox Code Playgroud)


lol*_*pop 0

在 Python 中调用实例的方法会自动将实例添加为第一个参数 - 这就是为什么您必须在方法中显式编写“self”参数。

例如,instance.method(args...)等于Class.method(instance, args...)

从我看来,程序员一定是忘记写 self 关键字,导致方法被破坏。尝试查看库代码内部。