爬取LeetCode題目——如何傳送GraphQL Query獲取資料

Zhaoxi_Zhang發表於2018-12-11

前言

  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

爬取LeetCode題目——如何傳送GraphQL Query獲取資料
找到 graphql 檔案(有好多 graphql 檔案,可以依次點選查詢自己想要的那個,這裡找到包含有題目資訊的),從 preview 中我們可以看到 data 返回了題目相關的資訊
爬取LeetCode題目——如何傳送GraphQL Query獲取資料
  那麼,如何構造 GraphQL Query 來獲取資訊呢?從 Header 中的 Request Payload 中我們可以看到一個query的欄位,這是我們要構造的 GraphQL Query 的一個重要資訊。
爬取LeetCode題目——如何傳送GraphQL Query獲取資料
  我們並不一開始就用程式碼來獲取題目資訊,而是先利用 Postman 來看看如何獲取題目資訊。右鍵 Network 下的 graphql 檔案—>Copy—>Copy as cURL(bash),如下圖所示:
爬取LeetCode題目——如何傳送GraphQL Query獲取資料
之後,開啟 Postman—>左上角Import—>Paste Raw Text貼上,從 Body中可以看到,構造好了的 GraphQL Query 與我們在 Request Payload 中看到的 query 的欄位相仿(因為有一點需要更改的細節)
爬取LeetCode題目——如何傳送GraphQL Query獲取資料
當然,如果不想直接貼上複製的 cURL,那麼我們可以自己在 Postman 中寫 Header 和 Body,需要注意的是這邊的 Content-Typeapplication/graphql,Body 中的 GraphQL 構造,參照 Request Payload 中的query的欄位來構造
爬取LeetCode題目——如何傳送GraphQL Query獲取資料
獲取到的結果如下:
爬取LeetCode題目——如何傳送GraphQL 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());

    }
}
複製程式碼

執行結果:

爬取LeetCode題目——如何傳送GraphQL Query獲取資料

相關文章