java模擬HTTP請求(集合了網上搜來的各類)

Java發送http請求 (get 與 post方法請求)php

Java發送http請求 (get 與post方法請求),如下代碼經本人親自調試可用!能夠直接使用之。html

注意:經過BufferedReader 讀取遠程返回的數據時,必須設置讀取編碼,不然中文會亂碼!java

packagecom.jiucool.www.struts.action;
 
importjava.io.BufferedReader;
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.net.HttpURLConnection;
importjava.net.URL;
importjava.net.URLEncoder;
 
publicclass post_request{
        publicstaticfinalString GET_URL="http://www.jiucool.com/request.php?key=j0r56u2";
 
publicstaticfinalString POST_URL="http://www.jiucool.com/request.php";
publicstaticvoid readContentFromGet()throwsIOException{
// 拼湊get請求的URL字串,使用URLEncoder.encode對特殊和不可見字符進行編碼
String getURL = GET_URL+"&activatecode="+URLEncoder.encode("久酷博客","utf-8");
URL getUrl =newURL(getURL);
// 根據拼湊的URL,打開鏈接,URL.openConnection函數會根據URL的類型,
// 返回不一樣的URLConnection子類的對象,這裏URL是一個http,所以實際返回的是HttpURLConnection
HttpURLConnection connection =(HttpURLConnection) getUrl
.openConnection();
// 進行鏈接,可是實際上get request要在下一句的connection.getInputStream()函數中才會真正發到
// 服務器
connection.connect();
// 取得輸入流,並使用Reader讀取
BufferedReader reader =newBufferedReader(newInputStreamReader(connection.getInputStream(),"utf-8"));//設置編碼,不然中文亂碼
System.out.println("=============================");
System.out.println("Contents of get request");
System.out.println("=============================");
String lines;
while((lines= reader.readLine())!=null){
        //lines = new String(lines.getBytes(), "utf-8");
System.out.println(lines);
}
reader.close();
// 斷開鏈接
connection.disconnect();
System.out.println("=============================");
System.out.println("Contents of get request ends");
System.out.println("=============================");
}
        publicstaticvoid readContentFromPost()throwsIOException{
// Post請求的url,與get不一樣的是不須要帶參數
URL postUrl =newURL(POST_URL);
// 打開鏈接
HttpURLConnection connection =(HttpURLConnection) postUrl
.openConnection();
// Output to the connection. Default is
// false, set to true because post
// method must write something to the
// connection
// 設置是否向connection輸出,由於這個是post請求,參數要放在
// http正文內,所以須要設爲true
connection.setDoOutput(true);
// Read from the connection. Default is true.
connection.setDoInput(true);
// Set the post method. Default is GET
connection.setRequestMethod("POST");
// Post cannot use caches
// Post 請求不能使用緩存
connection.setUseCaches(false);
// This method takes effects to
// every instances of this class.
// URLConnection.setFollowRedirects是static函數,做用於全部的URLConnection對象。
// connection.setFollowRedirects(true);
 
// This methods only
// takes effacts to this
// instance.
// URLConnection.setInstanceFollowRedirects是成員函數,僅做用於當前函數
connection.setInstanceFollowRedirects(true);
// Set the content type to urlencoded,
// because we will write
// some URL-encoded content to the
// connection. Settings above must be set before connect!
// 配置本次鏈接的Content-type,配置爲application/x-www-form-urlencoded的
// 意思是正文是urlencoded編碼過的form參數,下面咱們能夠看到咱們對正文內容使用URLEncoder.encode
// 進行編碼
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// 鏈接,從postUrl.openConnection()至此的配置必需要在connect以前完成,
// 要注意的是connection.getOutputStream會隱含的進行connect。
connection.connect();
DataOutputStream out =newDataOutputStream(connection
.getOutputStream());
// The URL-encoded contend
// 正文,正文內容其實跟get的URL中'?'後的參數字符串一致
String content="key=j0r53nmbbd78x7m1pqml06u2&type=1&toemail=jiucool@gmail.com"+"&activatecode="+URLEncoder.encode("久酷博客","utf-8");
// DataOutputStream.writeBytes將字符串中的16位的unicode字符以8位的字符形式寫道流裏面
out.writeBytes(content);
out.flush();
out.close();// flush and close
BufferedReader reader =newBufferedReader(newInputStreamReader(connection.getInputStream(),"utf-8"));//設置編碼,不然中文亂碼
String line="";
System.out.println("=============================");
System.out.println("Contents of post request");
System.out.println("=============================");
while((line= reader.readLine())!=null){
//line = new String(line.getBytes(), "utf-8");
System.out.println(line);
}
System.out.println("=============================");
System.out.println("Contents of post request ends");
System.out.println("=============================");
reader.close();
connection.disconnect();
}
}
apache

