openplanning

Liệt kê, thêm và xoá các Sitemap với Google Search Java API

  1. Các yêu cầu đòi hỏi
  2. Liệt kê các Sitemap
  3. Xem và download một Sitemap cụ thể
  4. Thêm một Sitemap
  5. Xoá một Sitemap

1. Các yêu cầu đòi hỏi

Các yêu cầu đòi hỏi để làm việc với Google Search Java API được đề cập trong bài viết dưới đây. Bài viết này cũng đề cập các cách khác nhau để tạo đối tượng HttpRequestInitializer (Với API Key, OAuth Client Id hoặc Service Account).
Một lớp tiện ích để tạo đối tượng HttpRequestInitializer, đối tượng này cung cấp thông tin xác thực (credentials) cho các request mỗi khi chúng được gửi tới Google:
MyUtils.java
package org.o7planning.googleapis.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.FileUtils;

import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.ObjectParser;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;

public class MyUtils {

	public static final String SERVICE_ACCOUNT_FILE_PATH = "/Volumes/New/_gsc/test-google-search-console-key.json";

	private static byte[] serviceAccountBytes;

	public static HttpRequestInitializer createHttpRequestInitializer(String... scopes) throws IOException {
		InputStream serviceAccountInputStream = getServiceAccountInputStream();

		GoogleCredentials credentials = ServiceAccountCredentials //
				.fromStream(serviceAccountInputStream) //
				.createScoped(scopes);

		HttpRequestInitializer requestInitializer = new HttpRequestInitializer() {

			@Override
			public void initialize(HttpRequest request) throws IOException {
				HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(credentials);
				adapter.initialize(request);
				//
				if (request.getParser() == null) {
					ObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
					request.setParser(parser);
				}
				//
				request.setConnectTimeout(60000); // 1 minute connect timeout
				request.setReadTimeout(60000); // 1 minute read timeout
			}

		};
		return requestInitializer;
	}

	public static synchronized InputStream getServiceAccountInputStream() throws IOException {
		if (serviceAccountBytes == null) {
			serviceAccountBytes = FileUtils.readFileToByteArray(new File(SERVICE_ACCOUNT_FILE_PATH));
		}
		return new ByteArrayInputStream(serviceAccountBytes);
	}
}
Một lớp tiện ích để tạo đối tượng SearchConsole.Sitemaps, một đối tượng quan trọng có liên quan tới tất cả các ví dụ trong bài viết này.
MySitemapsUtils.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.IOException;
import java.security.GeneralSecurityException;

import org.o7planning.googleapis.utils.MyUtils;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.searchconsole.v1.SearchConsole;
import com.google.api.services.searchconsole.v1.SearchConsoleScopes;

public class MySitemapsUtils {

	private static final String APPLICATION_NAME = "My-Application";

	public static SearchConsole.Sitemaps getSitemapsClient(String applicationName) //
			throws IOException, GeneralSecurityException {
		//
		// @scopes: WEBMASTERS or WEBMASTERS_READONLY
		//
		HttpRequestInitializer httpRequestInitializer = MyUtils
				.createHttpRequestInitializer(SearchConsoleScopes.WEBMASTERS);

		//
		HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

		//
		SearchConsole client = new SearchConsole.Builder( //
				httpTransport, GsonFactory.getDefaultInstance(), httpRequestInitializer) //
				.setApplicationName(applicationName) //
				.build();

		SearchConsole.Sitemaps sitemaps = client.sitemaps();
		return sitemaps;
	}

	public static SearchConsole.Sitemaps getSitemapsClient() //
			throws IOException, GeneralSecurityException {
		return getSitemapsClient(APPLICATION_NAME);
	}
}

2. Liệt kê các Sitemap

Với Google Search API, bạn có thể liệt kê các file sitemap đã được đệ trình với Google Search Console. Theo mặc định chúng bao gồm các file sitemap mà bạn đã khai báo trong file "robots.txt".
Hãy xem file "robots.txt" của Apple:
ListSitemapExample.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;

import org.o7planning.googleapis.MyTestConstants;

import com.google.api.services.searchconsole.v1.SearchConsole;
import com.google.api.services.searchconsole.v1.model.SitemapsListResponse;
import com.google.api.services.searchconsole.v1.model.WmxSitemap;
import com.google.api.services.searchconsole.v1.model.WmxSitemapContent;

public class ListSitemapExample {

