爬取LeetCode題目——如何發送GraphQL Query獲取數據

前言

  GraphQL 是一種用於 API 的查詢語言,是由 Facebook 開源的一種用於提供數據查詢服務的抽象框架。在服務端 API 開發中,很多時候定義一個接口返回的數據相對固定,因此要獲得更多信息或者只想得到某部分信息時,基於 RESTful API 的接口就顯得不那麼靈活。而 GraphQL 對 API 中的數據提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的數據,而且沒有任何冗餘,也讓 API 更容易地隨着時間推移而演進,還能用於構建強大的開發者工具。
  目前,LeetCode 和 GitHub 都藉助 GraphQL 來設計,提供了更大的靈活性,對於想借助 GitHub 來了解 GraphQL 可直接訪問 GraphQL API v4 ,或者參考 GraphQL 實戰:Github V4 API使用。而對於在 LeetCode 上使用 GraphQL 查詢,相對資料少一些,因此在這,我主要以 LeetCode 爲例,來做講解(其實是因爲自己業餘刷題時突發奇想,想寫一個爬蟲。

過程

  如果直接搜索以 Java 語言爲載體的 GraphQL 的話,一大部分搜索結果都是介紹使用 graphql-java 來搭建查詢服務,而我們的目的是利用 GraphQL 來獲取想要的數據,並非自己搭建一個查詢服務,因此如果一開始就選錯了工具,就會導致後面的方向都是錯誤的。
  以 LeetCode 第一題 1.Two Sum 爲例,獲取其後端發送過來的數據。利用 F12 功能調出如下界面,選 Network

找到 graphql 文件(有好多 graphql 文件,可以依次點擊查找自己想要的那個,這裏找到包含有題目信息的),從 preview 中我們可以看到 data 返回了題目相關的信息

  那麼,如何構造 GraphQL Query 來獲取信息呢?從 Header 中的 Request Payload 中我們可以看到一個query的字段,這是我們要構造的 GraphQL Query 的一個重要信息。

  我們並不一開始就用代碼來獲取題目信息,而是先利用 Postman 來看看如何獲取題目信息。右鍵 Network 下的 graphql 文件—>Copy—>Copy as cURL(bash),如下圖所示:

之後,打開 Postman—>左上角Import—>Paste Raw Text粘貼,從 Body中可以看到,構造好了的 GraphQL Query 與我們在 Request Payload 中看到的 query 的字段相仿(因爲有一點需要更改的細節)

當然,如果不想直接粘貼複製的 cURL,那麼我們可以自己在 Postman 中寫 Header 和 Body,需要注意的是這邊的 Content-Typeapplication/graphql,Body 中的 GraphQL 構造,參照 Request Payload 中的query的字段來構造

獲取到的結果如下:

我們在實際中,可能並不需要提供的所有信息,只想要某一部分,那麼只需更改query即可,這也是 GraphQL 的強大之處。比如我們只想要題目的content信息,那麼其query則爲

query{question(titleSlug:"two-sum") {content}}

代碼

在上邊,已經利用 Postman 查詢到想要的數據了,而現在我們要做的就是用代碼將上述操作展示出來。這邊,使用 OkHttp 來進行題目信息獲取。

import okhttp3.*;
import org.jsoup.Connection;
import org.jsoup.Jsoup;


import java.io.IOException;
import java.util.Map;

import static java.lang.System.out;

public class Question {
    public static void main(String... args) throws IOException {
        String questionUrl = "https://leetcode.com/problems/two-sum/description/";
        String graphqlUrl = "https://leetcode.com/graphql";
        Connection.Response response = Jsoup.connect(questionUrl)
                .method(Connection.Method.GET)
                .execute();

        Map<String,String>cookies = response.cookies();
        for (Map.Entry<String,String>entry:cookies.entrySet()){
            //out.println(entry.getKey() + ": " + entry.getValue());
        }
        String csrftoken = response.cookie("csrftoken");
        String __cfduid = response.cookie("__cfduid");

        OkHttpClient client = new OkHttpClient.Builder()
                .followRedirects(false)
                .followSslRedirects(false)
                .build();

        String postBody = "query{\n" +
                "  question(titleSlug:\"two-sum\") {\n" +
                "    content\n" +
                "  }\n" +
                "}\n";

        Request request = new Request.Builder()
                .addHeader("Content-Type","application/graphql")
                .addHeader("Referer",questionUrl)
                .addHeader("Cookie","__cfduid=" + __cfduid + ";" + "csrftoken=" + csrftoken)
                .addHeader("x-csrftoken",csrftoken)
                .url(graphqlUrl)
                .post(RequestBody.create(MediaType.parse("application/graphql; charset=utf-8"),postBody))
                .build();

        Response response1 = client.newCall(request).execute();
        //out.println(response1.headers());
        out.println(response1.body().string());

    }
}

執行結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章