HttpURLConnection.connect函數,實際上只是創建了一個與服務器的tcp鏈接,並無實際發送http請求。不管是post仍是get,http請求實際上直到HttpURLConnection.getInputStream()這個函數裏面才正式發送出去。編程

在readContentFromPost() 中,順序是重中之重,對connection對象的一切配置(那一堆set函數)都必需要在connect()函數執行以前完成。而對 outputStream的寫操做,又必需要在inputStream的讀操做以前。這些順序其實是由http請求的格式決定的。瀏覽器

http 請求實際上由兩部分組成,一個是http頭,全部關於這次http請求的配置都在http頭裏面定義,一個是正文content,在connect()函數裏面,會根據HttpURLConnection對象的配置值生成http頭,所以在調用connect函數以前,就必須把全部的配置準備好。緩存

緊接着http頭的是http請求的正文,正文的內容經過outputStream寫入,實際上outputStream不是一個網絡流,充其量是個字符串流,往裏面寫入的東西不會當即發送到網絡,而是在流關閉後,根據輸入的內容生成http正文。安全

至此,http請求的東西已經準備就緒。在getInputStream()函數調用的時候,就會把準備好的http請求正式發送到服務器了,而後返回一個輸入流,用於讀取服務器對於這次http請求的返回信息。因爲http請求在getInputStream的時候已經發送出去了(包括http頭和正文),所以在getInputStream()函數以後對connection對象進行設置(對http頭的信息進行修改)或者寫入 outputStream(對正文進行修改)都是沒有意義的了,執行這些操做會致使異常的發生。服務器

  

源文檔 <http://www.jiucool.com/java-sending-http-requests-get-and-post-method-request/>cookie

  

JAVA-HttpClient來模擬瀏覽器GET,POST


七月 9, 2009 |標籤 post  http   | 瀏覽 4808

評論 0

通常的狀況下咱們都是使用IE或者Navigator瀏覽器來訪問一個WEB服務器,用來瀏覽頁面查看信息或者提交一些數據等等。所訪問的這些頁面有的僅僅是一些普通的頁面,有的須要用戶登陸後方可以使用,或者須要認證以及是一些經過加密方式傳輸,例如HTTPS。目前咱們使用的瀏覽器處理這些狀況都不會構成問題。不過你可能在某些時候須要經過程序來訪問這樣的一些頁面,好比從別人的網頁中""一些數據;利用某些站點提供的頁面來完成某種功能,例如說咱們想知道某個手機號碼的歸屬地而咱們本身又沒有這樣的數據,所以只好藉助其餘公司已有的網站來完成這個功能,這個時候咱們須要向網頁提交手機號碼並從返回的頁面中解析出咱們想要的數據來。若是對方僅僅是一個很簡單的頁面,那咱們的程序會很簡單,本文也就沒有必要大張旗鼓的在這裏浪費口舌。可是考慮到一些服務受權的問題,不少公司提供的頁面每每並非能夠經過一個簡單的URL就能夠訪問的,而必須通過註冊而後登陸後方可以使用提供服務的頁面,這個時候就涉及到COOKIE問題的處理。咱們知道目前流行的動態網頁技術例如ASPJSP無不是經過COOKIE來處理會話信息的。爲了使咱們的程序能使用別人所提供的服務頁面,就要求程序首先登陸後再訪問服務頁面,這過程就須要自行處理cookie,想一想當你用java.net.HttpURLConnection來完成這些功能時是多麼恐怖的事情啊!何況這僅僅是咱們所說的頑固的WEB服務器中的一個很常見的"頑固"!再有如經過HTTP來上傳文件呢?不須要頭疼,這些問題有了""就很容易解決了! 

  