	//
	// @siteUrl: https://foo.com/ (IMPORTANT: needs suffix "/")
	//
	private static void listSitemaps(String siteUrl) //
			throws IOException, GeneralSecurityException {
		SearchConsole.Sitemaps sitemapClient = MySitemapsUtils.getSitemapsClient();
		SearchConsole.Sitemaps.List listRequest = sitemapClient.list(siteUrl);

		SitemapsListResponse listResponse = listRequest.execute();
		System.out.println("Sitemaps.List Response: " + listResponse.toPrettyString());

		List<WmxSitemap> sitemapList = listResponse.getSitemap();

		for (WmxSitemap sitemap : sitemapList) {
			System.out.println("\nSitemap: " + sitemap.getPath());
			System.out.println("- error: " + sitemap.getErrors());
			System.out.println("- warnings: " + sitemap.getWarnings());
			System.out.println("- isPending: " + sitemap.getIsPending());
			System.out.println("- isSitemapsIndex: " + sitemap.getIsPending());
			System.out.println("- lastDownloaded: " + sitemap.getLastDownloaded());
			System.out.println("- lastSubmitted: " + sitemap.getLastSubmitted());
			//
			List<WmxSitemapContent> contentList = sitemap.getContents();
			if (contentList != null) {
				for (WmxSitemapContent content : contentList) {
					System.out.println(" - indexed: " + content.getIndexed());
					System.out.println(" - submitted: " + content.getSubmitted());
					System.out.println(" - type: " + content.getType());
				}
			}
		}
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException {
		listSitemaps(MyTestConstants.siteUrl);
	}
}
Output:
Sitemaps.List Response: {
  "sitemap": [
    {
      "contents": [
        {
          "indexed": "0",
          "submitted": "890",
          "type": "web"
        }
      ],
      "errors": "0",
      "isPending": false,
      "isSitemapsIndex": false,
      "lastDownloaded": "2023-12-03T06:47:32.933Z",
      "lastSubmitted": "2023-12-03T06:47:02.495Z",
      "path": "http://foo.com/sitemap.xml",
      "type": "sitemap",
      "warnings": "0"
    }
  ]
}

Sitemap: http://foo.com/sitemap.xml
- error: 0
- warnings: 0
- isPending: false
- isSitemapsIndex: false
- lastDownloaded: 2023-12-03T06:47:32.933Z
- lastSubmitted: 2023-12-03T06:47:02.495Z
 - indexed: 0
 - submitted: 890
 - type: web

3. Xem và download một Sitemap cụ thể

Trong ví dụ này chúng ta sẽ lấy thông tin một sitemap theo URL của nó.
Tham số "siteUrl" có thể là:
  • sc-domain:yourdomain.com
  • http://yourdomain.com/
  • http://sub.yourdomain.com/
  • https://langlearning.net/
  • https://langlearning.net/app/en/
GetSitemapExample.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;

import org.o7planning.googleapis.MyTestConstants;

import com.google.api.services.searchconsole.v1.SearchConsole;
import com.google.api.services.searchconsole.v1.model.WmxSitemap;
import com.google.api.services.searchconsole.v1.model.WmxSitemapContent;

public class GetSitemapExample {
	//
	// @siteUrl: https://foo.com/ (Needs suffix "/") (for a URL-prefix property)
	// @siteUrl: sc-domain:foo.com (for a Domain property)
	// @feedpath: https://foo.com/sitemap.xml
	//
	private static void getOneSitemapInfo(String siteUrl, String feedpath)
			throws IOException, GeneralSecurityException {
		SearchConsole.Sitemaps sitemapClient = MySitemapsUtils.getSitemapsClient();

		SearchConsole.Sitemaps.Get getRequest = sitemapClient.get(siteUrl, feedpath);

		WmxSitemap sitemap = getRequest.execute();

		System.out.println("\nSitemap: " + sitemap.toPrettyString());

		System.out.println("- path: " + sitemap.getPath());
		System.out.println("- error: " + sitemap.getErrors());
		System.out.println("- warnings: " + sitemap.getWarnings());
		System.out.println("- isPending: " + sitemap.getIsPending());
		System.out.println("- isSitemapsIndex: " + sitemap.getIsPending());
		System.out.println("- lastDownloaded: " + sitemap.getLastDownloaded());
		System.out.println("- lastSubmitted: " + sitemap.getLastSubmitted());
		//
		List<WmxSitemapContent> contentList = sitemap.getContents();
		for (WmxSitemapContent content : contentList) {
			System.out.println(" - indexed: " + content.getIndexed());
			System.out.println(" - submitted: " + content.getSubmitted());
			System.out.println(" - type: " + content.getType());
		}
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException { 
		getOneSitemapInfo(MyTestConstants.siteUrl, MyTestConstants.feedpath);
	}
}
Output:
Sitemap: {
  "contents": [
    {
      "indexed": "0",
      "submitted": "890",
      "type": "web"
    }
  ],
  "errors": "0",
  "isPending": false,
  "isSitemapsIndex": false,
  "lastDownloaded": "2023-12-03T06:47:32.933Z",
  "lastSubmitted": "2023-12-03T06:47:02.495Z",
  "path": "https://langlearning.net/sitemap.xml",
  "type": "sitemap",
  "warnings": "0"
}
- path: https://langlearning.net/sitemap.xml
- error: 0
- warnings: 0
- isPending: false
- isSitemapsIndex: false
- lastDownloaded: 2023-12-03T06:47:32.933Z
- lastSubmitted: 2023-12-03T06:47:02.495Z
 - indexed: 0
 - submitted: 890
 - type: web
Ví dụ download một sitemap:
DownloadSitemapExample.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;

import org.o7planning.googleapis.MyTestConstants;

import com.google.api.services.searchconsole.v1.SearchConsole;

public class DownloadSitemapExample {

