Python Requests - 是否可能在HTTP POST请求后接收部分响应?

6

我正在使用Python Requests模块来进行网站数据挖掘。在数据挖掘的过程中,我需要HTTP POST一个表单,并通过检查生成的URL来检查是否成功。我的问题是,在POST之后,是否有可能请求服务器不发送整个页面?我只需要检查URL,但是我的程序下载了整个页面并消耗了不必要的带宽。代码非常简单。

import requests
r = requests.post(URL, payload)
if 'keyword' in r.url:
   success
fail

唯一的方法是尽早关闭TCP连接,您不能请求服务器不发送内容(只有“HEAD”请求可以这样做)。但是,我非常谨慎地建议不要从蜘蛛发送POST请求。 - Dietrich Epp
POST和GET可以互换使用吗?另外,什么是爬虫,为什么我应该小心? - Display Name
2
POST和GET绝对不能互换使用。发送GET请求是“安全的”--它不会修改网站,如果网站管理员看到大量的GET请求,那么只是流量而已。POST请求用于在Web上添加、修改和删除内容,它们通常执行某种操作。如果网站管理员从一个IP地址看到大量自动化的POST请求,他们可能会认为你是垃圾邮件机器人或黑客,并向你的ISP报告。所以我会小心谨慎。 - Dietrich Epp
尝试使用prefetch=False以避免立即下载正文。服务器可能在发布后重定向您。检查r.history,看看requests是否像浏览器一样在POST之后执行GET。还有一个redirect参数,但我不知道它对于POST请求有什么作用。 - jfs
我将prefetch设置为False,并使用wireshark捕获数据包。不幸的是,无论我是否设置了prefetch,服务器都会发送给我相同的内容。 - Display Name
1
您还需要关闭连接。这里是使用 httplib 的示例:http://ideone.com/KEQajG - jfs
3个回答

2

如果你能实现,一种简单的解决方案是采用低级别的方式。使用Socket库。 例如,你需要发送一个带有数据的POST请求。我在我的爬虫程序中就使用了这种方法。

import socket
from urllib import quote # POST body is escaped. use quote

req_header = "POST /{0} HTTP/1.1\r\nHost: www.yourtarget.com\r\nUser-Agent: For the lulz..\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nContent-Length: {1}"
req_body = quote("data1=yourtestdata&data2=foo&data3=bar=")
req_url = "test.php"
header = req_header.format(req_url,str(len(req_body))) #plug in req_url as {0} 
                                                       #and length of req_body as Content-length
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #create a socket
s.connect(("www.yourtarget.com",80))                   #connect it
s.send(header+"\r\n\r\n"+body+"\r\n\r\n")              # send header+ two times CR_LF + body + 2 times CR_LF to complete the request

page = ""
while True:
    buf = s.recv(1024) #receive first 1024 bytes(in UTF-8 chars), this should be enought to receive the header in one try
    if not buf:
        break
    if "\r\n\r\n" in page: # if we received the whole header(ending with 2x CRLF) break
        break
    page+=buf
s.close()       # close the socket here. which should close the TCP connection even if data is still flowing in
                # this should leave you with a header where you should find a 302 redirected and then your target URL in "Location:" header statement.

0

如果您提供一些更多的数据,例如您正在尝试请求的示例URL,那将会很有帮助。话虽如此,我认为您通常是在使用以下算法依赖重定向或HTTP 404错误来检查您的POST请求后是否具有正确的URL:

if original_url == returned request url:
    correct url to a correctly made request
else:
    wrong url and a wrongly made request

如果是这种情况,您可以在Python的requests库中使用HTTP HEAD请求(另一种HTTP请求类型,如GET、POST等)来仅获取标头而不是页面正文。然后,您将检查响应代码和重定向URL(如果存在)以查看是否向有效的URL发出了请求。
例如:
def attempt_url(url):
    '''Checks the url to see if it is valid, or returns a redirect or error.
    Returns True if valid, False otherwise.'''

    r = requests.head(url)
    if r.status_code == 200:
        return True
    elif r.status_code in (301, 302):
        if r.headers['location'] == url:
            return True
        else:
            return False
    elif r.status_code == 404:
        return False
    else:
        raise Exception, "A status code we haven't prepared for has arisen!"

如果这不完全符合您的要求,提供更多详细信息将会有所帮助。至少,这可以让您获取状态码和标头,而无需拉取所有页面数据。


使用 HEAD 在这里没有帮助,因为要求发送表单数据,这需要发送 POST - Piotr Dobrogost
@Piotr,这就是为什么我想要更多关于他的需求信息--他现在确实说他正在POST表单信息,但我猜测这可能不是必要的,因为在原始问题的评论中,他显示出不知道GET和POST之间的区别--所以也许这个需求并不是保证的。我在我的答案顶部指定了适用条件。 - jdotjdot

0

这个网站可能使用Post/Redirect/Get (PRG)模式。如果是这样,那么只需不跟随重定向并从响应中读取Location头即可。

示例

>>> import requests
>>> response = requests.get('http://httpbin.org/redirect/1', allow_redirects=False)
>>> response.status_code
302
>>> response.headers['location']
'http://httpbin.org/get'

如果您需要更多关于重定向后会得到什么信息,您可以在“Location”头中给定的URL上使用“HEAD”。
示例
>>> import requests
>>> response = requests.get('http://httpbin.org/redirect/1', allow_redirects=False)
>>> response.status_code
302
>>> response.headers['location']
'http://httpbin.org/get'
>>> response2 = requests.head(response.headers['location'])
>>> response2.status_code
200
>>> response2.headers
{'date': 'Wed, 07 Nov 2012 20:04:16 GMT', 'content-length': '352', 'content-type':
'application/json', 'connection': 'keep-alive', 'server': 'gunicorn/0.13.4'}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接