咱們不可能列舉全部可能的頑固,咱們會針對幾種最多見的問題進行處理。固然了,正如前面說到的,若是咱們本身使用java.net.HttpURLConnection來搞定這些問題是很恐怖的事情,所以在開始以前咱們先要介紹一下一個開放源碼的項目,這個項目就是Apache開源組織中的httpclient,它隸屬於Jakartacommons項目,目前的版本是2.0RC2commons下原本已經有一個net的子項目,可是又把httpclient單獨提出來,可見http服務器的訪問絕非易事。 

  

Commons-httpclient項目就是專門設計來簡化HTTP客戶端與服務器進行各類通信編程。經過它可讓原來很頭疼的事情如今輕鬆的解決,例如你再也不管是HTTP或者HTTPS的通信方式,告訴它你想使用HTTPS方式,剩下的事情交給httpclient替你完成。本文會針對咱們在編寫HTTP客戶端程序時常常碰到的幾個問題進行分別介紹如何使用httpclient來解決它們,爲了讓讀者更快的熟悉這個項目咱們最開始先給出一個簡單的例子來讀取一個網頁的內容,而後按部就班解決掉前進中的全部問題。 

1讀取網頁(HTTP/HTTPS)內容 

下面是咱們給出的一個簡單的例子用來訪問某個頁面 

  

*    

 * Created on 2003-12-14 by skydong 

 */    

    

package http.demo;    

import java.io.IOException;    

import org.apache.commons.httpclient.*;    

import org.apache.commons.httpclient.methods.*;    

/** *//**   

 * 最簡單的HTTP客戶端,用來演示經過GET或者POST方式訪問某個頁面   

 * @author skydong 

 */   

public class SimpleClient ...{    

    public static void main(String[] args) throws IOException    

    ...{    

        HttpClient client = new HttpClient();       

        //設置代理服務器地址和端口         

     //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);    

        //使用GET方法,若是服務器須要經過HTTPS鏈接,那隻須要將下面URL中的http換成https    

        HttpMethod method = new GetMethod("http://java.sun.com";);     

        //使用POST方法    

     //HttpMethod method = new PostMethod("http://java.sun.com";);     

        client.executeMethod(method);    

        //打印服務器返回的狀態    

     System.out.println(method.getStatusLine());    

       //打印返回的信息    

     System.out.println(method.getResponseBodyAsString());    

       //釋放鏈接    

     method.releaseConnection();    

    }    

}    

  

  

在這個例子中首先建立一個HTTP客戶端(HttpClient)的實例,而後選擇提交的方法是GET或者POST,最後在HttpClient實例上執行提交的方法,最後從所選擇的提交方法中讀取服務器反饋回來的結果。這就是使用HttpClient的基本流程。其實用一行代碼也就能夠搞定整個請求的過程,很是的簡單! 

  

2GET或者POST方式向網頁提交參數 

其實前面一個最簡單的示例中咱們已經介紹瞭如何使用GET或者POST方式來請求一個頁面,本小節與之不一樣的是多了提交時設定頁面所需的參數,咱們知道若是是GET的請求方式,那麼全部參數都直接放到頁面的URL後面用問號與頁面地址隔開,每一個參數用&隔開,例如:http://java.sun.com?name=liudong&mobile=123456,可是當使用POST方法時就會稍微有一點點麻煩。本小節的例子演示向如何查詢手機號碼所在的城市,代碼以下: 

  

  

*    

 * Created on 2009-7-9 by skydong    

 */    

package http.demo;    

import java.io.IOException;    

import org.apache.commons.httpclient.*;    

import org.apache.commons.httpclient.methods.*;    

/** *//**   

 * 提交參數演示   

 * 該程序鏈接到一個用於查詢手機號碼所屬地的頁面   

 * 以便查詢號碼段1330227所在的省份以及城市   

 * @author skydong  

 */   

    

public class SimpleHttpClient ...{    

    public static void main(String[] args) throws IOException    