	//
	// @siteUrl: https://foo.com/ (IMPORTANT: needs suffix "/")
	// @feedpath: https://foo.com/sitemap.xml
	//
	private static void downloadExample(String siteUrl, String feedpath) //
			throws IOException, GeneralSecurityException { 
		SearchConsole.Sitemaps sitemapClient = MySitemapsUtils.getSitemapsClient();

		SearchConsole.Sitemaps.Get getRequest = sitemapClient.get(siteUrl, feedpath);

		System.out.println("Sitemaps.Get Request: " + getRequest.toString());

		String outputFilePath = "/Volumes/New/Test/output/sitemap.xml";
		FileOutputStream fileOutputStream = new FileOutputStream(new File(outputFilePath));
		getRequest.executeAndDownloadTo(fileOutputStream);
		fileOutputStream.close();
		System.out.println("Done!");
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException {
		downloadExample(MyTestConstants.siteUrl, MyTestConstants.feedpath);
	}
}

4. Thêm một Sitemap

Đệ trình một file sitemap mới với Google, nghĩa là bạn nói với Google rằng hãy theo dõi (track) file này và thu thập nội dung các URLs được chỉ định bên trong nó. Sau hành động này file sitemap mà bạn vừa đệ trình sẽ xuất hiện trên danh sách mà bạn có được thông qua API. Nếu bạn đệ trình một file sitemap đã sẵn có trên danh sách sẽ không có điều gì xẩy ra.
SubmitSitemapExample.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.IOException;
import java.security.GeneralSecurityException;

import org.o7planning.googleapis.MyTestConstants;

import com.google.api.services.searchconsole.v1.SearchConsole;

public class SubmitSitemapExample {

	//
	// @siteUrl: https://foo.com/ (IMPORTANT: needs suffix "/")
	// @feedpathToSubmit: https://foo.com/sitemap.xml
	//
	private static void submitSitemap(String siteUrl, String feedpathToSubmit) //
			throws IOException, GeneralSecurityException {
		SearchConsole.Sitemaps sitemapClient = MySitemapsUtils.getSitemapsClient();
		SearchConsole.Sitemaps.Submit submitRequest //
				= sitemapClient.submit(siteUrl, feedpathToSubmit);

		submitRequest.execute();
		System.out.println("Done!");
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException {
		submitSitemap(MyTestConstants.siteUrl, MyTestConstants.feedpathToSubmit);
	}
}

5. Xoá một Sitemap

Sử dụng Google Console API bạn có thể xoá một file sitemap, nó sẽ bị loại bỏ khỏi danh sách các sitemaps trả về từ Google Search API.
Theo thời gian, hệ thống của Google sẽ ngừng cố gắng sử dụng file sitemap mà bạn vừa xoá. Lưu ý - việc xóa tệp sơ đồ trang web không ngăn cản việc thu thập thông tin các URLs đã được chỉ định trong sitemap. Bởi vì Google có thể phát hiện ra các liên kết trên website của bạn khi phân tích nội dung từng trang. Tuy nhiên, sitemap là cần thiết giúp Google thu thập dữ liệu hiệu quả hơn, đó thường là lý do tại sao Google khuyên bạn nên sử dụng file này.
Ngoài ra, một điều quan trọng để Google ngừng theo dõi file sitemap mà bạn vừa xoá là sitemap này phải trả về mã lỗi 404 hoặc 410.
Ví dụ, xoá một sitemap với Google Search Java API:
DeleteSitemapExample.java
package org.o7planning.googleapis.a14297.sitemap;

import java.io.IOException;
import java.security.GeneralSecurityException;

import org.o7planning.googleapis.MyTestConstants;

import com.google.api.services.searchconsole.v1.SearchConsole;

public class DeleteSitemapExample {

	//
	// @siteUrl: https://foo.com/ (IMPORTANT: needs suffix "/")
	// @feedpathToDelete: https://foo.com/sitemap2.xml
	//
	private static void deleteSitemap(String siteUrl, String feedpathToDelete) //
			throws IOException, GeneralSecurityException {
		SearchConsole.Sitemaps sitemapClient = MySitemapsUtils.getSitemapsClient();

		SearchConsole.Sitemaps.Delete deleteRequest //
				= sitemapClient.delete(siteUrl, feedpathToDelete);

		deleteRequest.execute();
		System.out.println("Done!");
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException {
		deleteSitemap(MyTestConstants.siteUrl, MyTestConstants.feedpathToDelete);
	}
}