標籤:
基本概念
電腦網路:把分布在不同地理地區的電腦與專門的外部裝置用通訊線路互連成一個規模大、功能強的網路系統,從而使眾多的電腦可以方便地互相傳遞資訊,共用硬體、軟體、資料資訊等資源。
電腦網路的主要功能:
資源共用;
資訊傳輸與集中處理;
均衡負荷與分布處理;
綜合資訊服務 (www/整合式服務數位網路絡 ISDN)。
網路通訊協定:電腦網路中實現通訊必須有一些約定即通訊協定,對速率、傳輸代碼、代碼結構、傳輸控制步驟、出錯控制等制定標準。
網路通訊介面:為了使兩個結點之間能進行對話,必須在它們之間建立通訊工具(即介面),使彼此之間能進行資訊交換。介面包括兩部分:
硬體裝置: 實現結點之間的資訊傳送
軟體裝置: 規定雙方進行通訊的約定協議
網路分層
分層原因:由於結點之間聯絡很複雜,在制定協議時,把複雜成份分解成一些簡單的成份,再將它們複合起來。最常用的複合方式是層次方式,即同層間可以通訊、上一層可以調用下一層,而與再下一層不發生關係。
封裝/拆分
網路爬蟲
網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取全球資訊網資訊的程式或者指令碼。另外一些不常使用的名字還有螞蟻、自動索引、類比程式或者蠕蟲。
隨著網路的迅速發展,全球資訊網成為大量資訊的載體,如何有效地提取並利用這些資訊成為一個巨大的挑戰。搜尋引擎(Search Engine),例如傳統的通用搜尋引擎AltaVista,Yahoo!和Google等,作為一個輔助人們檢索資訊的工具成為使用者訪問全球資訊網的入口和指南。但是,這些通用性搜尋引擎也存在著一定的局限性,如:
(1) 不同領域、不同背景的使用者往往具有不同的檢索目的和需求,通用搜尋引擎所返回的結果包含大量使用者不關心的網頁。
(2)通用搜尋引擎的目標是儘可能大的網路覆蓋率,有限的搜尋引擎伺服器資源與無限的網路資料資源之間的矛盾將進一步加深。
(3)全球資訊網資料形式的豐富和網路技術的不斷髮展,圖片、資料庫、音頻、視頻多媒體等不同資料大量出現,通用搜尋引擎往往對這些資訊含量密集且具有一定結構的資料無能為力,不能很好地發現和擷取。
(4)通用搜尋引擎大多提供基於關鍵字的檢索,難以支援根據語義資訊提出的查詢。
為瞭解決上述問題,定向抓取相關網頁資源的聚焦爬蟲應運而生。聚焦爬蟲是一個自動下載網頁的程式,它根據既定的抓取目標,有選擇的訪問全球資訊網上的網頁與相關的連結,擷取所需要的資訊。與通用爬蟲不同,聚焦爬蟲並不追求大的覆蓋,而將目標定為抓取與某一特定主題內容相關的網頁,為面向主題的使用者查詢準備資料資源。
1.聚焦爬蟲工作原理以及關鍵技術概述
網路爬蟲是一個自動提取網頁的程式,它為搜尋引擎從全球資訊網上下載網頁,是搜尋引擎的重要組成。傳統爬蟲從一個或若干初始頁面的URL開始,獲得初始頁面上的URL,在抓取網頁的過程中,不斷從當前頁面上抽取新的URL放入隊列,直到滿足系統的一定停止條件。聚焦爬蟲的工作流程較為複雜,需要根據一定的網頁分析演算法過濾與主題無關的連結,保留有用的連結並將其放入等待抓取的URL隊列。然後,它將根據一定的搜尋策略從隊列中選擇下一步要抓取的網頁URL,並重複上述過程,直到達到系統的某一條件時停止。另外,所有被爬蟲抓取的網頁將會被系統存貯,進行一定的分析、過濾,並建立索引,以便之後的查詢和檢索;對於聚焦爬蟲來說,這一過程所得到的分析結果還可能對以後的抓取過程給出反饋和指導。
相對於通用網路爬蟲,聚焦爬蟲還需要解決三個主要問題:
(1) 對抓取目標的描述或定義;
(2) 對網頁或資料的分析與過濾;
(3) 對URL的搜尋策略。
抓取目標的描述和定義是決定網頁分析演算法與URL搜尋策略如何制訂的基礎。而網頁分析演算法和候選URL排序演算法是決定搜尋引擎所提供的服務形式和爬蟲網頁抓取行為的關鍵所在。這兩個部分的演算法又是緊密相關的。
2.抓取目標描述
現有聚焦爬蟲對抓取目標的描述可分為基於目標網頁特徵、基於目標資料模式和基於領域概念3種。
基於目標網頁特徵的爬蟲所抓取、儲存並索引的對象一般為網站或網頁。根據種子樣本擷取方式可分為:
(1)預先給定的初始抓取種子樣本;
(2)預先給定的網頁分類目錄和與分類目錄對應的種子樣本,如Yahoo!分類結構等;
(3)通過使用者行為確定的抓取目標範例,分為:
(a) 使用者瀏覽過程中顯示標註的抓取樣本;
(b) 通過使用者日誌挖掘得到訪問模式及相關樣本。
其中,網頁特徵可以是網頁的內容特徵,也可以是網頁的連結結構特徵,等等。
Demo
<span style="font-size:18px;"><span style="font-size:18px;">package com.bjsxt.wsl;// HelloSpider類import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection; public class HelloSpider { publicstatic void main(String[] args) throws IOException { for(int i = 0; i < 10; i++) { //聲明地址 URLurl = new URL("http://www.iteye.com/news/31449"); //偽裝成瀏覽器進行爬取資料 URLConnectionconnection = url.openConnection(); connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, likeGecko) Chrome/48.0.2564.116 Safari/537.36"); //抓取資訊 InputStreaminputStream = connection.getInputStream(); //查看流中的資料(字元流) BufferedReaderbufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8")); //開始讀取資料 StringBufferbuffer = new StringBuffer(); Stringline = ""; while((line = bufferedReader.readLine()) != null) { buffer.append(line); } System.out.println(buffer); } }}</span></span>
UDP編程
在現有的網路中,網路通訊的方式主要有兩種:
1、 TCP(傳輸控制通訊協定)方式
2、 UDP(使用者資料包通訊協定)方式
為了方便理解這兩種方式,還是先來看一個例子。大家使用手機時,向別人傳遞資訊時有兩種方式:撥打到電話和傳送簡訊。使用撥打到電話的方式可以保證將資訊傳遞給別人,因為別人電話中時本身就確認接收到了該資訊。而傳送簡訊的方式價格低廉,使用方便,但是接收人有可能接收不到。
在網路通訊中,TCP方式就類似於撥打到電話,使用該種方式進行網路通訊時,需要建立專門的虛擬串連,然後進行可靠的資料轉送,如果資料發送失敗,則用戶端會自動重發該資料。而UDP方式就類似於傳送簡訊,使用這種方式進行網路通訊時,不需要建立專門的虛擬串連,傳輸也不是很可靠,如果發送失敗則用戶端無法獲得。
這兩種傳輸方式都是實際的網路編程中進行使用,重要的資料一般使用TCP方式進行資料轉送,而大量的非核心資料則都通過UDP方式進行傳遞,在一些程式中甚至結合使用這兩種方式進行資料的傳遞。
由於TCP需要建立專用的虛擬串連以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產生的資料量要比UDP稍微大一些。
在Java API中,實現UDP方式的編程,包含用戶端網路編程和伺服器端網路編程,主要由兩個類實現,分別是:
(1)DatagramSocket類實現“網路連接”,包括用戶端網路連接和伺服器端網路連接。雖然UDP方式的網路通訊不需要建立專用的網路連接,但是畢竟還是需要發送和接收資料,DatagramSocket實現的就是發送資料時的發射器,以及接收資料時的監聽器的角色。類比於TCP中的網路連接,該類既可以用於實現用戶端串連,也可以用於實現伺服器端串連。
(2)DatagramPacket類實現對於網路中傳輸的資料封裝,也就是說,該類的對象代表網路中交換的資料。在UDP方式的網路編程中,無論是需要發送的資料還是需要接收的資料,都必須被處理成DatagramPacket類型的對象,該對象中包含發送到的地址、發送到的連接埠號碼以及發送的內容等。其實DatagramPacket類的作用類似於現實中的信件,在信件中包含信件發送到的地址以及接收人,還有發送的內容等,郵局只需要按照地址傳遞即可。在接收資料時,接收到的資料也必須被處理成DatagramPacket類型的對象,在該對象中包含發送方的地址、連接埠號碼等資訊,也包含資料的內容。和TCP方式的網路傳輸相比,IO編程在UDP方式的網路編程中變得不是必須的內容,結構也要比TCP方式的網路編程簡單一些。
demo功能是實現將用戶端程式的系統時間發送給伺服器端,伺服器端接收到時間以後,向用戶端反饋字串“OK”。實現該功能的用戶端代碼如下所示:
<span style="font-size:18px;"><span style="font-size:18px;">package udp; import java.net.*;import java.util.*;/** * 簡單的UDP用戶端,實現向伺服器端發生系統時間功能 */public class SimpleUDPClient { public static void main(String[] args) { DatagramSocket ds = null;//連線物件 DatagramPacket sendDp; //發送資料包對象 DatagramPacket receiveDp; //接收資料包對象 String serverHost ="127.0.0.1"; //伺服器IP int serverPort = 10010; //伺服器連接埠號碼 try{ //建立串連 ds = newDatagramSocket(); //初始化發送資料 Date d = new Date(); //目前時間 String content =d.toString(); //轉換為字串 byte[] data =content.getBytes(); //初始化發送包對象 InetAddress address =InetAddress.getByName(serverHost); sendDp = newDatagramPacket(data,data.length,address,serverPort); //發送 ds.send(sendDp); //初始化接收資料 byte[] b = newbyte[1024]; receiveDp = newDatagramPacket(b,b.length); //接收 ds.receive(receiveDp); //讀取反饋內容,並輸出 byte[] response =receiveDp.getData(); int len =receiveDp.getLength(); String s = newString(response,0,len); System.out.println("伺服器端反饋為:"+ s); }catch(Exception e){ e.printStackTrace(); }finally{ try{ //關閉串連 ds.close(); }catch(Exceptione){} } } }</span></span>
Socket編程
基於tcp協議,建立穩定串連的點對點的通訊;
即時、快速、安全性高、佔用系統資源多、效率低;
“要求-回應”模式
用戶端:在網路通訊中,第一次主動發起通訊的程式被稱作用戶端(Client)程式。
伺服器:第一次通訊中等待串連的程式被稱作伺服器端(Server)程式。
Socket:發送TCP訊息。
ServerSocket:建立伺服器
通訊端是一種進程間的資料交換器制。這些進程既可以在同一機器上,也可以在通過網路連接的不同機器上。換句話說,通訊端起到通訊端點的作用。單個通訊端是一個端點,而一對通訊端則構成一個雙向通訊通道,使非關聯進程可以在本地或通過網路進行資料交換。一旦建立通訊端串連,資料即可在相同或不同的系統中雙向或單向發送,直到其中一個端點關閉串連。
URL(Uniform ResourceLocator):統一資源定位器,由4部分組成:協議、存放資源的主機網域名稱、資源檔名和連接埠號碼。
URL是指向互連網“資源”的指標,資源可以是簡單的檔案或目錄,也可以是對更為複雜的對象的引用,例如對資料庫或搜尋引擎的查詢。
【擴充】
Uniform Resource Identifier,統一資源識別項;
URL:Uniform ResourceLocator,統一資源定位器;
URN:Uniform ResourceName,統一資源名稱。
其中,URL,URN是URI的子集。
Web上地址的基本形式是URI,它代表統一資源識別項。有兩種形式:
URL:目前URI的最普遍形式就是無處不在的URL或統一資源定位器。
URN:URL的一種更新形式,統一資源名稱(URN, Uniform Resource Name)不依賴於位置,並且有可能減少失效串連的個數。但是其流行還需假以時日,因為它需要更精密軟體的支援。
URI是以某種統一的(標準化的)方式標識資源的簡單字串。
Demo聊天室:實現簡單控制台和伺服器之間的聊天功能。
<span style="font-size:18px;"><span style="font-size:18px;">package com.bjsxt.wsl.s4;//HelloServer類import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List; public class HelloServer { //聲明一個容器,存放所有當前正在訪問的用戶端 privatestatic List<ServerThread> threadList = new ArrayList<>(); publicstatic void main(String[] args) throws IOException { System.out.println("---------------我是伺服器----------------"); //建立一個伺服器 ServerSocketserverSocket = new ServerSocket(18888); //讓伺服器一直等待接受 //伺服器開始監聽連接埠等待用戶端請求 while(true) { Socketsocket = serverSocket.accept(); //開始啟動一個線程專門擷取用戶端的資訊,並將資訊返回 ServerThreadserverThread = new ServerThread(socket, threadList); //將當前用戶端加入到list threadList.add(serverThread); newThread(serverThread).start(); } }}===============================================================================package com.bjsxt.wsl.s4;//HelloSocket類import java.io.IOException;import java.net.Socket;import java.net.UnknownHostException; public class HelloSocket { publicstatic void main(String[] args) throws UnknownHostException, IOException { System.out.println("-------------我是用戶端--------------"); //建立一個用戶端 Socketsocket = new Socket("192.168.1.164", 18888); //開啟一個線程專門負責接收使用者在控制台的輸入,並把它寫出給伺服器 newThread(new SocketSendThread(socket)).start(); //開啟一個線程專門負責接收伺服器的返回資訊並列印 newThread(new SocketAcceptThread(socket)).start(); }}===============================================================================package com.bjsxt.wsl.s4;//ServerThread類import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;import java.util.List; public class ServerThread implementsRunnable { //封裝流的資訊 privateDataInputStream dataInputStream = null; privateDataOutputStream dataOutputStream = null; privateSocket socket = null; privateList<ServerThread> threadList = null; /** * 構造器 * @param socket */ publicServerThread(Socket socket, List<ServerThread> threadList) { try{ this.socket= socket; this.threadList= threadList; dataInputStream= new DataInputStream(socket.getInputStream()); dataOutputStream= new DataOutputStream(socket.getOutputStream()); }catch (IOException e) { e.printStackTrace(); } } @Override publicvoid run() { while(true) { //接收用戶端發送給伺服器的資訊 sendAllSocket(accept()); } } /** * 將訊息發送給其他人 * @param msg */ publicvoid sendAllSocket(String msg) { //遍曆 for(ServerThread serverThread : threadList) { serverThread.send(msg); } } /** * 將小心返回給客戶打斷 * @param msg */ publicvoid send(String msg) { try{ dataOutputStream.writeUTF(socket.getInetAddress()+ " 說:" + msg); dataOutputStream.flush(); }catch (IOException e) { e.printStackTrace(); } } /** * 開始接受用戶端的訊息 * @return */ privateString accept() { Stringmsg = ""; try{ msg= dataInputStream.readUTF(); }catch (IOException e) { e.printStackTrace(); } returnmsg; }} ===============================================================================package com.bjsxt.wsl.s4;//SocketAcceptThread類import java.io.DataInputStream;import java.io.IOException;import java.net.Socket; public class SocketAcceptThread implements Runnable{ //封裝流對象,防止重複建立 privateDataInputStream dataInputStream = null; publicSocketAcceptThread(Socket socket) { try{ //建立一個輸入資料流擷取伺服器的返回資訊 dataInputStream= new DataInputStream(socket.getInputStream()); }catch (IOException e) { e.printStackTrace(); } } @Override publicvoid run() { while(true) { accept(); } } /** * 接收並列印伺服器的返回資訊 */ privatevoid accept() { try{ Stringmsg = dataInputStream.readUTF(); System.out.println(msg); }catch (IOException e) { e.printStackTrace(); } } }===============================================================================package com.bjsxt.wsl.s4;//SocketSendThread類import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.net.Socket; public class SocketSendThread implementsRunnable { //將可複用的類進行封裝 privateBufferedReader bufferedReader = null; privateDataOutputStream dataOutputStream = null; /** * 構造器 * @param socket */ publicSocketSendThread(Socket socket) { try{ //接受使用者在控制台的輸入 bufferedReader= new BufferedReader(new InputStreamReader(System.in)); //將用戶端的資訊發送給伺服器 dataOutputStream= new DataOutputStream(socket.getOutputStream()); }catch (IOException e) { e.printStackTrace(); } } @Override publicvoid run() { //重複監聽用戶端的輸入 while(true) { send(); } } /** * 第一件 擷取使用者控制台的輸入的資料 * 第二件 將輸入的資料發送給伺服器 */ privatevoid send() { try{ //定義輸入資訊 Stringmsg = bufferedReader.readLine(); //判斷使用者輸入的資訊是否有效 if(msg != null && !"".equals(msg)) { dataOutputStream.writeUTF(msg); } }catch (IOException e) { e.printStackTrace(); } } }</span></span>
業務思想
網路編程是作為編程人員必傍身之計,其中想法是很重要的,也希望大家能夠更快的進入網路編程的世界。
[JAVA · 初級]:22.網路編程