Python模拟Web交互时PHPSESSID Cookie缺失问题及修复
我有一段PHP页面代码:
<?php session_start(); ?> <html> <body> <?php echo '<!-- cookies -->'; foreach($_COOKIE as $k => $v) { echo '<!-- ' . $k . '=>' . $v . ' -->'; } ?> </body> </html>
通过浏览器访问https://daley.ws/file1.php时,页面源码显示包含PHPSESSID Cookie:
<html> <body> <!-- cookies --><!-- PHPSESSID=>22u20jsi288vtk5epser56d2bn7leb6n --></body> </html>
但我在Mac上运行以下Python代码时:
#!/Library/Frameworks/Python.framework/Versions/3.8/bin/python3 import urllib.request REQ_HOST = "daley.ws" DALEY_WS = 'https://daley.ws' DALEY_WS_FILE1 = '{}/file1.php'.format(DALEY_WS) DALEY_WS_FILE2 = '{}/file2.php'.format(DALEY_WS) if __name__ == "__main__": print(DALEY_WS_FILE1) req = urllib.request.Request(DALEY_WS_FILE1,\ unverifiable=True, origin_req_host=REQ_HOST,\ method='POST') with urllib.request.urlopen(req) as response: data = response.read().decode() print('data: {}'.format(data))
得到的输出里没有PHPSESSID:
https://daley.ws/file1.php data: <html> <body> <!-- cookies --></body> </html>
想请教为什么PHPSESSID Cookie没出现?怎么修改Python代码才能获取正确的输出?
原因分析
这事儿其实是会话Cookie的生成和传递逻辑在搞怪:
- 你用浏览器访问时,大概率之前已经和这个站点打过交道,浏览器本地已经保存了PHPSESSID Cookie,所以这次请求会自动把Cookie带过去,PHP的
$_COOKIE数组就能读到它,自然页面输出里就有了。 - 而你的Python请求是完全全新的无状态请求,没有携带任何Cookie过去。这时候PHP执行
session_start()时,发现请求里没有PHPSESSID,就会生成一个新的会话ID,但这个ID是通过响应头的Set-Cookie字段返回给你的,并不会出现在当前请求的$_COOKIE数组里——毕竟$_COOKIE存的是这次请求客户端发过来的内容,不是服务器刚生成的内容。
解决方案
要让Python请求能拿到包含PHPSESSID的页面输出,核心就是要让后续请求带上服务器生成的Cookie。咱们可以用Python的http.cookiejar来自动管理Cookie,它会帮你保存服务器返回的Cookie,下次请求自动带上。
修改后的代码如下:
#!/Library/Frameworks/Python.framework/Versions/3.8/bin/python3 import urllib.request from http.cookiejar import CookieJar REQ_HOST = "daley.ws" DALEY_WS = 'https://daley.ws' DALEY_WS_FILE1 = '{}/file1.php'.format(DALEY_WS) if __name__ == "__main__": # 创建CookieJar对象来自动管理Cookie cookie_jar = CookieJar() # 构建带Cookie处理能力的请求器 opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie_jar)) # 第一次请求:获取服务器生成的PHPSESSID并保存 print("第一次请求(无Cookie):") req = urllib.request.Request(DALEY_WS_FILE1, method='POST') with opener.open(req) as response: data = response.read().decode() print('data: {}'.format(data)) # 可以查看服务器返回的Set-Cookie头,这里就能看到刚生成的PHPSESSID print('服务器返回的Cookie:', response.getheader('Set-Cookie')) # 第二次请求:自动携带保存的Cookie访问 print("\n第二次请求(带Cookie):") req2 = urllib.request.Request(DALEY_WS_FILE1, method='POST') with opener.open(req2) as response2: data2 = response2.read().decode() print('data: {}'.format(data2))
代码说明:
CookieJar负责存储服务器返回的Cookie;build_opener创建的请求器会自动处理Cookie的收发,第二次请求时会自动带上之前保存的PHPSESSID;- 第二次请求时,PHP收到你带过去的PHPSESSID,
$_COOKIE数组里就会包含这个ID,页面输出也就和浏览器访问的结果一致了。
如果你的需求是只发一次请求就看到PHPSESSID,那其实做不到——因为$_COOKIE只能读取本次请求客户端发送的内容,而PHPSESSID是服务器在这次响应中才生成的,不会出现在本次请求的输出里。浏览器能看到是因为它之前已经保存了Cookie,不是第一次访问的结果哦。
内容的提问来源于stack exchange,提问作者AixNPanes
相关产品推荐
相关产品推荐

