2012-03-22 1 views
17
#-*- 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!' 

При запуске первый раздел кода, он будет всплывающее окно:Интересный «getElementById() принимает ровно 1 аргумент (2 данный)», иногда это происходит. Может кто-нибудь объяснить это?

ie.Document.getElementById("browse_keyword").value ="Computer" 
TypeError: getElementById() takes exactly 1 argument (2 given) 

И вторая секция кода работает нормально. В чем разница, что делает результат другим?

+0

Что произойдет, если вы заберете '.1' из' ie = win32com.client.DispatchEx ('InternetExplorer.Application.1') '? – agf

+0

Я удалил «.1» из этого, ситуация остается. Теперь, когда запускается скрипт, работает только один браузер. Но "TypeError: getElementById() принимает ровно 1 аргумент (2 данных)" remians. –

+0

Я получаю ошибку даже со вторым примером кода. – wRAR

ответ

0

Вызов методов экземпляров в Python автоматически добавляет экземпляр в качестве первого аргумента - вот почему вы должны явно писать аргумент «self» внутри методов.

Например, instance.method(args...) равен Class.method(instance, args...).

Из того, что я вижу, программист, должно быть, забыл написать ключевое слово self, что привело к нарушению метода. Попробуйте заглянуть в библиотечный код.

+1

Это динамический COM-метод, никто его не написал. – wRAR

+0

Я думаю, win32com создает привязки. – nneonneo

+0

@nneonneo только во время выполнения. – wRAR

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'' 

К сожалению, я не знаю, как для переключения режим совместимости из сценария (свойство documentMode невозможно установить). Может, кто-то другой?

Неверный подсчет аргументов, я думаю, исходит из COM: Python передает аргументы, а объект COM отклоняет вызов с ошибочной ошибкой.

5

Как метод 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) 
+0

Как насчет 'CastTo (ie.Document.parentWindow.document," IHTMLDocument3 ")' (без приведения его в действие ihtmldocument2 в ie11) – Winand

+0

UPD. также нам нужно 'gencache.EnsureModule ('{3050F1C5-98B5-11CF-BB82-00AA00BDCE0B}', 0, 4, 0)' перед литьем – Winand

0

Я только что получил эту проблему, когда обновился до 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()