摘要:域套接字使用或指定請(qǐng)求方法的字符串。請(qǐng)求路徑包含非法字符時(shí)拋出異常。保持資源池周圍的套接字在未來被用于其它請(qǐng)求。默認(rèn)值為當(dāng)使用的時(shí)候,通過正在保持活動(dòng)的套接字發(fā)送包的頻繁程度。
前言文章來源:小青年原創(chuàng)
發(fā)布時(shí)間:2016-09-29
關(guān)鍵詞:JavaScript,nodejs,http,url ,Query String,爬蟲
轉(zhuǎn)載需標(biāo)注本文原始地址: http://zhaomenghuan.github.io...
一直以來想學(xué)習(xí)一下node,一來是自己目前也沒有什么時(shí)間去學(xué)習(xí)服務(wù)器端語言,但是有時(shí)候又想自己擼一下服務(wù)器端,本著愛折騰的精神開始寫一寫關(guān)于node的文章記錄學(xué)習(xí)心得。本系列文章不會(huì)過多去講解node安裝、基本API等內(nèi)容,而是通過一些實(shí)例去總結(jié)常用用法。本文主要講解node網(wǎng)絡(luò)操作的相關(guān)內(nèi)容,node中的網(wǎng)絡(luò)操作依賴于http模塊,http模塊提供了兩種使用方式:
作為服務(wù)器端使用,創(chuàng)建一個(gè)http服務(wù)器,監(jiān)聽http客戶端請(qǐng)求并返回響應(yīng);
作為客戶端使用,發(fā)起一個(gè)http客戶端請(qǐng)求,獲取服務(wù)器端響應(yīng)。
node http模塊創(chuàng)建服務(wù)器 node 處理 get 請(qǐng)求實(shí)例畢竟作為一個(gè)前端,我們經(jīng)常需要自己搭建一個(gè)服務(wù)器做測(cè)試,這里我們先來講一下node http模塊作為服務(wù)器端使用。首先我們需要,使用createServer創(chuàng)建一個(gè)服務(wù),然后通過listen監(jiān)聽客服端http請(qǐng)求。
我們可以創(chuàng)建一個(gè)最簡單的服務(wù)器,在頁面輸出hello world,我們可以創(chuàng)建helloworld.js,內(nèi)容如下:
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!。
下面我們?cè)诒镜貙懸粋€(gè)頁面,通過jsonp訪問我們創(chuàng)建的node服務(wù)器:
我們當(dāng)然需要將上述node服務(wù)器中的代碼稍作修改:
var http = require("http"); // 提供web服務(wù) var url = require("url"); // 解析GET請(qǐng)求 var data = { "name": "zhaomenghuan", "age": "22" }; http.createServer(function(req, res){ // 將url字符串轉(zhuǎn)換成Url對(duì)象 var params = url.parse(req.url, true); console.log(params); // 查詢參數(shù) if(params.query){ // 根據(jù)附件條件查詢 if(params.query.userid === "xiaoqingnian"){ // 判斷是否為jsonp方式請(qǐng)求,若是則使用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);
我們?cè)诿钚锌梢钥吹剑?/p>
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" }
經(jīng)過服務(wù)器端jsonp處理,然后返回一個(gè)函數(shù):
jsonpcallback({"name":"zhaomenghuan","age":"22"})
而我們?cè)陧撁嬷卸x了一個(gè)jsonpcallback()的方法,所以當(dāng)我們?cè)谡?qǐng)求頁面動(dòng)態(tài)生成script調(diào)用服務(wù)器地址,這樣相當(dāng)于在頁面執(zhí)行了下我們定義的函數(shù)。jsonp的實(shí)現(xiàn)原理主要是script標(biāo)簽src可以跨域執(zhí)行代碼,類似于你引用js庫,然后調(diào)用這個(gè)js庫里面的方法;這是這里我們可以認(rèn)為反過來了,你是在本地定義函數(shù),調(diào)用的邏輯通過服務(wù)器返回的一個(gè)函數(shù)執(zhí)行了,所以jsonp并沒有什么神奇的,和XMLHttpRequest、ajax半毛錢關(guān)系都沒有,而且JSONP需要服務(wù)器端支持,始終是無狀態(tài)連接,不能獲悉連接狀態(tài)和錯(cuò)誤事件,而且只能走GET的形式。
node 處理 post 請(qǐng)求實(shí)例當(dāng)然這里我們可以直接在后臺(tái)設(shè)置響應(yīng)頭進(jìn)行跨域(CORS),如:
var http = require("http"); // 提供web服務(wù) var query = require("querystring"); // 解析POST請(qǐng)求 http.createServer(function(req,res){ // 報(bào)頭添加Access-Control-Allow-Origin標(biāo)簽,值為特定的URL或"*"(表示允許所有域訪問當(dāng)前域) res.setHeader("Access-Control-Allow-Origin","*"); var postdata = ""; // 一旦監(jiān)聽器被添加,可讀流會(huì)觸發(fā) "data" 事件 req.addListener("data",function(chunk){ postdata += chunk; }) // "end" 事件表明已經(jīng)得到了完整的 body req.addListener("end",function(){ console.log(postdata); // "appid=xiaoqingnian" // 將接收到參數(shù)串轉(zhuǎn)換位為json對(duì)象 var params = query.parse(postdata); if(params.userid == "xiaoqingnian"){ res.end("{"name":"zhaomenghuan","age":"22"}"); } }) }).listen(8080);
我們通過流的形式接收前端post傳遞的參數(shù),通過監(jiān)聽data和end事件,后面在講解event模塊的時(shí)候再深入探究。
CORS默認(rèn)只支持GET/POST這兩種http請(qǐng)求類型,如果要開啟PUT/DELETE之類的方式,需要在服務(wù)端在添加一個(gè)"Access-Control-Allow-Methods"報(bào)頭標(biāo)簽:
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字符串
上述代碼中比較關(guān)鍵的是我們通過url.parse方法將url字符串轉(zhuǎn)成Url對(duì)象,用法如下:
url.parse(urlStr, [parseQueryString], [slashesDenoteHost])
接收參數(shù):
urlStr:url字符串
parseQueryString:參數(shù)為true時(shí),query會(huì)被解析為JSON格式,否則為普通字符串格式,默認(rèn)為false;如:
參數(shù)為true:query: { userid: "xiaoqingnian", callback: "jsonpcallback" }
參數(shù)為false:query: "userid=xiaoqingnian&callback=jsonpcallback"
slashesDenoteHost:默認(rèn)為false,當(dāng)url是 ‘http://’ 或 ‘ftp://’ 等標(biāo)志的協(xié)議前綴打頭的,或直接以地址打頭,如 ‘127.0.0.1’ 或 ‘localhost’ 時(shí)候是沒有區(qū)別的;當(dāng)且僅當(dāng)以2個(gè)斜杠打頭的時(shí)候,比如 ‘//127.0.0.1’ 才有區(qū)別。這時(shí)候,如果其值為true,則第一個(gè)單個(gè) ‘/’ 之前的部分被解析為 ‘host’ 和 ‘hostname’,如 ” host : ‘127.0.0.1’ “,如果為false,包括2個(gè)反斜杠在內(nèi)的所有字符串被解析為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對(duì)象和瀏覽器中的location對(duì)象類似,location中如果我們需要使用類似的方法,我們需要自己構(gòu)造。
url.format——格式化URL對(duì)象我們可以通過url.format方法將一個(gè)解析后的URL對(duì)象格式化成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:[email protected]:8080/p/a/t/h?query=string#hash" }) 結(jié)果為: "http://user:[email protected]:8080/p/a/t/h?query=string#hash"url.resolve——拼接url字符串
我們可以通過url.resolve為URL或 href 插入 或 替換原有的標(biāo)簽,接收參數(shù):
from源地址,to需要添加或替換的標(biāo)簽。
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)——序列化對(duì)象
querystring.stringify(obj[, sep][, eq][, options]) querystring.encode(obj[, sep][, eq][, options])
接收參數(shù):
obj: 欲轉(zhuǎn)換的對(duì)象
sep:設(shè)置分隔符,默認(rèn)為 ‘&"
eq:設(shè)置賦值符,默認(rèn)為 ‘="
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])
接收參數(shù):
str:欲轉(zhuǎn)換的字符串
sep:設(shè)置分隔符,默認(rèn)為 ‘&"
eq:設(shè)置賦值符,默認(rèn)為 ‘="
[options] maxKeys 可接受字符串的最大長度,默認(rèn)為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模塊發(fā)起請(qǐng)求
平時(shí)喜歡看博客,畢竟買書要錢而且有時(shí)候沒有耐心讀完整本書,所以很喜歡逛一些網(wǎng)站,但是很多時(shí)候把所有的站逛一下又沒有那么多時(shí)間,哈哈,所以就準(zhǔn)備把常去的網(wǎng)站的文章爬出來做一個(gè)文章列表,一來省去收集的時(shí)間,二來借此熟悉熟悉node相關(guān)的東西。這里我們首先看一個(gè)爬蟲的小例子,下面以SF為例加以說明(希望不要被封號(hào))。
http.request與http.get的區(qū)別 http.request(options, callback)options可以是一個(gè)對(duì)象或一個(gè)字符串。如果options是一個(gè)字符串, 它將自動(dòng)使用url.parse()解析。http.request() 返回一個(gè) http.ClientRequest類的實(shí)例。ClientRequest實(shí)例是一個(gè)可寫流對(duì)象。如果需要用POST請(qǐng)求上傳一個(gè)文件的話,就將其寫入到ClientRequest對(duì)象。使用http.request()方法時(shí)都必須總是調(diào)用req.end()以表明這個(gè)請(qǐng)求已經(jīng)完成,即使響應(yīng)body里沒有任何數(shù)據(jù)。如果在請(qǐng)求期間發(fā)生錯(cuò)誤(DNS解析、TCP級(jí)別的錯(cuò)誤或?qū)嶋HHTTP解析錯(cuò)誤),在返回的請(qǐng)求對(duì)象會(huì)觸發(fā)一個(gè)"error"事件。
Options配置說明:
host:請(qǐng)求發(fā)送到的服務(wù)器的域名或IP地址。默認(rèn)為"localhost"。
hostname:用于支持url.parse()。hostname比host更好一些
port:遠(yuǎn)程服務(wù)器的端口。默認(rèn)值為80。
localAddress:用于綁定網(wǎng)絡(luò)連接的本地接口。
socketPath:Unix域套接字(使用host:port或socketPath)
method:指定HTTP請(qǐng)求方法的字符串。默認(rèn)為"GET"。
path:請(qǐng)求路徑。默認(rèn)為"/"。如果有查詢字符串,則需要包含。例如"/index.html?page=12"。請(qǐng)求路徑包含非法字符時(shí)拋出異常。目前,只否決空格,不過在未來可能改變。
headers:包含請(qǐng)求頭的對(duì)象。
auth:用于計(jì)算認(rèn)證頭的基本認(rèn)證,即"user:password"
agent:控制Agent的行為。當(dāng)使用了一個(gè)Agent的時(shí)候,請(qǐng)求將默認(rèn)為Connection: keep-alive??赡艿闹禐椋?/p>
undefined(默認(rèn)):在這個(gè)主機(jī)和端口上使用[全局Agent][]。
Agent對(duì)象:在Agent中顯式使用passed。
false:在對(duì)Agent進(jìn)行資源池的時(shí)候,選擇停用連接,默認(rèn)請(qǐng)求為:Connection: close。
keepAlive:{Boolean} 保持資源池周圍的套接字在未來被用于其它請(qǐng)求。默認(rèn)值為false
keepAliveMsecs:{Integer} 當(dāng)使用HTTP KeepAlive的時(shí)候,通過正在保持活動(dòng)的套接字發(fā)送TCP KeepAlive包的頻繁程度。默認(rèn)值為1000。僅當(dāng)keepAlive被設(shè)置為true時(shí)才相關(guān)。
http.get(options, callback)因?yàn)榇蟛糠值恼?qǐng)求是沒有報(bào)文體的GET請(qǐng)求,所以Node提供了這種便捷的方法。該方法與http.request()的唯一區(qū)別是它設(shè)置的是GET方法并自動(dòng)調(diào)用req.end()。
爬蟲實(shí)例這里我們使用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的博客頁面的源碼,需要說明的是因?yàn)檫@里請(qǐng)求的網(wǎng)站是https協(xié)議,所以我們需要引入https模塊,用法同http一致。下面需要做的是解析html代碼,下面我們需要做的就是解析源碼,這里我們可以引入cheerio,一個(gè)node版的類jQuery模塊,npm地址:https://www.npmjs.com/package...。
首先第一步安裝:
npm install cheerio
然后就是將html代碼load進(jìn)來,如下:
var cheerio = require("cheerio"), var $ = cheerio.load(html);
最后我們就是分析dom結(jié)構(gòu)咯,通過類似于jQuery的方法獲取DOM元素的內(nèi)容,然后就將數(shù)據(jù)重新組裝成json結(jié)構(gòu)的數(shù)據(jù)。這里就是分析源碼然后,這里我就不詳細(xì)分析了,直接上代碼:
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; }
結(jié)果如下:
[{ title: "轉(zhuǎn)換流", href: "https://segmentfault.com/a/1190000007036273", author: "SwiftGG翻譯組", origin: "SwiftGG翻譯組", time: "1 小時(shí)前", votes: "0推薦", views: "14瀏覽", excerpt: "作者:Erica Sadun,原文鏈接,原文日期:2016-08-29譯者:Darren;校對(duì):shank s;定稿:千葉知風(fēng) 我在很多地方都表達(dá)了我對(duì)流的喜愛。我在 Swift Cookbook 中介紹了一些?,F(xiàn) 在,我將通過 Pearson 的內(nèi)容更新計(jì)劃..." }, ...... ]
這里我們只是抓取了文章列表的一頁,如果需要抓取多頁,只需要將內(nèi)容再次封裝一下,傳入一個(gè)地址參數(shù)?page=2,如:https://segmentfault.com/blog...
另外我們也沒有將詳情頁進(jìn)一步爬蟲,畢竟文章的目的只是學(xué)習(xí),同時(shí)方便自己查看列表,這里保留原始地址。
溫馨提示:大家不要都拿sf做測(cè)試哦,不然玩壞了就不好。
模擬登陸哈哈,寫到這里已經(jīng)很晚了,用node試了試模擬登陸SF,結(jié)果404,暫時(shí)沒有什么思路,等有時(shí)間再試試專門開篇講解咯。這里推薦一篇之前看到的文章:記一次用 NodeJs 實(shí)現(xiàn)模擬登錄的思路。
參考nodejs官網(wǎng)API文檔
進(jìn)擊Node.js基礎(chǔ)(一)
Node.js v4.2.4 文檔 中文版
文章代碼源碼下載:https://github.com/zhaomenghu...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84564.html
摘要:百度搜索資源平臺(tái)有閃電算法的支持,為了能夠保障用戶體驗(yàn),給予優(yōu)秀站點(diǎn)更多面向用戶的機(jī)會(huì),閃電算法在年月初上線。下欄是每一個(gè)指標(biāo)的細(xì)化性能評(píng)估。最后優(yōu)化之路漫漫,永無止境,天下武功,唯快不破。 showImg(https://segmentfault.com/img/remote/1460000018537491); 首屏作為直面用戶的第一屏,其重要性不言而喻,如何加快加載的速度是非常重...
摘要:我是一個(gè)知乎輕微重度用戶,之前寫了一只爬蟲幫我爬取并分析它的數(shù)據(jù),我感覺這個(gè)過程還是挺有意思,因?yàn)檫@是一個(gè)不斷給自己創(chuàng)造問題又去解決問題的過程。所以這只爬蟲還有登陸知乎搜索題目的功能。 我一直覺得,爬蟲是許多web開發(fā)人員難以回避的點(diǎn)。我們也應(yīng)該或多或少的去接觸這方面,因?yàn)榭梢詮呐老x中學(xué)習(xí)到web開發(fā)中應(yīng)當(dāng)掌握的一些基本知識(shí)。而且,它還很有趣。 我是一個(gè)知乎輕微重度用戶,之前寫了一只爬...
摘要:創(chuàng)建客戶端對(duì)象與服務(wù)器的參數(shù)屬性一樣此時(shí)端口有下邊的幾個(gè)屬性連接另一端所使用的遠(yuǎn)程地址連接另一端所使用的端口號(hào)本地用于建立連接的地址本地用于建立連接的端口號(hào)端口對(duì)象可以被用來寫入向客戶端或服務(wù)器端發(fā)送的流數(shù)據(jù)當(dāng)流數(shù)據(jù)被寫入后將立即發(fā)送到客戶 1. 創(chuàng)建TCP客戶端 const net = require(net); let socket = new net.Socket([option...
前言 在若干次前的一場(chǎng)面試,面試官看我做過python爬蟲/后端 的工作,順帶問了我些后端相關(guān)的問題:你覺得什么是后端? 送命題。當(dāng)時(shí)腦瓦特了,答曰:邏輯處理和數(shù)據(jù)增刪改查。。。 showImg(https://user-gold-cdn.xitu.io/2019/4/24/16a4ed4fc8c18078); 當(dāng)場(chǎng)被懟得體無完膚,羞愧難當(dāng)。事后再反思這問題,結(jié)合資料總結(jié)了一下。發(fā)現(xiàn)自己學(xué)過的Re...
摘要:對(duì)象表示請(qǐng)求并且具有請(qǐng)求查詢字符串參數(shù)正文標(biāo)題頭等屬性對(duì)應(yīng)用程序?qū)嵗囊帽4媪撕芏鄬?duì)使用中間件的應(yīng)用程序?qū)嵗囊脪燧d在路由實(shí)例上的路徑請(qǐng)求主體和和包含在請(qǐng)求正文中提交的數(shù)據(jù)的鍵值對(duì)默認(rèn)情況下它是未定義的當(dāng)您使用體解析中間件如和時(shí)將被填 2. request req對(duì)象表示http請(qǐng)求,并且具有請(qǐng)求查詢字符串,參數(shù),正文,http標(biāo)題頭等屬性 app.get(/user/:id, ...
閱讀 2277·2021-11-25 09:43
閱讀 3152·2021-10-14 09:42
閱讀 3500·2021-10-12 10:12
閱讀 1584·2021-09-07 10:17
閱讀 1916·2019-08-30 15:54
閱讀 3199·2019-08-30 15:54
閱讀 1572·2019-08-30 15:53
閱讀 1935·2019-08-29 11:21