1. ホーム
  2. アンドロイド

Android Advanced (XIII) ウェブクローラー&jsonアプリケーション

2022-02-26 02:53:56

    私が初めてウェブクローラーを扱ったとき、私は素人でした! 何度か悩んだ末に、その原理やそこで使われているjsonの技術を大体理解できたので、同じように戸惑っている人のためにまとめて勉強してみました。

    残券12306枚のクエリモジュールを自分のサイトでクロールしてみました。FirefoxのWeb開発者向けコンソールを使うと、以下の図のように、クロールしたWebページのリクエストURLを取得することができます。

The request URL is the URL we need to crawl. In addition, we can learn that the request protocol is Https protocol, using GET way to access. The crawl source code is shown below.
        public static String queryDate = "2015-04-19";
	public static String from_station = "JNK";
	public static String to_station = "BJP";

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

		HostnameVerifier hv = new HostnameVerifier() {
			public boolean verify(String urlHostName, SSLSession session) {
				System.out.println("Warning: URL Host: " + urlHostName
						+ " vs. " + session.getPeerHost());
				return true;
			}
		};

        String url = "https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate="
				+ queryDate
				+ "&from_station="
				+ from_station
				+ "&to_station="
				+ to_station;

		ProtocolUrlValidator.trustAllHttpsCertificates();
		HttpsURLConnection.setDefaultHostnameVerifier(hv);

		String result = WebServiceUtil.invokeByHTTPGET(url, null);
		
		Gson gson = new Gson();
		Trains trains = gson.fromJson(result, Trains.class);
		
		List<Items> items = trains.getData().getItems();
		
		if (trains.getHttpstatus() ! = 200) {
			trains.getMessages();
		} else {
			if (items ! = null && items.size() ! = 0)
				for (Item item : items) {
					System.out.println(item);
				}
		}
	}
}
<span style="font-size:14px;"> Since the protocol used is Https, we need to verify the certificate before accessing it. The blue code block is the URL we need to access, involving the invokeByHTTPGET(url,null) code shown below:</span>
<span style="font-size:18px;">public class WebServiceUtil {

	/**
	 * Calling Web services via SOAP1.1 protocol
	 * 
	 * @param wsdl WSDL path
	 * @param method method name
	 * @param namespace Namespace
	 * @param headerParameters header parameters
	 * @param bodyParameters 体参数
	 * @param isBodyParametersNS whether the body parameters have namespace
	 * @return String
	 * @throws Exception
	 */
	public static String invokeBySoap11(String wsdl, String method,
			String namespace, Map<String, String> headerParameters,
			Map<String, String> bodyParameters, boolean isBodyParametersNS)
			throws Exception {
		StringBuffer soapOfResult = null;
		// remove ?wsdl, get the list of methods
		int length = wsdl.length();
		wsdl = wsdl.substring(0, length - 5);
		URL url = new URL(wsdl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
		OutputStream out = conn.getOutputStream();
		// Get the soap1.1 message
		StringBuilder sb = new StringBuilder();
		sb.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
                xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" ");
		sb.append("xmlns:ns0=\"" + namespace + "\"");
		sb.append(">");
		if (headerParameters ! = null) {
			sb.append("<soap:Header>");
			for (Entry<String, String> headerParameter : headerParameters
					.entrySet()) {
				sb.append("<ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
				sb.append(headerParameter.getValue());
				sb.append("</ns0:");
				sb.append(headerParameter.getKey());
				
	public static String invokeBySoap12(String wsdl, String method,
			String namespace, Map<String, String> headerParameters,
			Map<String, String> bodyParameters, boolean isBodyParametersNS)
			throws Exception {
		StringBuffer soapOfResult = null;
		// remove ?wsdl
		int length = wsdl.length();
		wsdl = wsdl.substring(0, length - 5);
		URL url = new URL(wsdl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
		OutputStream out = conn.getOutputStream();
		// Get the soap1.1 message
		StringBuilder sb = new StringBuilder();
		sb.append("<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
                xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\ " ");
		sb.append("xmlns:ns0=\"" + namespace + "\"");
		sb.append(">");
		if (headerParameters ! = null) {
			sb.append("<soap12:Header>");
			for (Entry<String, String> headerParameter : headerParameters
					.entrySet()) {
				sb.append("<ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
				sb.append(headerParameter.getValue());
				sb.append("</ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
			}
			sb.append("</soap12:Header>");
		}
		sb.append("<soap12:Body><ns0:");
		sb.append(method);
		sb.append(">");
		// input parameters
		if (bodyParameters ! = null) {
			for (Entry<String, String> inputParameter : bodyParameters
					.entrySet()) {
				if (isBodyParametersNS) {
					sb.append("<ns0:");
					sb.append(inputParameter.getKey());
					sb.append(">");
					sb.append(inputParameter.getValue());
					sb.append("</ns0:");
					sb.append(inputParameter.getKey());
					sb.append(">");
				} else {
					sb.append("<");
					sb.append(inputParameter.getKey());
					sb.append(">");
					sb.append(inputParameter.getValue());
					sb.append("</");
					sb.append(inputParameter.getKey());
					sb.append(">");
				}
			}
		}
		sb.append("</ns0:");
		sb.append(method);
		sb.append("></soap12:Body></soap12:Envelope>");
		System.out.println(sb.toString());
		out.write(sb.toString().getBytes());
		int code = conn.getResponseCode();
		if (code == 200) {
			InputStream is = conn.getInputStream();
			byte[] b = new byte[1024];
			int len = 0;
			soapOfResult = new StringBuffer();
			while ((len = is.read(b)) ! = -1) {
				String s = new String(b, 0, len, "UTF-8");
				soapOfResult.append(s);
			}
		}
		conn.disconnect();
		return soapOfResult == null ? null : soapOfResult.toString();
	}

	/**
	 * Calling a service via HTTP POST pass-through
	 * 
	 * @param urlPath
	 * @param method
	 * @param namespace
	 * @param inputParameters
	 * @return
	 * @throws Exception
	 */
	public static String invokeByHTTPPOST(String urlPath, Map<String, String> inputParameters)
			throws Exception {
		StringBuffer resultStr = null;
		URL url = new URL(urlPath);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Conte

<span style="font-size:18px;"> At this point, the web crawler process is basically over. The returned results of this experiment are shown below:</span>

<span style="font-size:14px;"> Since the protocol used is Https, we need to verify the certificate before accessing it. The blue code block is the URL we need to access, involving the invokeByHTTPGET(url,null) code shown below:</span>

<span style="font-size:18px;">public class WebServiceUtil {

	/**
	 * Calling Web services via SOAP1.1 protocol
	 * 
	 * @param wsdl WSDL path
	 * @param method method name
	 * @param namespace Namespace
	 * @param headerParameters header parameters
	 * @param bodyParameters 体参数
	 * @param isBodyParametersNS whether the body parameters have namespace
	 * @return String
	 * @throws Exception
	 */
	public static String invokeBySoap11(String wsdl, String method,
			String namespace, Map<String, String> headerParameters,
			Map<String, String> bodyParameters, boolean isBodyParametersNS)
			throws Exception {
		StringBuffer soapOfResult = null;
		// remove ?wsdl, get the list of methods
		int length = wsdl.length();
		wsdl = wsdl.substring(0, length - 5);
		URL url = new URL(wsdl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
		OutputStream out = conn.getOutputStream();
		// Get the soap1.1 message
		StringBuilder sb = new StringBuilder();
		sb.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
                xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" ");
		sb.append("xmlns:ns0=\"" + namespace + "\"");
		sb.append(">");
		if (headerParameters ! = null) {
			sb.append("<soap:Header>");
			for (Entry<String, String> headerParameter : headerParameters
					.entrySet()) {
				sb.append("<ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
				sb.append(headerParameter.getValue());
				sb.append("</ns0:");
				sb.append(headerParameter.getKey());
				
	public static String invokeBySoap12(String wsdl, String method,
			String namespace, Map<String, String> headerParameters,
			Map<String, String> bodyParameters, boolean isBodyParametersNS)
			throws Exception {
		StringBuffer soapOfResult = null;
		// remove ?wsdl
		int length = wsdl.length();
		wsdl = wsdl.substring(0, length - 5);
		URL url = new URL(wsdl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
		OutputStream out = conn.getOutputStream();
		// Get the soap1.1 message
		StringBuilder sb = new StringBuilder();
		sb.append("<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
                xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\ " ");
		sb.append("xmlns:ns0=\"" + namespace + "\"");
		sb.append(">");
		if (headerParameters ! = null) {
			sb.append("<soap12:Header>");
			for (Entry<String, String> headerParameter : headerParameters
					.entrySet()) {
				sb.append("<ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
				sb.append(headerParameter.getValue());
				sb.append("</ns0:");
				sb.append(headerParameter.getKey());
				sb.append(">");
			}
			sb.append("</soap12:Header>");
		}
		sb.append("<soap12:Body><ns0:");
		sb.append(method);
		sb.append(">");
		// input parameters
		if (bodyParameters ! = null) {
			for (Entry<String, String> inputParameter : bodyParameters
					.entrySet()) {
				if (isBodyParametersNS) {
					sb.append("<ns0:");
					sb.append(inputParameter.getKey());
					sb.append(">");
					sb.append(inputParameter.getValue());
					sb.append("</ns0:");
					sb.append(inputParameter.getKey());
					sb.append(">");
				} else {
					sb.append("<");
					sb.append(inputParameter.getKey());
					sb.append(">");
					sb.append(inputParameter.getValue());
					sb.append("</");
					sb.append(inputParameter.getKey());
					sb.append(">");
				}
			}
		}
		sb.append("</ns0:");
		sb.append(method);
		sb.append("></soap12:Body></soap12:Envelope>");
		System.out.println(sb.toString());
		out.write(sb.toString().getBytes());
		int code = conn.getResponseCode();
		if (code == 200) {
			InputStream is = conn.getInputStream();
			byte[] b = new byte[1024];
			int len = 0;
			soapOfResult = new StringBuffer();
			while ((len = is.read(b)) ! = -1) {
				String s = new String(b, 0, len, "UTF-8");
				soapOfResult.append(s);
			}
		}
		conn.disconnect();
		return soapOfResult == null ? null : soapOfResult.toString();
	}

	/**
	 * Calling a service via HTTP POST pass-through
	 * 
	 * @param urlPath
	 * @param method
	 * @param namespace
	 * @param inputParameters
	 * @return
	 * @throws Exception
	 */
	public static String invokeByHTTPPOST(String urlPath, Map<String, String> inputParameters)
			throws Exception {
		StringBuffer resultStr = null;
		URL url = new URL(urlPath);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Conte


上記のコードブロックでは、SOAP1.1 プロトコルで Web サービスを呼び出してリクエストを送信し、SOAP1.2 プロトコルで Web サービスを呼び出して、そして

HTTP POST パススルーと HTTP GET パススルー。具体的なリクエスト方法については、ソースコードのコメント欄で詳しく説明しているので、ここでは繰り返さない。

また、クローリング処理では、jsonオンライン検証ツールで クリックするとリンクが開きます . このツールで行われる主な操作は、json形式の正しさを検証することと、json文字列を基に対応するPOJOクラスを生成することです。これは次の図に示されています。

                                                                                                                                           jsonフォーマット検証

                                                                                                                                                                                                      POJOクラスの生成

<span style="font-size:18px;"> At this point, the web crawler process is basically over. The returned results of this experiment are shown below:</span>

注:GSon が単独で json を解析することについて、まだ誤解があるようです。json の文字列の Key は、実際には対応するクラスの変数名に対応します。そうでない場合、パーススタイルでは null になってしまいます

例えば、上記のjson文字列のキーがdataの場合、POJOを作成する際に、変数名を自由に変更できないので、private List<Item>datas; を private List<Item>items; に書き換えると、変数 List<Item> items = 自分で変数名を変更したい場合は、 @SerializedName("datas") private List<Item>items などと注釈するとよいでしょう。