Halo博客的百度定時頁面提交
前言
- 好不容易搭建好博客,寫了些自以為有點意思的文章,但是沒人看!!因為沒有提交到搜索引擎,所以根本沒人能搜到嘛~。雖然Next主題提供了百度自動提交的配置,但是百度收錄已經不再提供推動收錄的服務,所以Next主題的配置也沒啥用了
-
百度收錄網站中提供了三種收錄方式,其中API提交最快捷,因此考慮使用Java實現將Halo博客文章推送到百度收錄中
- API提交
- sitemap提交
- 手動提交
-
Halo提供了用於獲取文章列表的API,因此思路很簡單:使用Java定時任務線程池按照固定的時間間隔從Halo API中獲取全部的文章鏈接,然後調用百度收錄API,向百度提交文章鏈接
- 百度收錄對於頻繁提交舊的鏈接有一定的限制,如果經常重複提交舊鏈接,會下調配額,甚至可能會失去API推送功能的權限,如果經常提交新的文章鏈接,可能適當提高配額。因此需要建立一個簡單的緩存,提交鏈接時濾除舊的已經提交過的鏈接
- 儘管Google使用站點地圖就已經能很好地進行鏈接的抓取了,不用單獨提交,但是Google同樣推薦使用indexing API主動提交要收錄的鏈接,具體可參考Halo博客的谷歌定時頁面提交
工程搭建
-
建立Gradle工程,配置文件如下所示
plugins { id 'java' id 'application' } group 'xyz.demoli' version '1.0-SNAPSHOT' sourceCompatibility = 1.11 mainClassName="xyz.demoli.Main" repositories { mavenCentral() } application{ applicationDefaultJvmArgs = ['-Duser.timezone=GMT+8'] } dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.9.3' implementation 'com.google.code.gson:gson:2.9.0' // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.14.1' // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.14.1' // https://mvnrepository.com/artifact/org.projectlombok/lombok compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.22' annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.22' } test { useJUnitPlatform() }annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.22'保證gradle項目中lombok的註解可以被正確解析applicationDefaultJvmArgs參數的設置是為了解決後續服務部署在容器中時日誌打印時間不是東八區時區的問題
-
配置文件如下:
prefix=https://blog.demoli.xyz postAPI=%s/api/content/posts?api_access_key=%s&page=%d apiAccessKey=*** baiduUrl=http://data.zz.baidu.com/urls? token=***-
apiAccessKey是在Halo博客設置中設定的 prefix是Halo博客的首頁訪問URLtoken是百度提交平台為用户提供的提交token,在百度提交網站頁面中有展示
-
-
日誌配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration status="INFO"> <appenders> <console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <loggers> <root level="INFO"> <appender-ref ref="console"/> </root> </loggers> </configuration> -
整個工程只有兩個核心類
-
PostScrapimport com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * 使用Halo API獲取文章鏈接 */ public class PostScrap { static private String postAPI; static private String apiAccessKey; static private String prefix; // 緩存 static private final Set<String> links = new HashSet<>(); // 注意properties配置文件中字符串不用加引號 static { try (InputStream stream = PostScrap.class.getResourceAsStream("/config.properties")) { Properties properties = new Properties(); properties.load(stream); apiAccessKey = properties.getProperty("apiAccessKey"); prefix = properties.getProperty("prefix"); postAPI = properties.getProperty("postAPI"); } catch (IOException e) { e.printStackTrace(); } } /** * 發起請求獲取全部文章鏈接 * * @return */ public static List<String> getPosts() { List<String> res = new ArrayList<>(); OkHttpClient client = new OkHttpClient(); Request initialRequest = new Request.Builder().get().url(String.format(postAPI, prefix, apiAccessKey, 0)) .build(); try (Response response = client.newCall(initialRequest).execute()) { res = handlePage(response, client); } catch (IOException e) { e.printStackTrace(); } return res; } /** * 處理分頁 * * @param initialResponse * @param client * @return * @throws IOException */ private static List<String> handlePage(Response initialResponse, OkHttpClient client) throws IOException { JsonObject jsonObject = new Gson().fromJson(initialResponse.body().string(), JsonObject.class); JsonArray array = jsonObject.get("data").getAsJsonObject().get("content").getAsJsonArray(); int pages = jsonObject.get("data").getAsJsonObject().get("pages").getAsInt(); // jsonArray轉為List List<String> posts = new ArrayList<>(); for (JsonElement element : array) { posts.add(element.getAsJsonObject().get("fullPath").getAsString()); } // 分頁查詢 for (int i = 1; i < pages; i++) { Request request = new Request.Builder().get().url(String.format(postAPI, prefix, apiAccessKey, i)) .build(); try (Response response = client.newCall(request).execute()) { jsonObject = new Gson().fromJson(response.body().string(), JsonObject.class); array = jsonObject.get("data").getAsJsonObject().get("content").getAsJsonArray(); for (JsonElement element : array) { posts.add(element.getAsJsonObject().get("fullPath").getAsString()); } } catch (IOException e) { e.printStackTrace(); } } // 緩存過濾 return posts.stream().map(content -> prefix + content).filter(links::add) .collect(Collectors.toList()); } } -
BaiduSubmitterimport com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.IOException; import java.io.InputStream; import java.util.Optional; import java.util.Properties; import lombok.extern.log4j.Log4j2; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; /** * 提交百度收錄 */ @Log4j2 public class BaiduSubmitter { static private String submitUrl; static { try (InputStream stream = PostScrap.class.getResourceAsStream("/config.properties")) { Properties properties = new Properties(); properties.load(stream); String baiduUrl = properties.getProperty("baiduUrl"); String site = properties.getProperty("prefix"); String token = properties.getProperty("token"); submitUrl = baiduUrl + "site=" + site + "&token=" + token; } catch (IOException e) { e.printStackTrace(); } } /** * 提交鏈接 */ public static void submit() { OkHttpClient client = new OkHttpClient(); Optional<String> urlStrings = PostScrap.getPosts().stream().reduce((out, url) -> out + "\n" + url); if (urlStrings.isEmpty()) { log.info("無新增文章"); return; } RequestBody body = RequestBody.create(MediaType.get("text/plain"), urlStrings.get()); Request request = new Request.Builder().post(body).url(submitUrl) .header("Content-Type", "text/plain") .build(); try (Response response = client.newCall(request).execute()) { JsonObject jsonObject = new Gson().fromJson(response.body().string(), JsonObject.class); if (jsonObject.get("error") != null) { log.error("提交失敗: {}", jsonObject.get("message").getAsString()); } log.info("提交成功 {} 條鏈接,剩餘額度: {},鏈接清單如下:", jsonObject.get("success").getAsInt(), jsonObject.get("remain").getAsInt()); log.info(urlStrings.get()); } catch (IOException e) { e.printStackTrace(); } } }
-
-
Mainpublic class Main { public static void main(String[] args) { Executors.newScheduledThreadPool(1) .scheduleWithFixedDelay(BaiduSubmitter::submit, 0, 12, TimeUnit.HOURS); } }
工程部署
- 項目根目錄執行
gradle build -x test -
將
build/distributions/BaiduSubmitter-1.0-SNAPSHOT.tar拷貝到安裝有Java環境的服務器tar xf BaiduSubmitter-1.0-SNAPSHOT.tar` cd BaiduSubmitter-1.0-SNAPSHOT nohup bin/BaiduSubmitter > nohup.out & tail -f nohup.out查看日誌
補充
- 博主是一個Docker容器的究極愛好者,因為使用容器可以保證宿主機環境的”純淨“,所以這裏補充使用Docker容器部署服務的方式
-
首先將項目構建得到的軟件包
build/distributions/BaiduSubmitter-1.0-SNAPSHOT.tar拷貝到服務器,解壓並重新命名,創建Dockerfiletar xf BaiduSubmitter-1.0-SNAPSHOT.tar mkdir -p blogSubmitter/baiduSubmitter mv BaiduSubmitter-1.0-SNAPSHOT blogSubmitter/baiduSubmitter/baidu cd blogSubmitter/baiduSubmitter touch Dockerfile -
Dockerfile文件如下:
FROM openjdk:11 COPY . /submitter WORKDIR /submitter # 更改時區 RUN rm -rf /etc/localtime RUN ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime CMD ["nohup","baidu/bin/BaiduSubmitter"," &"] -
創建yaml配置文件,使用Docker Compose構建服務
cd blogSubmitter touch submitter.yamlversion: '3.1' services: blog-baidu-submitter: build: ./baiduSubmitter container_name: blogBaiduSubmitter restart: unless-stopped - 執行
docker-compose -f submitter.yaml up -d創建服務
注意事項
- 如果更改了源碼,需要重新構建鏡像,此時要把之前的鏡像刪除(應該有更好的解決辦法,有待改善,比如使用volume的方式執行掛載)
參考
- Gradle Application Plugin
- 解決Docker容器和宿主機時間不一致的問題