使用python请求的GET/POST请求,带有额外的未命名参数

nnsrf1az  于 2023-03-20  发布在  Python
关注(0)|答案(2)|浏览(89)

我目前正在处理一个Anel电源插座管理器的实现。Anel电源插座支持以下请求:https://forum.anel.eu/viewtopic.php?f=52&t=888&sid=39081b8e472aaae7ffcec4cd3fb41e83
但是,http请求的特殊形式指定了URL中以逗号分隔的串联userpassword,这让我很头疼,因为它似乎不受python requests包的支持。

  • 因此我的问题是 *

如何以这两种形式之一发送http请求

  1. http://IP?param=value,userpassword
  2. http://IP?param=value&userpassword
  3. http://IP?param&userpassword
    例如:
  4. http://192.168.0.244?Stat&userpassword
  5. http://192.168.0.244?Sw=0xc0&userpassword
    使用python requests包?我可以使用浏览器手动发送请求并得到适当的响应。但是,我不能通过编程发送请求。
    我在语法上遇到了困难,因为请求文档没有提到任何未命名的参数(https://requests.readthedocs.io/en/latest/user/quickstart/#make-a-request)。
    它告诉我如何使用dict或tuple将参数作为键-值对传递,如何在请求体中传递参数,如何处理响应对象,但所有这些都不是我的问题,它只是发送“userpassword”字符串,它可以作为连接的明文传递,也可以作为相同的base64编码传递,在命名参数后用逗号或和号分隔。
req = requests.get("IP", {"Param": "Value"}, "userpassword")
Traceback (most recent call last):
  File "/home/user/pycharm-231.7864.77/plugins/python/helpers/pydev/pydevconsole.py", line 364, in runcode
    coro = func()
  File "<input>", line 1, in <module>
TypeError: get() takes from 1 to 2 positional arguments but 3 were given
req = requests.post("http://IP", {"Param": "Value"}, "userpassword")
req
<Response [401]>
req = requests.post("http://IP", {"Param": "Value", None: "userpassword"})
req
<Response [401]>

但是,手动组装URL可以工作。

req = requests.get("http://IP?Param=Value,userpassword", allow_redirects=False)
req.content
b'304 Redirect: /u_res.htm\r\n'

req2 = requests.get("http://IP/u_res.htm")
req2.text
"blablablabla"

不幸的是,如果我使用抽象层,我不希望我的代码依赖于请求格式的确切细节。

iqih9akk

iqih9akk1#

你可以创建一个函数并替换参数。
例如:

def request_function(host, param, value, user, passwd):
    try:
        host = f'http://{host}?{param}={value},{user}{password}'
        response = requests.get(host)
        return [True, response.status_code, response.text]
    except:
        try:
            host = f'http://{host}?{param}={value}&{user}{password}'
            response = requests.get(host)
            return [True, response.status_code, response.text]
        except:
            try:
                host = f'http://{host}?{param}&{user}{password}'
                response = requests.get(host)
                return [True, response.status_code, response.text]
            except:
                return [False, 0, 'No works']

取决于哪一个工作,你可以删除其他的try块。

cx6n0qe3

cx6n0qe32#

这需要一些修改,但现在我自己找到了一个解决方案,线索是认识到请求是可以准备的,并且所谓的PreparedRequest是完全可变的,正如手册www.example.com中所述https://requests.readthedocs.io/en/latest/api/#requests.PreparedRequest(后面是一个用法示例):
类请求。已准备请求
完全可变的PreparedRequest对象,包含将发送到服务器的确切字节数。
因此,当他们声称它是完全可变的时,我应该能够根据需要调整PreparedRequest
下面的代码显示了这一点,它向anel outlet发出了两个不同的请求。它首先生成一个Request对象,然后准备它,从而产生一个PreparatedRequest示例。在准备好的请求中,修改url,去掉一些占位符,只用于位置参数。然后,修改后的请求被发送到outlet。
额外的代码实际上需要7个LLOC,这在大多数情况下应该是合理的。剩下的只是测试代码和调试输出的代码。在本例中,给定的user和pass是出厂默认值,需要用实际的user和pass替换。

import requests
from requests import session, Request

BASE_URL = "http://192.168.0.245"

def request_hack(url: str = BASE_URL, params: dict = None) -> requests.PreparedRequest:
    DUMMY = "dummy"

    if isinstance(params, dict):
        # The requests lib would normally ignore params mapping to None,
        # but if we replace the None values using some DUMMY the resulting
        # url can be conditioned to strip the superfluous assignments in
        # the positional only parameters
        params = {k: v or DUMMY for k, v in params.items()}

    # The request cannot be sent directly as the URL syntax for the anel outlets is not supported by the requests lib
    # Therefore, we first set-up a PreparedRequest and then modify the url to our needs
    req = Request("GET", url, params=params).prepare()
    print(f"Unconditioned request {req.url}")
    # Remove the DUMMY values and the preceeding equal signs by replacing them with empty strings
    req.url = req.url.replace(f"={DUMMY}", "")
    print(f"Conditioned request {req.url}")

    return req

if __name__ == "__main__":
    the_session = session()

    """ Perform a Stat request to the anel outlet """
    req = request_hack(params={"Stat": None, "adminanel": None})
    res = the_session.send(req)
    print(res.text)

    """ Perform a Switch request to the anel outlet """
    req = request_hack(params={"Sw": "0x55", "adminanel": None})
    res = the_session.send(req)
    print(res.text)

示例代码将输出如下内容:

Unconditioned request http://192.168.0.245/?Stat=dummy&adminanel=dummy
Conditioned request http://192.168.0.245/?Stat&adminanel
NET-CONTROL    ;192.168.0.245;NET - Power Control;<font color=red><span style='font-size:7pt'>Kein Zeit Server. Die Uhr ist nicht gestellt: Timer inaktiv.</span></font>;1946;P;4.6 DE;24.5;<br>Nr. 1;1;0;Nr. 2;0;0;Nr. 3;1;0;Nr. 4;0;0;Nr. 5;1;0;Nr. 6;0;0;Nr. 7;1;0;Nr. 8;0;0;
Unconditioned request http://192.168.0.245/?Sw=0x55&adminanel=dummy
Conditioned request http://192.168.0.245/?Sw=0x55&adminanel
NET-CONTROL    ;192.168.0.245;NET - Power Control;<font color=red><span style='font-size:7pt'>Kein Zeit Server. Die Uhr ist nicht gestellt: Timer inaktiv.</span></font>;1946;P;4.6 DE;24.5;<br>Nr. 1;1;0;Nr. 2;0;0;Nr. 3;1;0;Nr. 4;0;0;Nr. 5;1;0;Nr. 6;0;0;Nr. 7;1;0;Nr. 8;0;0;

相关问题