Интересный "getElementById() принимает ровно 1 аргумент (2 данный)", иногда это происходит. Может кто-нибудь объяснить это?
#-*- 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()
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!'
При запуске кода первого раздела он будет всплывать:
ie.Document.getElementById("browse_keyword").value ="Computer"
TypeError: getElementById() takes exactly 1 argument (2 given)
И код второго раздела работает нормально. В чем разница между тем, что результат отличается?
Ответы
Ответ 1
Разница между этими двумя случаями не имеет никакого отношения к указанному 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''
К сожалению, я не знаю, как переключить режим совместимости с script (свойство documentMode
не может быть установлено). Может быть, кто-то другой?
Неверный подсчет аргументов, я думаю, исходит из COM: Python передает аргументы, а объект COM отклоняет вызов с ошибочной ошибкой.
Ответ 2
Как метод a COMObject
, getElementById
динамически построен win32com
.
На моем компьютере, если url http://ieeexplore.ieee.org/xpl/periodicals.jsp, он будет почти эквивалентен
def getElementById(self):
return self._ApplyTypes_(3000795, 1, (12, 0), (), 'getElementById', None,)
Если URL-адрес 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
Очевидно, что если вы передадите аргумент первому коду, вы получите TypeError
. Но если вы попытаетесь использовать его напрямую, а именно invoke ie.Document.getElementById()
, вы не получите TypeError
, но com_error
.
Почему win32com
построил неправильный код?
Посмотрим на ie
и ie.Document
. Они как COMObject
s, точнее, 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>
Чтобы построить getElementById
, win32com
необходимо получить информацию о типе для метода getElementById
из _oleobj_
. Пример: win32com
использует следующую процедуру
typeinfo = oleobj.GetTypeInfo()
typecomp = typeinfo.GetTypeComp()
x, funcdesc = typecomp.Bind('getElementById', pythoncom.INVOKE_FUNC)
......
funcdesc
содержит почти всю информацию об импорте, например. количество и типы параметров.
Если url http://ieeexplore.ieee.org/xpl/periodicals.jsp, funcdesc.args
- ()
, а для коррекции funcdesc.args
должно быть ((8, 1, None),)
.
Короче говоря, win32com
получил неверную информацию о типе, поэтому он построил неправильный метод.
Я не уверен, кто виноват, PyWin32 или IE. Но, основываясь на моем наблюдении, я не нашел ничего плохого в коде PyWin32. С другой стороны, следующий script отлично работает в Windows script Host.
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";
Дункан уже указал, что режим совместимости IE может предотвратить проблему. К сожалению, кажется невозможным включить режим совместимости с script.
Но я нашел трюк, который может помочь нам обойти проблему.
Сначала вам нужно посетить хороший сайт, который дает нам HTML-страницу и извлекает из него правильный объект Document
.
ie = win32com.client.DispatchEx('InternetExplorer.Application')
ie.Visible = 1
ie.Navigate('http://www.haskell.org/arrows')
time.sleep(5)
document = ie.Document
Затем перейдите на страницу, которая не работает
ie.Navigate('http://ieeexplore.ieee.org/xpl/periodicals.jsp')
time.sleep(5)
Теперь вы можете получить доступ к DOM второй страницы через старый объект Document
.
document.getElementById('browse_keyword').value = "Computer"
Если вы используете новый объект 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)
Ответ 3
Вызов методов экземпляров в Python автоматически добавляет экземпляр в качестве первого аргумента - поэтому вы должны явно писать аргумент "self" внутри методов.
Например, instance.method(args...)
равно Class.method(instance, args...)
.
Из того, что я вижу, программист, должно быть, забыл написать ключевое слово self, что привело к нарушению метода. Попробуйте заглянуть в библиотечный код.
Ответ 4
У меня появилась эта проблема, когда я обновился до IE11 из IE8.
Я тестировал это только в функции getElementsByTagName. Вы должны вызвать функцию из элемента Body.
#-*- 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.Body.getElementById("browse_keyword").value ="Computer"
ie.Document.Body.getElementsByTagName("input")[24].click()