11

我正在尝试以编程方式将一些数据提交到我们公司管理页面上的表单,而不是手动完成。

我编写了许多其他工具来抓取该网站并操纵数据。但是,由于某种原因,这个特殊的问题给我带来了很多麻烦。

使用浏览器浏览:

以下是我试图抓取和发布数据的页面。请注意,这些页面通常显示在 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()

我唯一改变的是SegmentFROMBROWSERto的值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 的尝试都失败了。发生了什么让requestsPOST 真正通过?

编辑——Wing Tang Wong 建议:

根据Wing Tand Wongs建议,我创建了一个 cookie 处理程序,并将其附加到urllib.opener. 因此,不再在标头中手动发送 cookie —— 事实上,我现在根本没有分配任何东西。

我首先连接到带有表单链接的 adm 页面,而不是立即连接到表单。

'http://my_web_page.com/adm/segments.php?&n=201399'

这会将IDcookie 提供给我的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 就在那里,并且已与请求一起发送.. 所以仍然不确定发生了什么。

4

2 回答 2

4

阅读并重新阅读您的帖子,其他人会回答几次。我的想法:

当您在 mechanize 和 urllib2 中实现时,看起来 cookie 被硬编码到标头响应中。这很可能会导致表单将您踢出局。

当您切换到使用网络浏览器并使用 python 'requests' 库时,cookie 和会话处理正在幕后处理。

我相信,如果您更改代码以考虑 cookie 和会话状态,即。假设在开始时有一个自动会话,站点的 cookie 为空且没有会话数据,但在会话期间正确跟踪和管理它,它应该可以工作。

简单的复制和替换标题数据是行不通的,一个正确编码的网站应该会让你回到开头。

在没有看到网站后端代码的情况下,以上是我的观察。Cookie 和会话数据是罪魁祸首。

编辑:

找到这个链接: http ://docs.python-requests.org/en/latest/

其中描述了使用身份验证/等访问站点。身份验证的格式类似于您使用的 Requests 实现。它们链接到一个 urllib2 实现的 git 源,它执行相同的操作,我注意到身份验证位与您执行身份验证位的方式不同:

https://gist.github.com/kennethreitz/973705

从页面:

password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, gh_url, 'user', 'pass')

auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)
opener = urllib2.build_opener(auth_manager)

我想知道您是否更改了为 urllib2 实现实现身份验证位的方式,它会起作用。

于 2013-04-04T23:47:08.677 回答
0

我认为 PHP 脚本出错并且没有显示任何内容,因为您的表单数据并不完全相同。尝试将发布请求复制为完全相同,包括所有值。我看到浏览器的 Wireshark 屏幕截图上基于行的文本数据包含参数,例如 SegmentPosition 为 0,但在您的 Python 屏幕截图中没有 SegmentPosition 的值。浏览器和 Python 请求之间的某些参数(例如 Segment)的格式似乎不同,这可能会在尝试解析它时导致错误。

于 2013-03-31T20:13:05.197 回答