mechanize无法读取禁用且没有值的SubmitControl表单

9 投票
2 回答
2773 浏览
提问于 2025-04-17 12:54

我正在尝试使用 mechanize(版本 0.2.5)来处理一个页面上的表单,这个表单里有一个禁用的图片作为其中一个元素。当我试图选择这个表单时,mechanize 报错了,提示 AttributeError: control 'test' is disabled,其中 test 是这个禁用控件的名字。例如,

br = mechanize.Browser(factory=mechanize.RobustFactory())
br.open("http://whatever...")
br.select_form(nr=0)

这导致了以下的错误追踪信息:

    br.select_form(nr=0)
  File "build\bdist.win32\egg\mechanize\_mechanize.py", line 499, in select_form
  File "build\bdist.win32\egg\mechanize\_html.py", line 544, in __getattr__
  File "build\bdist.win32\egg\mechanize\_html.py", line 557, in forms
  File "build\bdist.win32\egg\mechanize\_html.py", line 237, in forms
  File "build\bdist.win32\egg\mechanize\_form.py", line 844, in ParseResponseEx
  File "build\bdist.win32\egg\mechanize\_form.py", line 1017, in _ParseFileEx
  File "build\bdist.win32\egg\mechanize\_form.py", line 2735, in new_control
  File "build\bdist.win32\egg\mechanize\_form.py", line 2336, in __init__
  File "build\bdist.win32\egg\mechanize\_form.py", line 1221, in __setattr__
AttributeError: control 'test' is disabled

查看 mechanize 的源代码后,我发现只要表单里有任何一个元素被认为是 mechanize.SubmitControl,并且没有预定义的 value 属性,就会一直出现这个错误。例如,下面这个表单也会引发同样的错误:

<form action="http://whatever" method="POST">
    <input name="test" type="submit" disabled="disabled" />
</form>

我不确定这是否算是一个bug,但无论如何,有没有解决办法?比如说,我能否在调用 br.select_form() 之前,先修改目标页面的 HTML 来启用这些禁用的控件?

编辑

我已经提交了一个补丁给 mechanize,修复了这个问题。

2 个回答

0

这绝对是个bug,向上报告这个问题、制作修复补丁、提交修复并在此期间使用修复版本是处理这个问题的正确方法。(感谢你选择了这种方式。)

正如你提到的,另一种方法是通过预处理源HTML来解决这个问题(如果你很着急,或者因为某些原因不想使用修复版本,这可能会有用,但要注意,临时解决方法对社区没有帮助)。对于后处理,可以使用任何合适的方法——从简单的字符串替换(str.replace())到使用像BeautifulSouplxml这样的工具进行DOM级别的处理。

8

很遗憾,已经过去一年多了,mechanize的上游项目还是没有合并那个请求。

与此同时,你可以使用我写的这个“猴子补丁”来解决这个问题,这样就不需要手动安装修补过的版本了。希望这个问题在0.2.6版本发布时能得到解决,所以这个补丁只适用于0.2.5及之前的版本。

def monkeypatch_mechanize():
    """Work-around for a mechanize 0.2.5 bug. See: https://github.com/jjlee/mechanize/pull/58"""
    import mechanize
    if mechanize.__version__ < (0, 2, 6):
        from mechanize._form import SubmitControl, ScalarControl

        def __init__(self, type, name, attrs, index=None):
            ScalarControl.__init__(self, type, name, attrs, index)
            # IE5 defaults SUBMIT value to "Submit Query"; Firebird 0.6 leaves it
            # blank, Konqueror 3.1 defaults to "Submit".  HTML spec. doesn't seem
            # to define this.
            if self.value is None:
                if self.disabled:
                    self.disabled = False
                    self.value = ""
                    self.disabled = True
                else:
                    self.value = ""
            self.readonly = True

        SubmitControl.__init__ = __init__

撰写回答