我正在尝试以编程方式将一些数据提交到我们公司管理页面上的表单,而不是手动完成。
我编写了许多其他工具来抓取该网站并操纵数据。但是,由于某种原因,这个特殊的问题给我带来了很多麻烦。
使用浏览器浏览:
以下是我试图抓取和发布数据的页面。请注意,这些页面通常显示在 js shadowboxes 中,但是,它在禁用 Javascript 的情况下运行良好,所以我假设 javascript 不是关于刮板问题的问题。
(注意,由于这是一个公司页面,我已经填写了我已经用垃圾标题替换了所有表单字段,因此,例如,客户编号完全是虚构的)
另外,由于它是用户名/密码墙后面的公司页面,我无法提供网站进行测试,所以我尝试在这篇文章中注入尽可能多的细节!
主要入口点在这里:
在此页面中,我单击"Add New form"
,这将在新标签中打开下一页(因为禁用了 javascript)。
在这个页面上,我填写了一个小表格,点击提交,然后进入下一页显示成功消息。
应该很简单吧?
代码尝试1:机械化
import mechanize
import base64
import cookielib
br = mechanize.Browser()
username = 'USERNAME'
password = 'PASSWORD'
br.addheaders.append(('Authorization',
'Basic %s' % base64.encodestring('%s:%s' % (username, password))))
br.addheaders = [('User-agent',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML,'
' like Gecko) Chrome/25.0.1364.172 Safari/537.22')]
br.open('www.our_company_page.com/adm/add_forms.php')
links = [link for link in br.links()]
# Follow "Add a form" Link
response = br.follow_link(links[0])
br.select_form(nr=0)
br.form.set_all_readonly(False)
br.form['formNumber'] = "FROM_PYTHON"
br.form['RevisionNumber'] = ['20']
br.form['FormType'] = ['H(num)']
response = br.submit()
print response.read() #Shows the exact same page! >:(
因此,如您所见,我尝试复制在浏览器中执行的步骤。我加载初始/adm/forms
页面,点击第一个链接,即Add a Form
,填写表格,然后单击submit
按钮。但这就是问题所在。mechanize 返回的响应是与表单完全相同的页面。没有错误消息,没有成功消息,当我手动检查我们的管理页面时,没有进行任何更改。
检查网络活动
沮丧的是,我打开 Chrome 浏览器,看着网络选项卡,因为我在浏览器中手动提交并提交了表单。
提交表单后,这是网络活动:
对我来说似乎很简单。有一个post
,然后是一个get
用于 css 文件,另一个get
用于 jquery 库。还有另一个get
用于某种图像,但我不知道那是什么。
检查 POST 请求的详细信息:
在搜索了一些关于抓取问题的 Google 搜索后,我看到了一个建议,即服务器可能需要某个标头,我应该简单地复制在 POST 请求中生成的所有内容,然后慢慢删除标头,直到我弄清楚哪个是重要的一。所以我就这么做了,复制了“网络”选项卡中的每一点信息,并停留在我的发布请求中。
代码尝试2:Urllib
我在用 . 找出所有标题内容时遇到了一些麻烦Mechanize
,所以我切换到了 urllib2。
import urllib
import urllib2
import base64
url = 'www.our_company_page.com/adm/add_forms.php'
values = {
'SID':'', #Hidden field
'FormNumber':'FROM_PYTHON1030PM',
'RevisionNumber':'5',
'FormType':'H(num)',
'fsubmit':'Save Page'
}
username = 'USERNAME'
password = 'PASSWORD'
headers = {
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding' : 'gzip,deflate,sdch',
'Accept-Language' : 'en-US,en;q=0.8',
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Authorization': 'Basic %s' % base64.encodestring('%s:%s' % (username, password)),
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Type' : 'application/x-www-form-urlencoded',
'Cookie' : 'ID=201399',
'Host' : 'our_company_page.com',
'Origin' : 'http://our_company_page.com',
'Referer' : 'http://our_company_page.com/adm/add_form.php',
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, '
'like Gecko) Chrome/26.0.1410.43 Safari/537.31'
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
print response.read()
如您所见,我将 Chrome 网络选项卡中的标头添加到urllib2
.
Mechainze 版本的另一个变化是我现在add_form.php
通过将相关 cookie 添加到我的请求中来直接访问该页面。
但是,即使尽我所能复制,我仍然遇到完全相同的问题:响应与我开始时完全相同的页面 - 没有错误,没有成功消息,服务器上没有更改,只是返回到空白表单。
最后一步:绝望中,我安装了 WireShark
是时候做一些流量嗅探了。我决心在这个神奇的帖子请求中看到 WTF 正在进行!
我下载、安装并启动 Wireshark。我过滤http
,然后首先在浏览器中手动提交表单,然后运行我的代码并尝试以编程方式提交表单。
这是网络流量:
浏览器:
Python:
除了标题的顺序略有不同(这很重要)之外,它们看起来完全一样!
所以这就是我完全困惑的地方,为什么一个post
请求(据我所知)几乎与浏览器发出的请求相同,但没有在服务器上进行任何更改。
有没有人遇到过这样的事情?我错过了一些明显的东西吗?这里发生了什么?
编辑
根据 Ric 的建议,我POST
准确地复制了数据。我直接从 Chrome 的“网络源”选项卡复制它。
修改后的代码如下
data = 'SegmentID=&Segment=FROMPYTHON&SegmentPosition=1&SegmentContains=Sections&fsubmit=Save+Page'
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
print response.read()
我唯一改变的是Segment
从FROMBROWSER
to的值FROMPYTHON
。
不幸的是,这仍然会产生相同的结果。响应是相同的页面,我从开始。
更新
工作,但没有解决
我查看了requests
库,使用他们的 API 重复了我的工作,并且神奇地它起作用了!POST实际上通过了。问题仍然存在:为什么!?我再次用wireshark拍摄了另一个快照,据我所知,它与浏览器发出的POST完全相同。
编码
def post(eventID, name, pos, containsID):
segmentContains = ["Sections", "Products"]
url = 'http://my_site.com/adm/add_page.php'
cookies = dict(EventID=str(eventID))
payload = { "SegmentID" : "",
"FormNumber" : name,
"RevisionNumber" : str(pos),
"FormType" : containsID,
"fsubmit" : "Save Page"
}
r = requests.post(
url,
auth=(auth.username, auth.password),
allow_redirects=True,
cookies=cookies,
data=payload)
Wireshark 输出
要求
浏览器
因此,总结一下问题的当前状态。它有效,但我什么都没有真正改变。我不知道为什么使用 Mechanize 和 urllib2 的尝试都失败了。发生了什么让requests
POST 真正通过?
编辑——Wing Tang Wong 建议:
根据Wing Tand Wongs
建议,我创建了一个 cookie 处理程序,并将其附加到urllib.opener
. 因此,不再在标头中手动发送 cookie —— 事实上,我现在根本没有分配任何东西。
我首先连接到带有表单链接的 adm 页面,而不是立即连接到表单。
'http://my_web_page.com/adm/segments.php?&n=201399'
这会将ID
cookie 提供给我的urllib
cookieJar
. 从这一点开始,我点击链接到包含表单的页面,然后像往常一样尝试提交给它。
完整代码:
url = 'http://my_web_page.com/adm/segments.php?&n=201399'
post_url = 'http://my_web_page.com/adm/add_page.php'
values = {
'SegmentID':'',
'Segment':'FROM_PYTHON1030PM',
'SegmentPosition':'5',
'SegmentContains':'Products',
'fsubmit':'Save Page'
}
username = auth.username
password = auth.password
headers = {
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding' : 'gzip,deflate,sdch',
'Accept-Language' : 'en-US,en;q=0.8',
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Authorization': 'Basic %s' % base64.encodestring('%s:%s' % (username, password)),
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Type' : 'application/x-www-form-urlencoded',
'Host' : 'mt_site.com',
'Origin' : 'http://my_site.com',
'Referer' : 'http://my_site.com/adm/add_page.php',
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31'
}
COOKIEFILE = 'cookies.lwp'
cj = cookielib.LWPCookieJar()
if os.path.isfile(COOKIEFILE):
cj.load(COOKIEFILE)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)
data = urllib.urlencode(values)
req = urllib2.Request(url, headers=headers)
handle = urllib2.urlopen(req)
req = urllib2.Request(post_url, data, headers)
handle = urllib2.urlopen(req)
print handle.info()
print handle.read()
print
if cj:
print 'These are the cookies we have received so far :'
for index, cookie in enumerate(cj):
print index, ' : ', cookie
cj.save(COOKIEFILE)
和以前一样。服务器上不会进行任何更改。为了验证 cookie 确实存在,我在提交表单后将它们打印到控制台,它给出了输出:
These are the cookies we have received so far :
<Cookie EventID=201399 for my_site.com/adm>
因此,cookie 就在那里,并且已与请求一起发送.. 所以仍然不确定发生了什么。