    ...{    

        HttpClient client = new HttpClient();    

        client.getHostConfiguration().setHost("www.imobile.com.cn"80"http");    

        HttpMethod method = getPostMethod();//使用POST方式提交數據    

     client.executeMethod(method);    

       //打印服務器返回的狀態    

     System.out.println(method.getStatusLine());    

       //打印結果頁面    

    String response =   new String(method.getResponseBodyAsString().getBytes("8859_1"));    

       //打印返回的信息    

     System.out.println(response);    

        method.releaseConnection();    

    }    

    /** *//**   

     * 使用GET方式提交數據   

   * @return   

     */   

    private static HttpMethod getGetMethod()...{    

        return new GetMethod("/simcard.php?simcard=1330227");    

    }    

    /** *//**   

     * 使用POST方式提交數據   

   * @return   

     */   

    private static HttpMethod getPostMethod()...{    

        PostMethod post = new PostMethod("/simcard.php");    

        NameValuePair simcard = new NameValuePair("simcard","1330227");    

        post.setRequestBody(new NameValuePair[] ...{ simcard});    

        return post;    

    }    

}    

  

在上面的例子中頁面http://www.imobile.com.cn/simcard.php須要一個參數是simcard,這個參數值爲手機號碼段,即手機號碼的前七位,服務器會返回提交的手機號碼對應的省份、城市以及其餘詳細信息。GET的提交方法只須要在URL後加入參數信息,而POST則須要經過NameValuePair類來設置參數名稱和它所對應的值 

  

3處理頁面重定向 

  

JSP/Servlet編程中response.sendRedirect方法就是使用HTTP協議中的重定向機制。它與JSP中的的區別在於後者是在服務器中實現頁面的跳轉,也就是說應用容器加載了所要跳轉的頁面的內容並返回給客戶端;而前者是返回一個狀態碼,這些狀態碼的可能值見下表,而後客戶端讀取須要跳轉到的頁面的URL並從新加載新的頁面。就是這樣一個過程,因此咱們編程的時候就要經過HttpMethod.getStatusCode()方法判斷返回值是否爲下表中的某個值來判斷是否須要跳轉。若是已經確認須要進行頁面跳轉了,那麼能夠經過讀取HTTP頭中的location屬性來獲取新的地址。 

  

狀態碼 

對應HttpServletResponse的常量 

詳細描述 

  

301 

SC_MOVED_PERMANENTLY 

頁面已經永久移到另一個新地址 

  

302 

SC_MOVED_TEMPORARILY 

頁面暫時移動到另一個新的地址 

  

303 

SC_SEE_OTHER 

客戶端請求的地址必須經過另外的URL來訪問 

  

307 

SC_TEMPORARY_REDIRECT 

SC_MOVED_TEMPORARILY 

  

  

下面的代碼片斷演示如何處理頁面的重定向 

  

