Как метод 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
. Но если вы попытаетесь использовать его напрямую, а именно, вызовите 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
содержит почти всю информацию импорта, например, количество и типы параметров.
Если код http://ieeexplore.ieee.org/xpl/periodicals.jsp, funcdesc.args
- ()
, а коррекция funcdesc.args
- ((8, 1, None),)
.
Короче говоря, win32com
получил неверную информацию о типе, поэтому он построил неправильный метод.
Я не уверен, кто виноват, PyWin32 или IE. Но, основываясь на моем наблюдении, я не нашел ничего плохого в коде PyWin32. С другой стороны, следующий сценарий отлично работает в 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 может предотвратить проблему. К сожалению, кажется невозможным включить режим совместимости из сценария.
Но я нашел трюк, который может помочь нам обойти проблему.
Во-первых, вам нужно посетить хороший сайт, который дает нам 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)
Что произойдет, если вы заберете '.1' из' ie = win32com.client.DispatchEx ('InternetExplorer.Application.1') '? – agf
Я удалил «.1» из этого, ситуация остается. Теперь, когда запускается скрипт, работает только один браузер. Но "TypeError: getElementById() принимает ровно 1 аргумент (2 данных)" remians. –
Я получаю ошибку даже со вторым примером кода. – wRAR