JSONP
目录
同源策略介绍
基于安全的原因,浏览器是存在同源策略机制的,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。
验证同源策略
from django.shortcuts import render,HttpResponse from django.views import View import json class Index(View): def get(self,request): data = {"a":1,"b":2,"c":"中国"} return HttpResponse(json.dumps(data))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <script src="jquery.js"></script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> </body> </html>
解决同源策略
服务器端直接添加
from django.shortcuts import render,HttpResponse from django.views import View import json class Index(View): def get(self,request): data = {"a":1,"b":2,"c":"中国"} ret = HttpResponse(json.dumps(data)) # 在头部添加Access-Control-Allow-Origin字段为*,在源头允许跨域,测试有效 print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8')} ret._headers["Access-Control-Allow-Origin"] = ("Access-Control-Allow-Origin","*") print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'Access-Control-Allow-Origin': ('Access-Control-Allow-Origin', '*')} return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <script src="jquery.js"></script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> </body> </html>
使用script的src字段绕过同源策略
简单使用script的src字段
from django.shortcuts import render,HttpResponse from django.views import View import json # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # ret = HttpResponse(json.dumps(data)) # # # 在头部添加Access-Control-Allow-Origin字段为*,在源头允许跨域,测试有效 # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8')} # ret._headers["Access-Control-Allow-Origin"] = ("Access-Control-Allow-Origin","*") # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'Access-Control-Allow-Origin': ('Access-Control-Allow-Origin', '*')} # # return ret class Index(View): def get(self,request): data = {"a":1,"b":2,"c":"中国"} ret = HttpResponse(json.dumps(data)) return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <input type="button" id="btn2" value="btn2"> <script src="jquery.js"></script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> <script> function CreateScript(src) { var Scrip=document.createElement('script'); // 创建script标签 Scrip.src=src; // 设置script的src属性 document.body.appendChild(Scrip); } $('#btn2').click(function(){ console.log("btn2"); CreateScript("http://127.0.0.1:8000/jsonp/index/") }) </script> </body> </html>
原因分析
- 添加标签:<script src="http://127.0.0.1:8000/jsonp/index/"></script>
- 经过服务端返回数据{"a": 1, "b": 2, "c": "\u4e2d\u56fd"},浏览器将其解析为html语言出现错误,非数据,比如返回的是字符串“abc”,但浏览器会解析为abc对象。
- 浏览器不知道怎么处理这些返回来的html代码
解决方法
- 如果返回的数据是"func("abc")",那么浏览器就会找到func函数对字符串“abc”进行处理
自定义方法,使用script的src字段,返回带有callback方法的数据
from django.shortcuts import render,HttpResponse from django.views import View import json # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # ret = HttpResponse(json.dumps(data)) # # # 在头部添加Access-Control-Allow-Origin字段为*,在源头允许跨域,测试有效 # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8')} # ret._headers["Access-Control-Allow-Origin"] = ("Access-Control-Allow-Origin","*") # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'Access-Control-Allow-Origin': ('Access-Control-Allow-Origin', '*')} # # return ret # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # # ret = HttpResponse(json.dumps(data)) # return ret class Index(View): def get(self,request): callback= request.GET.get("callback") data = {"a":1,"b":2,"c":"中国"} data = '{func}({data})'.format(func=callback,data=data) # data = '{func}("{data}")'.format(func=callback,data=data) # 如果是字符串,则需要加双引号 ret = HttpResponse(data) return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <input type="button" id="btn2" value="btn2"> <script src="jquery.js"></script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> <script> function CreateScript(src) { var Scrip=document.createElement('script'); // 创建script标签 Scrip.src=src; // 设置script的src属性 document.body.appendChild(Scrip); } function myjsonpcallback(data) { console.log("==================jsonpcallback==================") console.log(data) } $('#btn2').click(function(){ console.log("btn2"); // CreateScript("http://127.0.0.1:8000/jsonp/index/") CreateScript("http://127.0.0.1:8000/jsonp/index/?callback=myjsonpcallback") }) </script> </body> </html>
直接使用ajax自带的jsonp,返回带有callback方法的数据
from django.shortcuts import render,HttpResponse from django.views import View import json # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # ret = HttpResponse(json.dumps(data)) # # # 在头部添加Access-Control-Allow-Origin字段为*,在源头允许跨域,测试有效 # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8')} # ret._headers["Access-Control-Allow-Origin"] = ("Access-Control-Allow-Origin","*") # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'Access-Control-Allow-Origin': ('Access-Control-Allow-Origin', '*')} # # return ret # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # # ret = HttpResponse(json.dumps(data)) # return ret class Index(View): def get(self,request): callback= request.GET.get("callback") data = {"a":1,"b":2,"c":"中国"} data = '{func}({data})'.format(func=callback,data=data) # data = '{func}("{data}")'.format(func=callback,data=data) # 如果是字符串,则需要加双引号 ret = HttpResponse(data) return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <input type="button" id="btn2" value="btn2"> <input type="button" id="btn3" value="btn3"> <script src="jquery.js"></script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> <script> function CreateScript(src) { var Scrip=document.createElement('script'); // 创建script标签 Scrip.src=src; // 设置script的src属性 document.body.appendChild(Scrip); } function myjsonpcallback(data) { console.log("==================jsonpcallback==================") console.log(data) } $('#btn2').click(function(){ console.log("btn2"); // CreateScript("http://127.0.0.1:8000/jsonp/index/") CreateScript("http://127.0.0.1:8000/jsonp/index/?callback=myjsonpcallback") }) </script> <script> function myjsonpcallback(data) { console.log("==================jsonpcallback==================") console.log(data) } $(document).ready(function(){ $("#btn3").click(function(){ console.log("btn3"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", dataType:"jsonp", jsonp:"callback", //此为URL上的参数,url?callback=myjsonpcallback,可修改,比如jsonp:cb,则变为url?cb=myjsonpcallback jsonpCallback:"myjsonpcallback", // 回调函数名称 success:function(data){ console.log("==================success==================") console.log(data) } }) }) } ) </script> </body> </html>
完整版
from django.shortcuts import render,HttpResponse from django.views import View import json # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # ret = HttpResponse(json.dumps(data)) # # # 在头部添加Access-Control-Allow-Origin字段为*,在源头允许跨域,测试有效 # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8')} # ret._headers["Access-Control-Allow-Origin"] = ("Access-Control-Allow-Origin","*") # print(ret._headers) # {'content-type': ('Content-Type', 'text/html; charset=utf-8'), 'Access-Control-Allow-Origin': ('Access-Control-Allow-Origin', '*')} # # return ret # class Index(View): # def get(self,request): # data = {"a":1,"b":2,"c":"中国"} # # ret = HttpResponse(json.dumps(data)) # return ret # class Index(View): # def get(self,request): # callback= request.GET.get("callback") # data = {"a":1,"b":2,"c":"中国"} # # data = '{func}({data})'.format(func=callback,data=data) # # data = '{func}("{data}")'.format(func=callback,data=data) # 如果是字符串,则需要加双引号 # # ret = HttpResponse(data) # return ret class Index(View): def get(self,request): callback= request.GET.get("callback") # data = "abcd" data = {"a":1,"b":2,"c":"中国"} # data = [1,2,"中国","a"] if isinstance(data,str): data = '{func}("{data}")'.format(func=callback,data=data) else: data = '{func}({data})'.format(func=callback,data=data) ret = HttpResponse(data) return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>haha</h1> <input type="button" id="btn1" value="btn1"> <input type="button" id="btn2" value="btn2"> <input type="button" id="btn3" value="btn3"> <hr> <textarea id="text" style="width: 400px; height: 100px;"></textarea> <script src="jquery.js"></script> <script> function myjsonpcallback(data) { console.log("==================jsonpcallback==================") console.log(data) $("#text").val(data) } </script> <script> $("#btn1").click(function () { console.log("btn1"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", success:function(msg){ console.log(msg) } }) }); </script> <script> // 自定义方法,使用script的src字段,返回带有callback方法的数据 function CreateScript(src) { var Scrip=document.createElement('script'); // 创建script标签 Scrip.src=src; // 设置script的src属性 document.body.appendChild(Scrip); } $('#btn2').click(function(){ console.log("btn2"); // CreateScript("http://127.0.0.1:8000/jsonp/index/") CreateScript("http://127.0.0.1:8000/jsonp/index/?callback=myjsonpcallback") }) </script> <script> // 直接使用ajax自带的jsonp,返回带有callback方法的数据 $(document).ready(function(){ $("#btn3").click(function(){ console.log("btn3"); $.ajax({ type:"GET", url:"http://127.0.0.1:8000/jsonp/index/", dataType:"jsonp", jsonp:"callback", //此为URL上的参数,url?callback=myjsonpcallback,可修改,比如jsonp:cb,则变为url?cb=myjsonpcallback jsonpCallback:"myjsonpcallback", // 回调函数名称 success:function(data){ console.log("==================success==================") console.log(data) } }) }) } ) </script> </body> </html>
网上实例
百度搜索框
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> *{margin: 0px;padding: 0px;} #sch{width:200px;margin: 50px auto;} #tex{width: 200px;height: 20px;} ul{border: 1px solid #eee;width: 202px;display: inline-block;} ul li{list-style: none;border: 1px solid #ddd;width:200px;height: 20px;text-align: left;line-height: 20px;} ul li:hover{background: orange;} </style> </head> <body> <div id = 'sch'> <input type="text" placeholder='请输入关键字' id = 'tex'> <ul id="test_ul"> <!-- <li>1</li> <li>2</li> <li>3</li> <li>4</li> --> </ul> </div> <script src="../../static/jquery.js"></script> <script> // 定义回调函数 function testcb(data){ var result = data["s"] $.each(result,function(index,item){ $("<li>").html(item).appendTo("#test_ul") }); } </script> <script> // jsonp获取数据 function getData(keyword){ var params = {'wd': keyword, 'p': '3', //'cb': 'testcb', "req":2, "sid":"1426_21116_22074", "json":1, "csor":1,}; $.ajax({ async: false, //url: "http://suggestion.baidu.com/su", url: "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su", type: "GET", dataType: 'jsonp', jsonp: 'cb', jsonpCallback:"testcb", data: params, timeout: 5000, success: function (json) { console.log(json) }, error: function (xhr) { } }); } </script> <script> /* 使用input,keyup等都行 $("#tex").input(function(){ var mykeyword = $("#tex").val() if(mykeyword.length > 0){ getData(mykeyword) } }) */ $("#tex").on("input",function(){ $("#test_ul").empty() var mykeyword = $("#tex").val(); if (mykeyword.length>0){ getData(mykeyword) } }) </script> <script> // 网上其他人的做法 /* var oTex = document.getElementById('tex'); var oUl = document.getElementsByTagName('ul')[0]; function baiduSU(data){ var d = data.s if(d.length){ oUl.style.display = 'block'; var str = ''; for(var i =0;i<d.length;i++) { str += '<li>'+d[i]+'</li>' } oUl.innerHTML = str; }else{ oUl.style.display = 'none'; } } oTex.onkeyup = function() { if(oTex.value != '') { var oScript = document.createElement('script'); oScript.src = 'http://unionsug.baidu.com/su?wd='+oTex.value+'&cb=baiduSU'; document.body.appendChild(oScript); }else{ oUl.style.display = 'none'; } } */ </script> </body> </html>
过程
图中的url应该是会变的,后面使用python爬取该url
结果
爬取url
import requests import re url = "https://www.baidu.com" method = "GET" headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0", } params = {} req = requests.request(method=method,url=url,params=params,headers=headers) target = re.search("sugHost : (.+?),",req.text) print(target.group(1)[1:-1]) # https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su