整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          如何利用python+selenium+ajax-hook抓取網(wǎng)頁后臺返回的json數(shù)據(jù)

          章開始之前,我們先來看一個常見的問題:

          接到一個任務(wù),需要抓取某個網(wǎng)站上的數(shù)據(jù)內(nèi)容,網(wǎng)頁上需要輸入搜索關(guān)鍵詞,然后點擊搜索按鈕,等待頁面加載完畢,獲取網(wǎng)頁上的搜索結(jié)果,而每一個搜索結(jié)果項,都需要點擊展開才能查看到具體內(nèi)容。

          對于該問題,我們可以從網(wǎng)上找到一些解決問題的途徑,但是大都不是很全面。這里小編對所有可能出現(xiàn)的問題做了一次調(diào)研,并匯總成如下的解決方案,希望對大家有所幫助。

          首先,我們先來匯總一下python生態(tài)下爬取網(wǎng)站數(shù)據(jù)需要用到的工具。

          1,selenium chromedriver, 這個標(biāo)準(zhǔn)的自動化工具,可以幫助我們從后臺操控google瀏覽器,并能模擬鼠標(biāo)移動和點擊事件,配合xpath快速定位網(wǎng)頁元素,從根本上解放了我們的雙手。

          2,ajax-hook, 這個是用來攔截xhr的鉤子,可以快速獲取網(wǎng)站服務(wù)器的響應(yīng)內(nèi)容,而無需我們被動地從網(wǎng)頁上來獲取。

          明確了我們處理任務(wù)的可用資源,下一步就是各種踩坑了。

          這里小編先列舉一下,我所遇到的坑:

          1,selenium的webdriver加載網(wǎng)頁,怎么判斷頁面是否加載完全,使用

          wait = WebDriverWait(self.driver, timeout=10)

          wait.until(lambda my_driver: my_driver.execute_script('return document.readyState') == 'complete')

          發(fā)現(xiàn)這種方案不可行,遇到動態(tài)網(wǎng)頁異步加載就gameover,不通用,可行的方案應(yīng)該wait for webelement loaded,也就是等待你所關(guān)注的網(wǎng)頁元素加載完畢才行。

          2,xpath的相對路徑查找,是帶了position的向下查找,也就是說每次調(diào)用xpath find, 其position就往下移動一次。所以如果用xpath在同一個流程調(diào)用多次只有第一次可以找到元素,后面都是失敗的。所以對于需要反復(fù)查找的元素,最好要用絕對路徑,獲取xpath絕對路徑很簡單

          打開瀏覽器的開發(fā)者工具,點擊左上角的小箭頭,移動鼠標(biāo)到具體某個按鈕或者輸入框點擊一下就可以定位到html的標(biāo)簽源代碼位置,鼠標(biāo)右鍵,Copy xpath即可

          3,怎么截獲并過濾xhr請求,將響應(yīng)內(nèi)容保存下來,而不會造成內(nèi)存暴漲。

          from selenium import webdriver

          from selenium.webdriver.chrome.options import Options

          from selenium.webdriver.support.ui import WebDriverWait

          chrome_options = Options()

          chrome_options.add_argument("--disable-extensions")

          chrome_options.add_argument("--disable-gpu")

          chrome_options.add_argument("--headless")

          chrome_options.add_argument("--no-sandbox")

          prefs = {

          'download.default_directory': os.getenv('OS_LOG_PATH')

          }

          chrome_options.add_experimental_option('prefs', prefs)

          capabilities = webdriver.DesiredCapabilities().CHROME

          capabilities['acceptSslCerts'] = True

          driver = webdriver.Chrome(options=chrome_options, desired_capabilities=capabilities)

          我們先來初始化一下webdriver,網(wǎng)上有很多資料,但是都不是很全面,上面是我整理的初始化內(nèi)容,大家按照這個來,就不會有問題。

          截獲https/http響應(yīng)body內(nèi)容,網(wǎng)上大致有三種實現(xiàn)思路:

          1,開啟browser, performance log,再根據(jù)requestId,調(diào)用chrome-devtools protocal Network.getResponseBody, 獲取響應(yīng)body。但是問題來了,我們需要大量采集response,而performance log都是存在內(nèi)存中,還需要我們?nèi)藶榍謇恚⑶襭erformance log采集的信息很雜,沒有過濾的入口提供給開發(fā)者。而且最煩的是,還需要去解析log,需要為了查詢一個repsonse body,有時候需要人為過濾N行日志。。。。。(直接無語凝噎)

          2,使用代理,網(wǎng)上傳的最多的是browsermob-proxy,但是這個玩意很無語的是只提供response的統(tǒng)計信息,不返回實質(zhì)性的response body內(nèi)容,配置各種參數(shù)都不會返回content.text字段,氣得跳腳,去github上看源碼發(fā)現(xiàn),text被下放了,當(dāng)前穩(wěn)定版本都會省略這個參數(shù)。所以你辛辛苦苦安裝proxy,調(diào)整ssl參數(shù),在python中配置各種proxy har,設(shè)置captureContent,結(jié)果卻得不到response body,不自覺就口吐芬芳了。所以想要其返回text字段,你需要改它的源碼,改源碼不難,但是很煩。

          3,使用ajax-hook, 這個方案的思路是:在源網(wǎng)頁加載之前,實現(xiàn)一個XMLHttpRequest的代理對象,然后覆蓋全局的XMLHttpRequest,這樣一但上層調(diào)用 new XMLHttpRequest這樣的代碼時,其實創(chuàng)建的是Ajax-hook的代理對象實例

          我們先新建一個hook.js腳本

          !function(t,e){for(var n in e)t[n]=e[n]}(window,function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){"use strict";function r(t,e){var n={};for(var r in t)n[r]=t[r];return n.target=n.currentTarget=e,n}function o(t){function e(e){return function(){var n=this.hasOwnProperty(e+"_")?this[e+"_"]:this.xhr[e],r=(t[e]||{}).getter;return r&&r(n,this)||n}}function n(e){return function(n){var o=this.xhr,i=this,u=t[e];if("on"===e.substring(0,2))i[e+"_"]=n,o[e]=function(u){u=r(u,i),t[e]&&t[e].call(i,o,u)||n.call(i,u)};else{var s=(u||{}).setter;n=s&&s(n,i)||n,this[e+"_"]=n;try{o[e]=n}catch(t){}}}}function o(e){return function(){var n=[].slice.call(arguments);if(t[e]){var r=t[e].call(this,n,this.xhr);if(r)return r}return this.xhr[e].apply(this.xhr,n)}}return window[s]=window[s]||XMLHttpRequest,XMLHttpRequest=function(){var t=new window[s];for(var r in t){var i="";try{i=u(t[r])}catch(t){}"function"===i?this[r]=o(r):Object.defineProperty(this,r,{get:e(r),set:n(r),enumerable:!0})}var a=this;t.getProxy=function(){return a},this.xhr=t},window[s]}function i(){window[s]&&(XMLHttpRequest=window[s]),window[s]=void 0}Object.defineProperty(e,"__esModule",{value:!0});var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};e.configEvent=r,e.hook=o,e.unHook=i;var s="_rxhr"},function(t,e,n){"use strict";function r(t){if(h)throw"Proxy already exists";return h=new f(t)}function o(){h=null,(0,d.unHook)()}function i(t){return t.replace(/^\s+|\s+$/g,"")}function u(t){return t.watcher||(t.watcher=document.createElement("a"))}function s(t,e){var n=t.getProxy(),r="on"+e+"_",o=(0,d.configEvent)({type:e},n);n[r]&&n[r](o);var i;"function"==typeof Event?i=new Event(e,{bubbles:!1}):(i=document.createEvent("Event"),i.initEvent(e,!1,!0)),u(t).dispatchEvent(i)}function a(t){this.xhr=t,this.xhrProxy=t.getProxy()}function c(t){function e(t){a.call(this,t)}return e[b]=Object.create(a[b]),e[b].next=t,e}function f(t){function e(t,e){var n=new P(t);if(!f)return n.resolve();var r={response:e.response,status:e.status,statusText:e.statusText,config:t.config,headers:t.resHeader||t.getAllResponseHeaders().split("\r\n").reduce(function(t,e){if(""===e)return t;var n=e.split(":");return t[n.shift()]=i(n.join(":")),t},{})};f(r,n)}function n(t,e,n){var r=new H(t),o={config:t.config,error:n};h?h(o,r):r.next(o)}function r(){return!0}function o(t,e){return n(t,this,e),!0}function a(t,n){return 4===t.readyState&&0!==t.status?e(t,n):4!==t.readyState&&s(t,w),!0}var c=t.onRequest,f=t.onResponse,h=t.onError;return(0,d.hook)({onload:r,onloadend:r,onerror:o,ontimeout:o,onabort:o,onreadystatechange:function(t){return a(t,this)},open:function(t,e){var r=this,o=e.config={headers:{}};o.method=t[0],o.url=t[1],o.async=t[2],o.user=t[3],o.password=t[4],o.xhr=e;var i="on"+w;e[i]||(e[i]=function(){return a(e,r)});var u=function(t){n(e,r,(0,d.configEvent)(t,r))};if([x,y,g].forEach(function(t){var n="on"+t;e[n]||(e[n]=u)}),c)return!0},send:function(t,e){var n=e.config;if(n.withCredentials=e.withCredentials,n.body=t[0],c){var r=function(){c(n,new m(e))};return!1===n.async?r():setTimeout(r),!0}},setRequestHeader:function(t,e){return e.config.headers[t[0].toLowerCase()]=t[1],!0},addEventListener:function(t,e){var n=this;if(-1!==l.indexOf(t[0])){var r=t[1];return u(e).addEventListener(t[0],function(e){var o=(0,d.configEvent)(e,n);o.type=t[0],o.isTrusted=!0,r.call(n,o)}),!0}},getAllResponseHeaders:function(t,e){var n=e.resHeader;if(n){var r="";for(var o in n)r+=o+": "+n[o]+"\r\n";return r}},getResponseHeader:function(t,e){var n=e.resHeader;if(n)return n[(t[0]||"").toLowerCase()]}})}Object.defineProperty(e,"__esModule",{value:!0}),e.proxy=r,e.unProxy=o;var h,d=n(0),l=["load","loadend","timeout","error","readystatechange","abort"],v=l[0],p=l[1],y=l[2],x=l[3],w=l[4],g=l[5],b="prototype";a[b]=Object.create({resolve:function(t){var e=this.xhrProxy,n=this.xhr;e.readyState=4,n.resHeader=t.headers,e.response=e.responseText=t.response,e.statusText=t.statusText,e.status=t.status,s(n,w),s(n,v),s(n,p)},reject:function(t){this.xhrProxy.status=0,s(this.xhr,t.type),s(this.xhr,p)}});var m=c(function(t){var e=this.xhr;t=t||e.config,e.withCredentials=t.withCredentials,e.open(t.method,t.url,!1!==t.async,t.user,t.password);for(var n in t.headers)e.setRequestHeader(n,t.headers[n]);e.send(t.body)}),P=c(function(t){this.resolve(t)}),H=c(function(t){this.reject(t)})},,function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.ah=void 0;var r=n(0),o=n(1);e.ah={proxy:o.proxy,unProxy:o.unProxy,hook:r.hook,unHook:r.unHook}}]));

          !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new s(e),n=i(s.prototype.request,t);return o.extend(n,s.prototype,t),o.extend(n,t),n}var o=n(2),i=n(3),s=n(4),a=n(22),u=n(10),c=r(u);c.Axios=s,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"undefined"==typeof e}function i(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function s(e){return"[object ArrayBuffer]"===j.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){return"[object Date]"===j.call(e)}function l(e){return"[object File]"===j.call(e)}function h(e){return"[object Blob]"===j.call(e)}function m(e){return"[object Function]"===j.call(e)}function y(e){return p(e)&&m(e.pipe)}function g(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function v(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function w(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.call(null,e[i],i,e)}function b(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=b(t[n],e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)w(arguments[n],e);return t}function E(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=E(t[n],e):"object"==typeof e?t[n]=E({},e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)w(arguments[n],e);return t}function S(e,t,n){return w(t,function(t,r){n&&"function"==typeof t?e[r]=C(t,n):e[r]=t}),e}var C=n(3),j=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:s,isBuffer:i,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isUndefined:o,isDate:d,isFile:l,isBlob:h,isFunction:m,isStream:y,isURLSearchParams:g,isStandardBrowserEnv:x,forEach:w,merge:b,deepMerge:E,extend:S,trim:v}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new s,response:new s}}var o=n(2),i=n(5),s=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),i(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(o.isURLSearchParams(t))i=t.toString();else{var s=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),s.push(r(t)+"="+r(e))}))}),i=s.join("&")}if(i){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+i}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),i=n(8),s=n(9),a=n(10);e.exports=function(e){r(e),e.headers=e.headers||{},e.data=i(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=i(t.data,t.headers,e.transformResponse),t},function(t){return s(t)||(r(e),t&&t.response&&(t.response.data=i(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!i.isUndefined(e)&&i.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof XMLHttpRequest?e=n(12):"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)&&(e=n(12)),e}var i=n(2),s=n(11),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return s(t,"Accept"),s(t,"Content-Type"),i.isFormData(e)||i.isArrayBuffer(e)||i.isBuffer(e)||i.isStream(e)||i.isFile(e)||i.isBlob(e)?e:i.isArrayBufferView(e)?e.buffer:i.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):i.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},i.forEach(["delete","get","head"],function(e){u.headers[e]={}}),i.forEach(["post","put","patch"],function(e){u.headers[e]=i.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(13),i=n(5),s=n(16),a=n(19),u=n(20),c=n(14);e.exports=function(e){return new Promise(function(t,f){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password||"";d.Authorization="Basic "+btoa(h+":"+m)}var y=s(e.baseURL,e.url);if(l.open(e.method.toUpperCase(),i(y,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l.onreadystatechange=function(){if(l&&4===l.readyState&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in l?a(l.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:r,status:l.status,statusText:l.statusText,headers:n,config:e,request:l};o(t,f,i),l=null}},l.onabort=function(){l&&(f(c("Request aborted",e,"ECONNABORTED",l)),l=null)},l.onerror=function(){f(c("Network Error",e,null,l)),l=null},l.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),f(c(t,e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=n(21),v=(e.withCredentials||u(y))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),r.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),f(e),l=null)}),void 0===p&&(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(17),o=n(18);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,s={};return e?(r.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?s[t]=(s[t]?s[t]:[]).concat([n]):s[t]=s[t]?s[t]+", "+n:n}}),s):s}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),s===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={},o=["url","method","params","data"],i=["headers","auth","proxy"],s=["baseURL","url","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"];r.forEach(o,function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(i,function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(s,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])});var a=o.concat(i).concat(s),u=Object.keys(t).filter(function(e){return a.indexOf(e)===-1});return r.forEach(u,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])});

          (function(console){

          console.save = function(data, filename){

          if(!data) {

          console.error('Console.save: No data')

          return;

          }

          if(!filename) filename = 'console.json'

          if(typeof data === "object"){

          data = JSON.stringify(data, undefined, 4)

          }

          var blob = new Blob([data], {type: 'text/json'}),

          e = document.createEvent('MouseEvents'),

          a = document.createElement('a')

          a.download = filename

          a.href = window.URL.createObjectURL(blob)

          a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')

          e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)

          a.dispatchEvent(e)

          }

          })(console)

          ah.proxy({

          //請求發(fā)起前進入

          onRequest: (config, handler) => {

          console.log(config.url)

          handler.next(config);

          },

          //請求發(fā)生錯誤時進入,比如超時;注意,不包括http狀態(tài)碼錯誤,如404仍然會認為請求成功

          onError: (err, handler) => {

          console.log(err.type)

          handler.next(err)

          },

          //請求成功后進入

          onResponse: (response, handler) => {

          if (response.config.url.startsWith('api/apps') && response.config.url.endsWith('/search')) {

          timestamp = new Date().getTime().toString()

          console.save(response.response, timestamp+'.json')

          }

          handler.next(response)

          }

          })

          該hook.js是我改良之后的,可以拿來直接用,功能就是根據(jù)response.config.url過濾我們需要的請求,并將response body保存到本地的json文件中,注意到上文中webdriver初始化的字段中有個選項,download.default_directory

          prefs = {

          'download.default_directory': os.getenv('OS_LOG_PATH')

          }

          os.getenv('OS_LOGPATH')這個是獲取環(huán)境變量,環(huán)境變量的值為我們需要保存response body的文件夾路徑,每一個response body都是一個json文件。大功告成!!簡潔明了,不需要依賴于其他任何東西,就可以實現(xiàn)了,長舒一口氣。

          總結(jié)經(jīng)驗:

          1,不要盲目使用網(wǎng)上的源碼,也不要死扣一種解決方案,不要輕易就相信網(wǎng)上的博文資料而不親自實踐,人云亦云。

          2,解決問題最有效的途徑是:看清問題的本質(zhì),從問題的內(nèi)部挖掘出解決文圖的思路,拆解成更容易解決的小問題。當(dāng)一個問題關(guān)聯(lián)了太多變量因素時,要學(xué)會控制單一變量,用排除法一步步解決,省時省力,不容易走彎路。

          家好,我是Python進階者。

          背景介紹

          我們知道再爬蟲的過程中我們對于爬取到的網(wǎng)頁數(shù)據(jù)需要進行解析,因為大多數(shù)數(shù)據(jù)是不需要的,所以我們需要進行數(shù)據(jù)解析,常用的數(shù)據(jù)解析方式有正則表達式,xpath,bs4,這次我們來介紹一下另一個數(shù)據(jù)解析庫--jsonpath,在此之前我們需要先了解一下什么是json。


          一、初識Json

          JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式,它使得人們很容易的進行閱讀和編寫。同時也方便了機器進行解析和生成。適用于進行數(shù)據(jù)交互的場景,比如網(wǎng)站前臺與后臺之間的數(shù)據(jù)交互。


          Python 2.7及之后版本,自帶了JSON模塊,直接import json就可以使用了。

          官方文檔:http://docs.python.org/library/json.html

          Json在線解析網(wǎng)站:http://www.json.cn/#


          二、Json的基本使用

          簡介

          json簡單說就是javascript中的對象和數(shù)組,所以這兩種結(jié)構(gòu)就是對象和數(shù)組兩種結(jié)構(gòu),通過這兩種結(jié)構(gòu)可以表示各種復(fù)雜的結(jié)構(gòu);

          1. 對象:對象在js中表示為{ }括起來的內(nèi)容,數(shù)據(jù)結(jié)構(gòu)為 { key:value, key:value, ... }的鍵值對的結(jié)構(gòu),在面向?qū)ο蟮恼Z言中,key為對象的屬性,value為對應(yīng)的屬性值,所以很容易理解,取值方法為 對象.key 獲取屬性值,這個屬性值的類型可以是數(shù)字、字符串、數(shù)組、對象這幾種。
          2. 數(shù)組:數(shù)組在js中是中括號[ ]括起來的內(nèi)容,數(shù)據(jù)結(jié)構(gòu)為 ["Python", "javascript", "C++", ...],取值方式和所有語言中一樣,使用索引獲取,字段值的類型可以是 數(shù)字、字符串、數(shù)組、對象幾種。


          使用

          json模塊提供了四個功能:dumpsdumploadsload,用于字符串 和 python數(shù)據(jù)類型間進行轉(zhuǎn)換。

          把Json格式字符串解碼轉(zhuǎn)換成Python對象 從json到python的類型轉(zhuǎn)化對照如下:

          JSON

          Python

          object

          dict

          array

          list

          string

          unicode

          number(int)

          int,long

          number(real)

          float

          true(false)

          True(False)

          null

          None


          1.json.loads()

          import json
          
          
          strDict = '{"city": "廣州", "name": "小黑"}'
          
          
          r = json.loads(strDict) # json數(shù)據(jù)自動按Unicode存儲
          
          
          print(r)

          結(jié)果如下:

          {'city': '廣州', 'name': '小黑'}


          2. json.load()

          讀取文件中json形式的字符串元素 轉(zhuǎn)化成python類型

          import json
          s = json.load(open('test.json','r',encoding='utf-8'))
          print(s,type(s))

          結(jié)果如下:

          {'city': '廣州', 'name': '小黑'} <class 'dict'>


          3. json.dumps()

          實現(xiàn)python類型轉(zhuǎn)化為json字符串,返回一個str對象 把一個Python對象編碼轉(zhuǎn)換成Json字符串

          import json
          
          
          listStr = [1, 2, 3, 4]
          dictStr = {"city": "北京", "name": "大貓"}
          
          
          s1 = json.dumps(listStr)
          s2 = json.dumps(dictStr,ensure_ascii=False)
          
          
          print(s1,type(s1))
          print(s2)

          結(jié)果如下:

          [1, 2, 3, 4] <class 'str'>{"city": "北京", "name": "大貓"} <class 'str'>

          注意:

          1. json.dumps() 序列化時默認使用的ascii編碼
          2. 添加參數(shù) ensure_ascii=False 禁用ascii編碼,按utf-8編碼


          4. json.dump()

          將Python內(nèi)置類型序列化為json對象后寫入文件

          import json
          
          
          json_info = "{'age': '12'}"
          file = open('ceshi.json','w',encoding='utf-8')
          json.dump(json_info,file)

          結(jié)果如下:

          ceshii,json(目錄文件產(chǎn)生)



          三、JsonPath

          JsonPath 是一種信息抽取類庫,是從JSON文檔中抽取指定信息的工具,提供多種語言實現(xiàn)版本,包括:Javascript, Python, PHP 和 Java。

          JsonPath 對于 JSON 來說,相當(dāng)于 XPATH 對于 XML。

          下載地址:https://pypi.python.org/pypi/jsonpath

          安裝方法:點擊Download URL鏈接下載jsonpath,解壓之后執(zhí)行python setup.py install

          官方文檔:http://goessner.net/articles/JsonPath


          JsonPath與XPath語法對比

          Json結(jié)構(gòu)清晰,可讀性高,復(fù)雜度低,非常容易匹配,下表中對應(yīng)了XPath的用法。

          XPath

          JSONPath

          描述

          /

          $

          根節(jié)點

          .

          @

          現(xiàn)行節(jié)點

          /

          .or[]

          取子節(jié)點

          ..

          n/a

          取父節(jié)點,Jsonpath未支持

          //

          ..

          就是不管位置,選擇所有符合條件的條件

          *

          *

          匹配所有元素節(jié)點

          @

          n/a

          根據(jù)屬性訪問,Json不支持,因為Json是個Key-value遞歸結(jié)構(gòu),不需要。

          []

          []

          迭代器表示(可以在里邊做簡單的迭代操作,如數(shù)組下標(biāo),根據(jù)內(nèi)容選值等)

          |

          [,]

          支持迭代器中做多選。

          []

          ?()

          支持過濾操作.

          n/a

          ()

          支持表達式計算

          ()

          n/a

          分組,JsonPath不支持


          四、案例測試

          我們爬取淘票票官網(wǎng)的城市信息,保存為json文件,進行jsonpath語法測試,獲取所有城市名稱。

          請求

          import requests
          import time
          
          
          url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1632211792156_137&jsoncallback=jsonp138&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'
          
          
          headers = {
              'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36',
          } 
          
          
          res = requests.get(url,headers=headers)
          
          
          result = res.content.decode('utf-8')
          
          
          print(result) # xxx省略

          注意:

          headers里面的鍵值對最好都加上,還是有反爬的,該網(wǎng)站,這里為了簡便省去了;


          保存數(shù)據(jù)

          content = result.split('(')[1].split(')')[0] # 由于文件首尾的字符不需要需要剔除掉做字符串切割
          
          
          with open('tpp.json','w',encoding='utf-8')as fp:
              fp.write(content)

          打開json文件如下所示:


          解析數(shù)據(jù)

          這里我們獲取全部城市名稱

          import json
          import jsonpath
          
          
          obj = json.load(open('tpp.json','r',encoding='utf-8')) # 注意,這里是文件的形式,不能直接放一個文件名的字符串
          
          
          city_list = jsonpath.jsonpath(obj,'$..regionName') # 文件對象   jsonpath語法
          
          
          print(city_list)

          結(jié)果如下:


          五、總結(jié)

          我們知道json是一種常見的數(shù)據(jù)傳輸形式,所以對于爬取數(shù)據(jù)的數(shù)據(jù)解析,json的相關(guān)操作是比較重要的,能夠加快我們的數(shù)據(jù)提取效率,本文簡單介紹了json和jsonpath的相關(guān)操作,對于測試網(wǎng)站(淘票票)的json做了簡單的數(shù)據(jù)解析,感興趣的小伙伴可以把其他數(shù)據(jù)解析一下。

          JSON(JavaScript 對象標(biāo)注)是一種流行的輕量級數(shù)據(jù)交換格式,在網(wǎng)絡(luò)上已很常見。眾所周知,JSON 讓開發(fā)人員易于使用,又讓機器易于解析和生成。

          JSON 吸引了工具構(gòu)建者的注意,它們開發(fā)了用于重新格式化、驗證和解析 JSON 的眾多工具,這不足為奇。這些工具既有在 Web瀏覽器 中運行的在線實用程序,又有面向代碼編輯器和 IDE 的插件。

          平時開發(fā)中,服務(wù)之間的接口交互幾乎都使用 JSON 格式的數(shù)據(jù),而一個能夠?qū)?JSON 比較好可視化,并直觀的去查看數(shù)據(jù)的工具非常重要。

          今天給大家介紹幾款 JSON 工具:

          1、JSON Visio

          它可以在圖表上無縫地展示您的數(shù)據(jù),而無需重組任何內(nèi)容、直接粘貼或?qū)胛募?/span>

          地址:https://jsonvisio.com/editor

          2、JSONLint

          來自 CircleCell 的 JSONLint 是一款面向 JSON 的在線驗證和重新格式化工具。開發(fā)人員可以將 JSON 粘貼或輸入到編輯器中,或者輸入 URL。JSONLint 可以驗證“混亂”的 JSON 代碼,還可以解析。

          如果用戶將?reformat=compress 添加到 URL,JSONLint 還可以用作 JSON 壓縮工具。

          地址:https://jsonlint.com

          3、Code Beautify JSON

          Code Beautify JSON 工具包括 JSON查看器、JSON編輯器、JSON驗證器以及 JSON 到 HTML、JSON 到 XML 和 JSON 到 YAML 等轉(zhuǎn)換器。還提供了 Excel 到 JSON轉(zhuǎn)換器和JSON 縮小器。Code Beautify 還為XML、HTML、CSV、CSS、RSS、SQL、Base64 及其他數(shù)據(jù)格式和文件類型提供了在線腳本編輯器、美化器、縮小器和轉(zhuǎn)換器。

          這款工具非常強大,支持各種數(shù)據(jù)的相互轉(zhuǎn)換,在我平時的開發(fā)中,可以稱得上必備的工具,非常推薦。

          4、ijson

          ijson最初于2016年開發(fā),是一款擁有標(biāo)準(zhǔn) Python 迭代器界面的迭代 JSON 解析工具。最常見的用途是讓 ijson 從前綴以下的 JSON 流生成原生 Python 對象。ijson 提供實際解析的幾種實現(xiàn):基于 C 的 YAJL(另一種JSON庫)或 Python 后端。

          該工具主要用途為標(biāo)準(zhǔn)的 JSON 流生成原生 Python 對象,非常實用。

          地址:https://pypi.org/project/ijson

          5、jtc

          「Jtc」 的全稱是“JSON測試控制臺”,這個 CLI 工具用于提取、處理和轉(zhuǎn)換源 JSON。開發(fā)人員可以使用 jtc 從 源JSON 選擇一個或多個元素,立即對這些元素執(zhí)行操作,比如將選定的元素包裝到 新的JSON 中、過濾進出或更新元素。用戶界面允許使用單個命令進行大量更改。Linux 和 MacOS 編譯二進制文件可供下載。

          開源地址:https://github.com/ldn-softdev/jtc#compile-and-install-instructions

          6、JSON-Handle

          JSON-handle是一款對JSON格式的內(nèi)容進行瀏覽和編輯,以樹形圖樣式展現(xiàn)JSON文檔,并可實時編輯。實際開發(fā)工作中經(jīng)常用到j(luò)son數(shù)據(jù),那么就會有這樣一個需求:在谷歌瀏覽器中訪問URL地址返回的json數(shù)據(jù)能否按照json格式展現(xiàn)出來。

          這是一款瀏覽器插件,但是目前已經(jīng)被下架,擴展插件中搜索不到,只能下載后去添加。

          下載地址:http://jsonhandle.sinaapp.com/

          總結(jié)

          這六款免費的工具可以非常方便的處理開發(fā)中遇到的各種 JSON 轉(zhuǎn)換、可視化、壓縮及校驗問題,能夠極大提高效率,趕快試試吧!


          主站蜘蛛池模板: 亚洲日本乱码一区二区在线二产线 | 国产在线精品一区二区夜色| 亚洲av综合av一区| 日本亚洲国产一区二区三区| 日韩一区二区三区在线观看| 一区二区免费电影| 国模无码视频一区| 91精品国产一区| 国产亚洲欧洲Aⅴ综合一区| 亚洲高清一区二区三区| 亚洲av无码一区二区三区乱子伦| 亚洲丶国产丶欧美一区二区三区| 国产亚洲日韩一区二区三区| 麻豆AV一区二区三区久久| 亚州国产AV一区二区三区伊在| 国产免费一区二区三区| 无码乱人伦一区二区亚洲| 国产A∨国片精品一区二区| 日本精品一区二区三区在线视频一 | 国产视频一区二区| 78成人精品电影在线播放日韩精品电影一区亚洲 | 国产激情精品一区二区三区| 成人一区专区在线观看| 人妻av综合天堂一区| 在线精品亚洲一区二区| 亚洲香蕉久久一区二区三区四区| 一区二区国产在线观看| 日韩熟女精品一区二区三区| 精品视频一区二区三区免费| 91国在线啪精品一区| 中文字幕一区二区三区永久| 亚洲av区一区二区三| 久久成人国产精品一区二区| 一区二区三区免费视频网站| 香蕉免费看一区二区三区| 国产午夜精品一区理论片| 久久久老熟女一区二区三区| 日韩一区二区久久久久久| 成人H动漫精品一区二区| 成人影片一区免费观看| 无码精品一区二区三区免费视频 |