摘要:域套接字使用或指定請求方法的字符串。請求路徑包含非法字符時拋出異常。保持資源池周圍的套接字在未來被用于其它請求。默認值為當使用的時候,通過正在保持活動的套接字發送包的頻繁程度。
前言文章來源:小青年原創
發布時間:2016-09-29
關鍵詞:JavaScript,nodejs,http,url ,Query String,爬蟲
轉載需標注本文原始地址: http://zhaomenghuan.github.io...
一直以來想學習一下node,一來是自己目前也沒有什么時間去學習服務器端語言,但是有時候又想自己擼一下服務器端,本著愛折騰的精神開始寫一寫關于node的文章記錄學習心得。本系列文章不會過多去講解node安裝、基本API等內容,而是通過一些實例去總結常用用法。本文主要講解node網絡操作的相關內容,node中的網絡操作依賴于http模塊,http模塊提供了兩種使用方式:
作為服務器端使用,創建一個http服務器,監聽http客戶端請求并返回響應;
作為客戶端使用,發起一個http客戶端請求,獲取服務器端響應。
node http模塊創建服務器 node 處理 get 請求實例畢竟作為一個前端,我們經常需要自己搭建一個服務器做測試,這里我們先來講一下node http模塊作為服務器端使用。首先我們需要,使用createServer創建一個服務,然后通過listen監聽客服端http請求。
我們可以創建一個最簡單的服務器,在頁面輸出hello world,我們可以創建helloworld.js,內容如下:
var http = require("http"); http.createServer(function(request, response){ response.writeHead(200, { "Content-Type": "text-plain" }); response.end("hello world!") }).listen(8888);
在命令行輸入node helloworld.js即可,我們打開在瀏覽器打開http://127.0.0.1:8888/就可以看到頁面輸出hello world!。
下面我們在本地寫一個頁面,通過jsonp訪問我們創建的node服務器:
我們當然需要將上述node服務器中的代碼稍作修改:
var http = require("http"); // 提供web服務 var url = require("url"); // 解析GET請求 var data = { "name": "zhaomenghuan", "age": "22" }; http.createServer(function(req, res){ // 將url字符串轉換成Url對象 var params = url.parse(req.url, true); console.log(params); // 查詢參數 if(params.query){ // 根據附件條件查詢 if(params.query.userid === "xiaoqingnian"){ // 判斷是否為jsonp方式請求,若是則使用jsonp方式,否則為普通web方式 if (params.query.callback) { var resurlt = params.query.callback + "(" + JSON.stringify(data) + ")"; res.end(resurlt); } else { res.end(JSON.stringify(data)); } } } }).listen(8888);
我們在命令行可以看到:
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: "?userid=xiaoqingnian&callback=jsonpcallback", query: { userid: "xiaoqingnian", callback: "jsonpcallback" }, pathname: "/", path: "/?userid=xiaoqingnian&callback=jsonpcallback", href: "/?userid=xiaoqingnian&callback=jsonpcallback" }
經過服務器端jsonp處理,然后返回一個函數:
jsonpcallback({"name":"zhaomenghuan","age":"22"})
而我們在頁面中定義了一個jsonpcallback()的方法,所以當我們在請求頁面動態生成script調用服務器地址,這樣相當于在頁面執行了下我們定義的函數。jsonp的實現原理主要是script標簽src可以跨域執行代碼,類似于你引用js庫,然后調用這個js庫里面的方法;這是這里我們可以認為反過來了,你是在本地定義函數,調用的邏輯通過服務器返回的一個函數執行了,所以jsonp并沒有什么神奇的,和XMLHttpRequest、ajax半毛錢關系都沒有,而且JSONP需要服務器端支持,始終是無狀態連接,不能獲悉連接狀態和錯誤事件,而且只能走GET的形式。
node 處理 post 請求實例當然這里我們可以直接在后臺設置響應頭進行跨域(CORS),如:
var http = require("http"); // 提供web服務 var query = require("querystring"); // 解析POST請求 http.createServer(function(req,res){ // 報頭添加Access-Control-Allow-Origin標簽,值為特定的URL或"*"(表示允許所有域訪問當前域) res.setHeader("Access-Control-Allow-Origin","*"); var postdata = ""; // 一旦監聽器被添加,可讀流會觸發 "data" 事件 req.addListener("data",function(chunk){ postdata += chunk; }) // "end" 事件表明已經得到了完整的 body req.addListener("end",function(){ console.log(postdata); // "appid=xiaoqingnian" // 將接收到參數串轉換位為json對象 var params = query.parse(postdata); if(params.userid == "xiaoqingnian"){ res.end("{"name":"zhaomenghuan","age":"22"}"); } }) }).listen(8080);
我們通過流的形式接收前端post傳遞的參數,通過監聽data和end事件,后面在講解event模塊的時候再深入探究。
CORS默認只支持GET/POST這兩種http請求類型,如果要開啟PUT/DELETE之類的方式,需要在服務端在添加一個"Access-Control-Allow-Methods"報頭標簽:
res.setHeader( "Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, HEAD, PATCH" );
前端訪問代碼如下:
var xhr = new XMLHttpRequest(); xhr.onload = function () { console.log(this.responseText); }; xhr.onreadystatechange = function() { console.log(this.readyState); }; xhr.open("post", "http://127.0.0.1:8080", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send("userid=xiaoqingnian");url 模塊API詳解 url.parse——解析url字符串
上述代碼中比較關鍵的是我們通過url.parse方法將url字符串轉成Url對象,用法如下:
url.parse(urlStr, [parseQueryString], [slashesDenoteHost])
接收參數:
urlStr:url字符串
parseQueryString:參數為true時,query會被解析為JSON格式,否則為普通字符串格式,默認為false;如:
參數為true:query: { userid: "xiaoqingnian", callback: "jsonpcallback" }
參數為false:query: "userid=xiaoqingnian&callback=jsonpcallback"
slashesDenoteHost:默認為false,當url是 ‘http://’ 或 ‘ftp://’ 等標志的協議前綴打頭的,或直接以地址打頭,如 ‘127.0.0.1’ 或 ‘localhost’ 時候是沒有區別的;當且僅當以2個斜杠打頭的時候,比如 ‘//127.0.0.1’ 才有區別。這時候,如果其值為true,則第一個單個 ‘/’ 之前的部分被解析為 ‘host’ 和 ‘hostname’,如 ” host : ‘127.0.0.1’ “,如果為false,包括2個反斜杠在內的所有字符串被解析為pathname。如:
> url.parse("http://www.foo/bar",true,true) Url { protocol: null, slashes: true, auth: null, host: "www.foo", port: null, hostname: "www.foo", hash: null, search: "", query: {}, pathname: "/bar", path: "/bar", href: "http://www.foo/bar" } > url.parse("http://www.foo/bar",true,false) Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: "", query: {}, pathname: "http://www.foo/bar", path: "http://www.foo/bar", href: "http://www.foo/bar" }
這里的URL對象和瀏覽器中的location對象類似,location中如果我們需要使用類似的方法,我們需要自己構造。
url.format——格式化URL對象我們可以通過url.format方法將一個解析后的URL對象格式化成url字符串,用法為:
url.format(urlObj)
例子:
url.format({ protocol: "http:", slashes: true, auth: "user:pass", host: "host.com:8080", port: "8080", hostname: "host.com", hash: "#hash", search: "?query=string", query: "query=string", pathname: "/p/a/t/h", path: "/p/a/t/h?query=string", href: "http://user:pass@host.com:8080/p/a/t/h?query=string#hash" }) 結果為: "http://user:pass@host.com:8080/p/a/t/h?query=string#hash"url.resolve——拼接url字符串
我們可以通過url.resolve為URL或 href 插入 或 替換原有的標簽,接收參數:
from源地址,to需要添加或替換的標簽。
url.resolve(from, to)
例子為:
url.resolve("/one/two/three", "four") => "/one/two/four" url.resolve("http://example.com/", "/one") => "http://example.com/one" url.resolve("http://example.com/one", "/two") => "http://example.com/two"Query String 模塊Query String querystring.escape——字符串編碼
querystring.escape("appkey=123&version=1.0.0+") // "appkey%3D123%26version%3D1.0.0%2B"querystring.unescape——字符串解碼
querystring.unescape("appkey%3D123%26version%3D1.0.0%2B") // "appkey=123&version=1.0.0+"querystring.stringify(querystring.encode)——序列化對象
querystring.stringify(obj[, sep][, eq][, options]) querystring.encode(obj[, sep][, eq][, options])
接收參數:
obj: 欲轉換的對象
sep:設置分隔符,默認為 ‘&"
eq:設置賦值符,默認為 ‘="
querystring.stringify({foo: "bar", baz: ["qux", "quux"], corge: ""}) // "foo=bar&baz=qux&baz=quux&corge=" querystring.stringify({foo: "bar", baz: ["qux", "quux"], corge: ""},",",":") // "foo:bar,baz:qux,baz:quux,corge:"querystring.parse(querystring.decode)——解析query字符串
querystring.parse(str[, sep][, eq][, options]) querystring.decode(str[, sep][, eq][, options])
接收參數:
str:欲轉換的字符串
sep:設置分隔符,默認為 ‘&"
eq:設置賦值符,默認為 ‘="
[options] maxKeys 可接受字符串的最大長度,默認為1000
querystring.parse("foo=bar&baz=qux&baz=quux&corge=") // { foo: "bar", baz: [ "qux", "quux" ], corge: "" } querystring.parse("foo:bar,baz:qux,baz:quux,corge:",",",":") { foo: "bar", baz: [ "qux", "quux" ], corge: "" }node http模塊發起請求
平時喜歡看博客,畢竟買書要錢而且有時候沒有耐心讀完整本書,所以很喜歡逛一些網站,但是很多時候把所有的站逛一下又沒有那么多時間,哈哈,所以就準備把常去的網站的文章爬出來做一個文章列表,一來省去收集的時間,二來借此熟悉熟悉node相關的東西。這里我們首先看一個爬蟲的小例子,下面以SF為例加以說明(希望不要被封號)。
http.request與http.get的區別 http.request(options, callback)options可以是一個對象或一個字符串。如果options是一個字符串, 它將自動使用url.parse()解析。http.request() 返回一個 http.ClientRequest類的實例。ClientRequest實例是一個可寫流對象。如果需要用POST請求上傳一個文件的話,就將其寫入到ClientRequest對象。使用http.request()方法時都必須總是調用req.end()以表明這個請求已經完成,即使響應body里沒有任何數據。如果在請求期間發生錯誤(DNS解析、TCP級別的錯誤或實際HTTP解析錯誤),在返回的請求對象會觸發一個"error"事件。
Options配置說明:
host:請求發送到的服務器的域名或IP地址。默認為"localhost"。
hostname:用于支持url.parse()。hostname比host更好一些
port:遠程服務器的端口。默認值為80。
localAddress:用于綁定網絡連接的本地接口。
socketPath:Unix域套接字(使用host:port或socketPath)
method:指定HTTP請求方法的字符串。默認為"GET"。
path:請求路徑。默認為"/"。如果有查詢字符串,則需要包含。例如"/index.html?page=12"。請求路徑包含非法字符時拋出異常。目前,只否決空格,不過在未來可能改變。
headers:包含請求頭的對象。
auth:用于計算認證頭的基本認證,即"user:password"
agent:控制Agent的行為。當使用了一個Agent的時候,請求將默認為Connection: keep-alive。可能的值為:
undefined(默認):在這個主機和端口上使用[全局Agent][]。
Agent對象:在Agent中顯式使用passed。
false:在對Agent進行資源池的時候,選擇停用連接,默認請求為:Connection: close。
keepAlive:{Boolean} 保持資源池周圍的套接字在未來被用于其它請求。默認值為false
keepAliveMsecs:{Integer} 當使用HTTP KeepAlive的時候,通過正在保持活動的套接字發送TCP KeepAlive包的頻繁程度。默認值為1000。僅當keepAlive被設置為true時才相關。
http.get(options, callback)因為大部分的請求是沒有報文體的GET請求,所以Node提供了這種便捷的方法。該方法與http.request()的唯一區別是它設置的是GET方法并自動調用req.end()。
爬蟲實例這里我們使用es6的新特性寫:
const https = require("https"); https.get("https://segmentfault.com/blogs", (res) => { console.log("statusCode: ", res.statusCode); console.log("headers: ", res.headers); var data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("end", () => { console.log(data); }) }).on("error", (e) => { console.error(e); });
這樣一小段代碼我們就可以拿到segmentfault的博客頁面的源碼,需要說明的是因為這里請求的網站是https協議,所以我們需要引入https模塊,用法同http一致。下面需要做的是解析html代碼,下面我們需要做的就是解析源碼,這里我們可以引入cheerio,一個node版的類jQuery模塊,npm地址:https://www.npmjs.com/package...。
首先第一步安裝:
npm install cheerio
然后就是將html代碼load進來,如下:
var cheerio = require("cheerio"), var $ = cheerio.load(html);
最后我們就是分析dom結構咯,通過類似于jQuery的方法獲取DOM元素的內容,然后就將數據重新組裝成json結構的數據。這里就是分析源碼然后,這里我就不詳細分析了,直接上代碼:
function htmlparser(html){ var baseUrl = "https://segmentfault.com"; var $ = cheerio.load(html); var bloglist = $(".stream-list__item"); var data = []; bloglist.each(function(item){ var page = $(this); var summary = page.find(".summary"); var blogrank = page.find(".blog-rank"); var title = summary.find(".title a").text(); var href = baseUrl + summary.find(".title a").attr("href"); var author = summary.find(".author li a").first().text().trim(); var origin = summary.find(".author li a").last().text().trim(); var time = summary.find(".author li span")[0].nextSibling.data.trim(); var excerpt = summary.find("p.excerpt").text().trim(); var votes = blogrank.find(".votes").text().trim(); var views = blogrank.find(".views").text().trim(); data.push({ title: title, href: href, author: author, origin: origin, time: time, votes: votes, views: views, excerpt: excerpt }) }) return data; }
結果如下:
[{ title: "轉換流", href: "https://segmentfault.com/a/1190000007036273", author: "SwiftGG翻譯組", origin: "SwiftGG翻譯組", time: "1 小時前", votes: "0推薦", views: "14瀏覽", excerpt: "作者:Erica Sadun,原文鏈接,原文日期:2016-08-29譯者:Darren;校對:shank s;定稿:千葉知風 我在很多地方都表達了我對流的喜愛。我在 Swift Cookbook 中介紹了一些。現 在,我將通過 Pearson 的內容更新計劃..." }, ...... ]
這里我們只是抓取了文章列表的一頁,如果需要抓取多頁,只需要將內容再次封裝一下,傳入一個地址參數?page=2,如:https://segmentfault.com/blog...
另外我們也沒有將詳情頁進一步爬蟲,畢竟文章的目的只是學習,同時方便自己查看列表,這里保留原始地址。
溫馨提示:大家不要都拿sf做測試哦,不然玩壞了就不好。
模擬登陸哈哈,寫到這里已經很晚了,用node試了試模擬登陸SF,結果404,暫時沒有什么思路,等有時間再試試專門開篇講解咯。這里推薦一篇之前看到的文章:記一次用 NodeJs 實現模擬登錄的思路。
參考nodejs官網API文檔
進擊Node.js基礎(一)
Node.js v4.2.4 文檔 中文版
文章代碼源碼下載:https://github.com/zhaomenghu...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84564.html
摘要:百度搜索資源平臺有閃電算法的支持,為了能夠保障用戶體驗,給予優秀站點更多面向用戶的機會,閃電算法在年月初上線。下欄是每一個指標的細化性能評估。最后優化之路漫漫,永無止境,天下武功,唯快不破。 showImg(https://segmentfault.com/img/remote/1460000018537491); 首屏作為直面用戶的第一屏,其重要性不言而喻,如何加快加載的速度是非常重...
摘要:我是一個知乎輕微重度用戶,之前寫了一只爬蟲幫我爬取并分析它的數據,我感覺這個過程還是挺有意思,因為這是一個不斷給自己創造問題又去解決問題的過程。所以這只爬蟲還有登陸知乎搜索題目的功能。 我一直覺得,爬蟲是許多web開發人員難以回避的點。我們也應該或多或少的去接觸這方面,因為可以從爬蟲中學習到web開發中應當掌握的一些基本知識。而且,它還很有趣。 我是一個知乎輕微重度用戶,之前寫了一只爬...
摘要:創建客戶端對象與服務器的參數屬性一樣此時端口有下邊的幾個屬性連接另一端所使用的遠程地址連接另一端所使用的端口號本地用于建立連接的地址本地用于建立連接的端口號端口對象可以被用來寫入向客戶端或服務器端發送的流數據當流數據被寫入后將立即發送到客戶 1. 創建TCP客戶端 const net = require(net); let socket = new net.Socket([option...
前言 在若干次前的一場面試,面試官看我做過python爬蟲/后端 的工作,順帶問了我些后端相關的問題:你覺得什么是后端? 送命題。當時腦瓦特了,答曰:邏輯處理和數據增刪改查。。。 showImg(https://user-gold-cdn.xitu.io/2019/4/24/16a4ed4fc8c18078); 當場被懟得體無完膚,羞愧難當。事后再反思這問題,結合資料總結了一下。發現自己學過的Re...
摘要:對象表示請求并且具有請求查詢字符串參數正文標題頭等屬性對應用程序實例的引用保存了很多對使用中間件的應用程序實例的引用掛載在路由實例上的路徑請求主體和和包含在請求正文中提交的數據的鍵值對默認情況下它是未定義的當您使用體解析中間件如和時將被填 2. request req對象表示http請求,并且具有請求查詢字符串,參數,正文,http標題頭等屬性 app.get(/user/:id, ...
閱讀 2256·2021-11-25 09:43
閱讀 3123·2021-10-14 09:42
閱讀 3484·2021-10-12 10:12
閱讀 1526·2021-09-07 10:17
閱讀 1901·2019-08-30 15:54
閱讀 3181·2019-08-30 15:54
閱讀 1550·2019-08-30 15:53
閱讀 1908·2019-08-29 11:21