client.executeMethod(post);    

        System.out.println(post.getStatusLine().toString());     

        post.releaseConnection();    

        //檢查是否重定向    

     int statuscode = post.getStatusCode();    

        if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) ||    

            (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) ||    

            (statuscode == HttpStatus.SC_SEE_OTHER) ||    

            (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT))     

        ...{//讀取新的URL地址    

         Header header = post.getResponseHeader("location");    

              if (header != null) ...{    

                 String newuri = header.getValue();    

                 if ((newuri == null) || (newuri.equals("")))    

                      newuri = "/";     

               GetMethod redirect = new GetMethod(newuri);    

               client.executeMethod(redirect);    

                System.out.println("Redirect:"+ redirect.getStatusLine().toString());     

                redirect.releaseConnection();    

            } else ...{    

               System.out.println("Invalid redirect");    

        } 

  

咱們能夠自行編寫兩個JSP頁面,其中一個頁面用response.sendRedirect方法重定向到另一個頁面用來測試上面的例子。 

本小節應該說是HTTP客戶端編程中最常遇見的問題,不少網站的內容都只是對註冊用戶可見的,這種狀況下就必需要求使用正確的用戶名和口令登陸成功後,方可瀏覽到想要的頁面。由於HTTP協議是無狀態的,也就是鏈接的有效期只限於當前請求,請求內容結束後鏈接就關閉了。在這種狀況下爲了保存用戶的登陸信息必須使用到Cookie機制。以JSP/Servlet爲例,當瀏覽器請求一個JSP或者是Servlet的頁面時,應用服務器會返回一個參數,名爲jsessionid(因不一樣應用服務器而異),值是一個較長的惟一字符串的Cookie,這個字符串值也就是當前訪問該站點的會話標識。瀏覽器在每訪問該站點的其餘頁面時候都要帶上jsessionid這樣的Cookie信息,應用服務器根據讀取這個會話標識來獲取對應的會話信息。 

  

對於須要用戶登陸的網站,通常在用戶登陸成功後會將用戶資料保存在服務器的會話中,這樣當訪問到其餘的頁面時候,應用服務器根據瀏覽器送上的Cookie中讀取當前請求對應的會話標識以得到對應的會話信息,而後就能夠判斷用戶資料是否存在於會話信息中,若是存在則容許訪問頁面,不然跳轉到登陸頁面中要求用戶輸入賬號和口令進行登陸。這就是通常使用JSP開發網站在處理用戶登陸的比較通用的方法。 

  

這樣一來,對於HTTP的客戶端來說,若是要訪問一個受保護的頁面時就必須模擬瀏覽器所作的工做,首先就是請求登陸頁面,而後讀取Cookie值;再次請求登陸頁面並加入登陸頁所需的每一個參數;最後就是請求最終所需的頁面。固然在除第一次請求外其餘的請求都須要附帶上Cookie信息以便服務器能判斷當前請求是否已經經過驗證。說了這麼多,但是若是你使用httpclient的話,你甚至連一行代碼都無需增長,你只須要先傳遞登陸信息執行登陸過程,而後直接訪問想要的頁面,跟訪問一個普通的頁面沒有任何區別,由於類HttpClient已經幫你作了全部該作的事情了,太棒了!下面的例子實現了這樣一個訪問的過程 

  

*    

 * Created on 2009-7-9 by skydong 

 */    

package http.demo;    

import org.apache.commons.httpclient.*;    

import org.apache.commons.httpclient.cookie.*;    

import org.apache.commons.httpclient.methods.*;    

/** *//**   

 * 用來演示登陸表單的示例   

 * @author skydong 

 */   

public class FormLoginDemo ...{    

    static final String LOGON_SITE = "localhost";    

    static final int    LOGON_PORT = 8080;    

    public static void main(String[] args) throws Exception...{    

        HttpClient client = new HttpClient();    

        client.getHostConfiguration().setHost(LOGON_SITE, LOGON_PORT);    

        //模擬登陸頁面login.jsp->main.jsp    

        PostMethod post = new PostMethod("/main.jsp");    

        NameValuePair name = new NameValuePair("name""ld");         

        NameValuePair pass = new NameValuePair("password""ld");         

        post.setRequestBody(new NameValuePair[]...{name,pass});    

       int status = client.executeMethod(post);    

       System.out.println(post.getResponseBodyAsString());    

       post.releaseConnection();      

       //查看cookie信息    

    CookieSpec cookiespec = CookiePolicy.getDefaultSpec();    

      Cookie[] cookies = cookiespec.match(LOGON_SITE, LOGON_PORT, "/"false, client.getState().getCookies());    

       if (cookies.length == 0) ...{    

           System.out.println("None");        

       } else ...{    

           for (int i = 0; i < cookies.length; i++) ...{    

              System.out.println(cookies[i].toString());        

           }    

      }    

       //訪問所需的頁面main2.jsp    

        GetMethod get = new GetMethod("/main2.jsp");    

        client.executeMethod(get);    

        System.out.println(get.getResponseBodyAsString());    

        get.releaseConnection();    

    }    

}    

  

  

5提交XML格式參數 

  

提交XML格式的參數很簡單,僅僅是一個提交時候的ContentType問題,下面的例子演示從文件文件中讀取XML信息並提交給服務器的過程,該過程能夠用來測試Web服務。

  

import java.io.File;    

import java.io.FileInputStream;    

import org.apache.commons.httpclient.HttpClient;    

import org.apache.commons.httpclient.methods.EntityEnclosingMethod;    

import org.apache.commons.httpclient.methods.PostMethod;    

/** *//**   

 * 用來演示提交XML格式數據的例子   

 */   

public class PostXMLClient ...{    

    public static void main(String[] args) throws Exception ...{    

        File input = new File("test.xml");    

        PostMethod post = new PostMethod("http://localhost:8080/httpclient/xml.jsp");    

        // 設置請求的內容直接從文件中讀取    

     post.setRequestBody(new FileInputStream(input));    

        if (input.length() < Integer.MAX_VALUE)     

           post.setRequestContentLength(input.length());    

        else               

           post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);    

        // 指定請求內容的類型    

     post.setRequestHeader("Content-type""text/xml; charset=GBK");    

        HttpClient httpclient = new HttpClient();     

        int result = httpclient.executeMethod(post);     

        System.out.println("Response status code: " + result);    

        System.out.println("Response body: ");    

        System.out.println(post.getResponseBodyAsString());    

        post.releaseConnection();    

    }    

}    

  

