mechanize 可以支持 ajax / 通过 javascript 填写表格吗?

Can mechanize support ajax / filling out forms via javascript?

我正在尝试创建一个程序来填写此网站上的表格: Insurance survey

我正在使用 python 2.7 并在多次尝试 3.4 后使用 mechanize 并意识到 mechanize 不适用于 3.4。我是新手,但在尝试这样做时学到了很多东西(python 很棒)。

import mechanize
br = mechanize.Browser() 
urlofmypage = 'https://interactive.web.insurance.ca.gov/survey/'
br.open(urlofmypage) 
print br.geturl()
br.select_form(nr=0)

br['location'] = ['ALAMEDA BERKELEY']   #SET FORM ENTRIES
br['coverageType'] = ['HOMEOWNERS']
br['coverageAmount'] = ['0,000']
br['homeAge'] = ['1-3 Years']

result = br.submit()
print result

这是我的错误:mechanize._form.ItemNotFoundError:名称为“$150,000”的商品不足

问题是,只有在我填写了表格字段 location 和 coverageType 之后,coverageAmount 选项才会显示 :( 。我一直在搞这个,在线观看了很多视频,我所有的研究都导致了我的结论是 mechanize 不会这样做。

我还了解到这是一个 ajax 调用,而 mechanize 对此不起作用。事情似乎指向 selenium webdriver ...有人有任何意见吗?

AJAX调用由javascript执行,mechanize没办法运行javascript。 Mechanize 仅查看静态 HTML 页面上的表单字段,并允许您填写并提交这些字段。这就是为什么您的研究将您指向诸如 Selenium 或 Ghost 之类的东西,后者 运行 在可以执行 javascript.

的真实浏览器之上

虽然有一种更简单的方法可以做到这一点!如果您使用浏览器上的开发人员工具(例如 Firefox 或 Chrome 中的网络选项卡)并填写表单,您可以看到您的浏览器在后台发出的请求,即使是 AJAX:

这告诉你:

  • 浏览器发出了 POST 请求
  • 为此URL:https://interactive.web.insurance.ca.gov/survey/survey?type=homeownerSurvey&event=HOMEOWNERS
  • 使用以下表单参数:
    • 位置=阿拉米达+阿拉米达
    • coverageType=HOMEOWNERS
    • 覆盖金额=150000
    • homeAge=新

您可以使用此信息在 Python 中发出相同的 POST 请求:

import urllib.parse, urllib.request

url = "https://interactive.web.insurance.ca.gov/survey/survey?type=homeownerSurvey&event=HOMEOWNERS"
data = urllib.parse.urlencode(dict(
    location="ALAMEDA ALAMEDA",
    coverageType="HOMEOWNERS",
    coverageAmount="150000",
    homeAge="New",
))
res = urllib.request.urlopen(URL, data.encode("utf8"))

print(res.read())

这是python3。 requests 库为发出 HTTP 请求提供了更好的 API。


编辑: 回答你的三个问题:

is it possible for the dictionary that you've created to have more than 1 location and cycle through them using a for loop?

是的,只需在代码周围添加一个循环并每次为 location 传递不同的值。我会将这段代码放入一个函数中以使代码更清晰,如下所示:

https://gist.github.com/lost-theory/08786e3a27c8d8ce3839

the results are in a lot of jibberish, so I'd have to find a way to sift through it huh. Like pick out which is which

是的,乱码是 HTML,您需要对其进行解析以收集您要查找的数据。看看 HTMLParser in the python standard library, or install a library like lxml or BeautifulSoup,它有更好的 API。您也可以尝试使用 str.split.

手动解析文本

如果您想将 table 的行转换为 python list,您需要找到所有行,如下所示:

  <tr Valign="top">
    <td align="left">Bankers Standard <a href='http://interactive.web.insurance.ca.gov/companyprofile/companyprofile?event=companyProfile&doFunction=getCompanyProfile&eid=5906'><small>(Info)</small></a></td>
    <td align="left"><div align="right">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;N/A</td>
    <td align="left"><div align="right">250</div></td>
    <td align="left">&nbsp;</td>
    <td align="left">Bankers Standard <a href='http://interactive.web.insurance.ca.gov/companyprofile/companyprofile?event=companyProfile&doFunction=getCompanyProfile&eid=5906'><small>(Info)</small></a></td>
    <td align="left"><div align="right">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1255</td>
    <td align="left"><div align="right">500</div></td>
  </tr>

您想遍历所有 <tr>(行)元素,获取每行内的所有 <td>(列)元素,然后清理每列中的文本(删除那些 &nbsp; 空格等)。

关于如何在 python 中解析或抓取 HTML 的 Whosebug 和 Internet 上的教程有很多问题,例如 this or this

could you explain why we had to do the data.encode line

好的!在 documentation for urlopen 中,它表示:

data must be a bytes object specifying additional data to be sent to the server, or None if no such data is needed.

urlencode 函数 returns 一个 unicode 字符串,如果我们试图将其传递给 urlopen,我们会得到这个错误:

TypeError: POST data should be bytes or an iterable of bytes. It cannot be of type str.

所以我们使用data.encode('utf8')将unicode字符串转换为字节。您通常需要使用字节进行输入和输出,例如读取或写入磁盘上的文件、通过网络发送或接收数据(例如 HTTP 请求等)。This presentation 对 [ 中的字节与 unicode 字符串有很好的解释=97=] 以及为什么在执行 I/O.

时需要 decode/encode

该页面没有 AJAX 调用。这是从 onchange 事件为 "Type of Coverage:" select 框执行的简单 Javascript 代码。

如果您查看页面的源代码,您会发现所有值都存储在 Javascript 函数 coverageTypeOnChange() 中。从中你可以计算出所有情况下 post 的内容。如果这些值没有改变,您将能够在没有 运行 Javascript 代码的情况下自动抓取网站。

但是,如果值随时间变化(例如,保费通常会发生变化),那么您最好还是看看 Selenium 或替代的无头浏览器。

这个问题曾经让我很头疼。关于以下行:

br['location'] = ['ALAMEDA BERKELEY']   #SET FORM ENTRIES

这意味着您正在从列表中选择 'ALAMEDA BERKELEY'。如果是,则尝试在项目后添加一个逗号:

br['location'] = ['ALAMEDA BERKELEY',]

否则使用:

br['location'] =  'ALAMEDA BERKELEY' 

我经常为机械化问题尝试精心设计的解决方法,只是回到我的原始代码并稍作修改......非常强大,非常无情