python 请求是否缺少 curl 中存在的内容?

Is python requests missing something that is present in curl?

所以我有这个 api 我正在尝试使用 python 实现自动化。我已经在 php curl 中完成了,而且效果很好。 api 没有证书,所以我在 php 中使用了 CURLOPT_SSL_VERIFYPEER, false,这很好用。但是,Python 请求也会看到此证书问题,因此我在执行 post 请求时使用了 verify=False。当我使用 python 向登录名 url 发送 post 请求时,问题就出现了。服务器 returns 一个 404 响应,指出所请求的 url 在服务器上不存在。这是我的 php 代码

<?php

function post($url,$post,$header){
    $ch=curl_init();
    curl_setopt_array($ch,array(
        CURLOPT_URL => $url,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POSTFIELDS => $post,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_PORT => 3456,
        CURLOPT_HTTPHEADER => $header,
    ));
    return json_decode(curl_exec($ch));
    curl_close($ch);
}

function login($m, $p){
    $ua = array("Connection:keep-alive","User-Agent:Mozilla/5.0 (Linux; Android 10; M2006C3LG Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36","Content-Type:application/x-www-form-urlencoded; charset=UTF-8","Accept-Encoding:gzip, deflate");
    $data = "email=".$m."&password=".$p."";
    $login = post("https://aartre.com/user/login",$data,$ua);
    print_r($login);
}

$mail = "email@gmail.com";
$pass = "password";
login($mail, $pass);

?>

这个 php 代码 returns 一个在登录期间分配的令牌,这意味着它正在工作。

这是我的python代码

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

s = requests.Session()


requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

headers = {
    "Host":"aartre.com:3456",
    "Connection":"keep-alive",
    "User-Agent":"Mozilla/5.0 (Linux; Android 10; M2006C3LG Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36",
    "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
    "Accept-Encoding":"gzip, deflate"
}

def get(url):
    return s.get(url, headers=headers, verify=False)

def post(url, data):
    return s.post(url, data=data, headers=headers, verify=False)

def login(email, password):
    url = "https://aartre.com/user/login"
    data = "email={email}&password={password}"
    res = post(url, data)
    print(res.text)

email = "email@gmail.com"
password = 'password'

login(email, password)

这段代码在我看来没有任何错误。登录 url 也是正确的,与 php 中使用的相同。但是在运行之后。这是回应

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.48 (Win64) OpenSSL/1.1.1l PHP/7.3.30 Server at aartre.com Port 3456</address>
</body></html>

可能是什么问题?我做错了吗?

您已指定给 CURLCURLOPT_PORT => 3456。根据文档:

This option sets number to be the remote port number to connect to, instead of the one specified in the URL or the default port for the used protocol.

所以CURL真的没有魔法。等效的 Python requests 将明确指定 URL 中的端口。正如@whiplash 指出的那样,POST 数据应该是 dict:

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

s = requests.Session()


requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

headers = {
    "Host":"aartre.com:3456",
    "Connection":"keep-alive",
    "User-Agent":"Mozilla/5.0 (Linux; Android 10; M2006C3LG Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36",
    "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
    "Accept-Encoding":"gzip, deflate"
}

def get(url):
    return s.get(url, headers=headers, verify=False)

def post(url, data):
    return s.post(url, data=data, headers=headers, verify=False)

def login(email, password):
    url = "https://aartre.com:3456/user/login"
    data = {'email': email, 'password': password}
    res = post(url, data=data)
    print(res.text)

email = "email@gmail.com"
password = 'password'

login(email, password)

请注意,我认为返回的响应是 JSON 字符串,因此您可以考虑打印 res.json() 而不是 res.text

更新

由于您使用的是 Session 实例,因此更常见的做法是在 Session 实例本身中设置要用于每个请求的任何 headers 并以这种方式您不需要在每个请求上明确指定 headers:

import requests

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


def login(email, password):
    url = "https://aartre.com:3456/user/login"
    data = {'email': email, 'password': password}
    res = s.post(url, data=data, verify=False)
    result = res.json()
    print(result)


with requests.Session() as s:
    headers = {
        "Connection":"keep-alive",
        "User-Agent":"Mozilla/5.0 (Linux; Android 10; M2006C3LG Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36",
        "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
        "Accept-Encoding":"gzip, deflate"
    }

    s.headers.update(headers)

    email = "email@gmail.com"
    password = 'password'

    login(email, password)