6經過HTTP上傳文件 

  

httpclient使用了單獨的一個HttpMethod子類來處理文件的上傳,這個類就是MultipartPostMethod,該類已經封裝了文件上傳的細節,咱們要作的僅僅是告訴它咱們要上傳文件的全路徑便可,下面的代碼片斷演示如何使用這個類。 

  

MultipartPostMethod filePost = new MultipartPostMethod(targetURL);    

filePost.addParameter("fileName", targetFilePath);    

HttpClient client = new HttpClient();    

//因爲要上傳的文件可能比較大,所以在此設置最大的鏈接超時時間    

client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);    

int status = client.executeMethod(filePost);    

  

上面代碼中,targetFilePath即爲要上傳的文件所在的路徑。 

  

7訪問啓用認證的頁面 

  

咱們常常會碰到這樣的頁面,當訪問它的時候會彈出一個瀏覽器的對話框要求輸入用戶名和密碼後方可,這種用戶認證的方式不一樣於咱們在前面介紹的基於表單的用戶身份驗證。這是HTTP的認證策略,httpclient支持三種認證方式包括:基本、摘要以及NTLM認證。其中基本認證最簡單、通用但也最不安全;摘要認證是在HTTP 1.1中加入的認證方式,而NTLM則是微軟公司定義的而不是通用的規範,最新版本的NTLM是比摘要認證還要安全的一種方式。 

  

下面例子是從httpclientCVS服務器中下載的,它簡單演示如何訪問一個認證保護的頁面: 

import org.apache.commons.httpclient.HttpClient;    

import org.apache.commons.httpclient.UsernamePasswordCredentials;    

import org.apache.commons.httpclient.methods.GetMethod;    

public class BasicAuthenticationExample ...{    

   public BasicAuthenticationExample() ...{    

    }    

   public static void main(String[] args) throws Exception ...{    

       HttpClient client = new HttpClient();    

        client.getState().setCredentials(    

            "www.verisign.com",    

            "realm",    

            new UsernamePasswordCredentials("username""password")    

        );    

        GetMethod get = new GetMethod("https://www.verisign.com/products/index.html";);    

        get.setDoAuthentication( true );    

        int status = client.executeMethod( get );    

        System.out.println(status+""+ get.getResponseBodyAsString());    

        get.releaseConnection();    

    }    

}   

  

8多線程模式下使用httpclient 

  

多線程同時訪問httpclient,例如同時從一個站點上下載多個文件。對於同一個HttpConnection同一個時間只能有一個線程訪問,爲了保證多線程工做環境下不產生衝突,httpclient使用了一個多線程鏈接管理器的類:MultiThreadedHttpConnectionManager,要使用這個類很簡單,只須要在構造HttpClient實例的時候傳入便可,代碼以下: 

  

MultiThreadedHttpConnectionManager connectionManager =     

   new MultiThreadedHttpConnectionManager();    

HttpClient client = new HttpClient(connectionManager);    

  

注:因爲有的網站限制,使用httpclient是要設置一下client,不然提示沒有權限。。。

HttpClient client = new HttpClient();
		
		//User-Agent
		client.getParams().setParameter(HttpMethodParams.USER_AGENT,
				"Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.2) Gecko/20090803");
相關文章
相關標籤/搜索