記事

Medium API|GraphQLデータスクレイピングとCloudflare突破技術の実践ガイド

MediumのGraphQL Private APIを活用し、記事データを安全にスクレイピング。Cloudflareの認証障壁を突破する具体的手法を解説し、効率的な記事バックアップを実現します。

Medium API|GraphQLデータスクレイピングとCloudflare突破技術の実践ガイド

本記事は AI による翻訳をもとに作成されています。表現が不自然な箇所がありましたら、ぜひコメントでお知らせください。

記事一覧


Medium API データスクレイピングと Cloudflare 攻防の心の軌跡

Medium Graphql Private API を使ったデータ取得、記事バックアップおよび Cloudflare 認証ブロック突破方法。

背景

2018年からMediumで記事を書き始め、2022年頃には記事数が50本以上、文字数は6万字以上、記事の画像は約3GBに達しました(2025年時点で120本以上/10万字以上/6GB以上の画像)。バックアップなし!バックアップなし!バックアップなし! ずっとMediumで直接記事を書いており(エディターは本当に使いやすい)、書き終わったらすぐに保存して公開し、ローカルには一切バックアップがありません。

その時、不安な気持ちが自然と湧いてきました。もしある日 Medium がなくなったら、私の記事は消えてしまうのではないか?(しかもその数年間、Medium の状況はずっと良くなく、収益も上がっていませんでした)あるいはアカウントが突然停止されたら、これまでの努力が全て無駄になってしまうのではないかと。

そこでその年から Medium のデータ、特に記事内容の取得とダウンロード・バックアップ方法の研究を始めました。

Medium HTML ❌

構造が非常に複雑で変わりやすく、クロールも遅いため、ブラウザを直接使って内容を取得するのは最悪の方法です。

Medium Official API ❌

警告 Medium APIは既にサポートされていません。使用は推奨しません。

Medium はかつて公式の API を提供していましたが、おそらくリリース後すぐに停止されました。製品の目的が異なるためと推測されます。Medium はプライベートなトラフィックを重視しており、コンテンツをサイト内に留めておくべきで、外部に API を開放してデータを取得されることを避けたいのだと思われます。

Medium 非公式API 🤔

公式がAPIを提供していないため、有志の開発者が外部向けのAPIインターフェースを独自に開発し、必要なユーザーが接続できるようにしています。しかし、その仕組みはおそらくMediumのPrivate APIを利用しており、一層のラッピングを越えて、リクエストをPrivate APIに変換して原始データを取得していると思われます。

料金も安くなく、約2,500リクエストで5米ドル ;このサービスはかなり長く続いていますが、結局はMediumのプライベートAPIを使うので、自分で解析して自分でリクエストすればいいと思いました。

Medium Private API ✅

何が Private API かというと、この API インターフェースは 外部向けではなく、ドキュメントもなく、いつでも停止される可能性があり、法的リスクがある場合もある ということです。つまり、直接そのウェブサイトやアプリの動作を確認して呼び出されている API を見つけ出し、自分でプログラムを書いてその API を利用して自分のサービスにデータを取得する ということです。

https://medium.com/_/graphql

ネットワークリクエストを解析すると、すべての Medium API は https://medium.com/_/graphql このエンドポイントを使用していることがわかります。自分のプログラムで同じデータを呼び出して取得できれば、この方法は実現可能です。

Medium Private API — Medium Graphql のスニッフィング(フォロワー数取得の例)

私の最初のアプリケーションは、自分のフォロワー数を取得して、自分のポータルサイトに表示することでした。

link.zhgchg.li

したがって、自分のMediumアカウントのフォロワー数をスクレイピングする必要があります。

  • まずブラウザの「開発者ツール」(Chrome -> 右クリック -> 検証)を開き、「Network」タブに切り替え、「Preserve log」をチェックし、検索ボックスに「graphql」と入力します。

  • Medium アカウントのフォロワーページにアクセス:「 https://medium.com/@YOUR_USER_NAME/followers
    この形式のフォロワーURLでのみAPIを嗅ぎ取れます。https://YOUR_USER_NAME.medium.com/followers の形式ではFollowers APIの呼び出しはありません。

  • 左側の「graphql」リクエストを一つずつ確認し、「Response」データ内で「followerCount」というキーワードを検索します。このキーワードが含まれ、かつ username がページのユーザー名と一致し、フォロワー数もページ表示と同じであれば、その対象のリクエストを見つけます。

  • 目的の API を見つけたら、Payload → View Source → 以下の全内容をコピーしてください。

現在私たちは Medium のフォロワー取得用 API とペイロードを手に入れました。Postman を使って試してみましょう!

Medium フォロワー数取得エンドポイント — リクエスト & レスポンス

エンドポイント:

1
POST https://medium.com/_/graphql

Authorization: 認証不要(TokenやCookieの設定不要)

ペイロード:

1
2
3
4
5
6
7
8
9
10
[
    {
        "operationName": "UserFollowers",
        "variables": {
            "id": null,
            "username": "zhgchgli"
        },
        "query": "query UserFollowers($username: ID, $id: ID, $paging: PagingOptions) {\n  userResult(username: $username, id: $id) {\n    __typename\n    ... on User {\n      id\n      followersUserConnection(paging: $paging) {\n        pagingInfo {\n          next {\n            from\n            limit\n            __typename\n          }\n          __typename\n        }\n        users {\n          ...FollowList_publisher\n          __typename\n        }\n        __typename\n      }\n      ...UserCanonicalizer_user\n      ...FollowersHeader_publisher\n      ...NoFollows_publisher\n      __typename\n    }\n  }\n}\n\nfragment collectionUrl_collection on Collection {\n  id\n  domain\n  slug\n  __typename\n}\n\nfragment CollectionAvatar_collection on Collection {\n  name\n  avatar {\n    id\n    __typename\n  }\n  ...collectionUrl_collection\n  __typename\n  id\n}\n\nfragment SignInOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment SignUpOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment SusiModal_collection on Collection {\n  name\n  ...SignInOptions_collection\n  ...SignUpOptions_collection\n  __typename\n  id\n}\n\nfragment PublicationFollowButton_collection on Collection {\n  id\n  slug\n  name\n  ...SusiModal_collection\n  __typename\n}\n\nfragment PublicationFollowRow_collection on Collection {\n  __typename\n  id\n  name\n  description\n  ...CollectionAvatar_collection\n  ...PublicationFollowButton_collection\n}\n\nfragment userUrl_user on User {\n  __typename\n  id\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  hasSubdomain\n  username\n}\n\nfragment UserAvatar_user on User {\n  __typename\n  id\n  imageId\n  membership {\n    tier\n    __typename\n    id\n  }\n  name\n  username\n  ...userUrl_user\n}\n\nfragment isUserVerifiedBookAuthor_user on User {\n  verifications {\n    isBookAuthor\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment SignInOptions_user on User {\n  id\n  name\n  imageId\n  __typename\n}\n\nfragment SignUpOptions_user on User {\n  id\n  name\n  imageId\n  __typename\n}\n\nfragment SusiModal_user on User {\n  ...SignInOptions_user\n  ...SignUpOptions_user\n  __typename\n  id\n}\n\nfragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {\n  id\n  type\n  slug\n  name\n  collection {\n    slug\n    __typename\n    id\n  }\n  user {\n    id\n    name\n    username\n    newsletterV3 {\n      id\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment useNewsletterV3Subscription_user on User {\n  id\n  username\n  newsletterV3 {\n    ...useNewsletterV3Subscription_newsletterV3\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment useAuthorFollowSubscribeButton_user on User {\n  id\n  name\n  ...useNewsletterV3Subscription_user\n  __typename\n}\n\nfragment useAuthorFollowSubscribeButton_newsletterV3 on NewsletterV3 {\n  id\n  name\n  ...useNewsletterV3Subscription_newsletterV3\n  __typename\n}\n\nfragment AuthorFollowSubscribeButton_user on User {\n  id\n  name\n  imageId\n  ...SusiModal_user\n  ...useAuthorFollowSubscribeButton_user\n  newsletterV3 {\n    id\n    ...useAuthorFollowSubscribeButton_newsletterV3\n    __typename\n  }\n  __typename\n}\n\nfragment UserFollowRow_user on User {\n  __typename\n  id\n  name\n  bio\n  ...UserAvatar_user\n  ...isUserVerifiedBookAuthor_user\n  ...AuthorFollowSubscribeButton_user\n}\n\nfragment FollowsHeader_publisher on Publisher {\n  __typename\n  id\n  name\n  ... on Collection {\n    ...collectionUrl_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...userUrl_user\n    __typename\n    id\n  }\n}\n\nfragment FollowList_publisher on Publisher {\n  id\n  ... on Collection {\n    ...PublicationFollowRow_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...UserFollowRow_user\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment UserCanonicalizer_user on User {\n  id\n  username\n  hasSubdomain\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment FollowersHeader_publisher on Publisher {\n  ...FollowsHeader_publisher\n  ... on Collection {\n    subscriberCount\n    __typename\n    id\n  }\n  ... on User {\n    socialStats {\n      followerCount\n      __typename\n    }\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment NoFollows_publisher on Publisher {\n  id\n  name\n  __typename\n}\n"
    }
]

username : 調べたいユーザー名に置き換えてください。

Postman

  • POST https://medium.com/_/graphql

  • Body -> raw -> JSON

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[
    {
        "data": {
            "userResult": {
                "__typename": "User",
                "id": "8854784154b8",
                "followersUserConnection": {
                    "pagingInfo": {
                        "next": {
                            "from": "1140babdfef7",
                            "limit": 8,
                            "__typename": "PageParams"
                        },
                        "__typename": "Paging"
                    },
                    "users": [
                        ///.. 
                    ],
                    "__typename": "UserConnection"
                },
                "username": "zhgchgli",
                "hasSubdomain": true,
                "customDomainState": {
                    "live": {
                        "domain": "zhgchgli.medium.com",
                        "__typename": "CustomDomain"
                    },
                    "__typename": "CustomDomainState"
                },
                "name": "ZhgChgLi",
                "socialStats": {
                    "followerCount": 1050,
                    "__typename": "SocialStats"
                }
            }
        }
    }
]

重要な情報:response[0]["data"]["userResult"]["socialStats"]["followerCount"] が目標のフォロワー数です!

Ruby Demo Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def load_medium_followers(username)
    begin
        url = "https://medium.com/_/graphql"
        uri = URI.parse(url)

        payload = [
            {
            "operationName": "UserFollowers",
            "variables": {
                "id": nil,
                "username": username,
                "paging": nil
            },
            "query": "query UserFollowers($username: ID, $id: ID, $paging: PagingOptions) {\n  userResult(username: $username, id: $id) {\n    __typename\n    ... on User {\n      id\n      followersUserConnection(paging: $paging) {\n        pagingInfo {\n          next {\n            from\n            limit\n            __typename\n          }\n          __typename\n        }\n        users {\n          ...FollowList_publisher\n          __typename\n        }\n        __typename\n      }\n      ...UserCanonicalizer_user\n      ...FollowersHeader_publisher\n      ...NoFollows_publisher\n      __typename\n    }\n  }\n}\n\nfragment collectionUrl_collection on Collection {\n  id\n  domain\n  slug\n  __typename\n}\n\nfragment CollectionAvatar_collection on Collection {\n  name\n  avatar {\n    id\n    __typename\n  }\n  ...collectionUrl_collection\n  __typename\n  id\n}\n\nfragment SignInOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment SignUpOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment SusiModal_collection on Collection {\n  name\n  ...SignInOptions_collection\n  ...SignUpOptions_collection\n  __typename\n  id\n}\n\nfragment PublicationFollowButton_collection on Collection {\n  id\n  slug\n  name\n  ...SusiModal_collection\n  __typename\n}\n\nfragment PublicationFollowRow_collection on Collection {\n  __typename\n  id\n  name\n  description\n  ...CollectionAvatar_collection\n  ...PublicationFollowButton_collection\n}\n\nfragment userUrl_user on User {\n  __typename\n  id\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  hasSubdomain\n  username\n}\n\nfragment UserAvatar_user on User {\n  __typename\n  id\n  imageId\n  membership {\n    tier\n    __typename\n    id\n  }\n  name\n  username\n  ...userUrl_user\n}\n\nfragment isUserVerifiedBookAuthor_user on User {\n  verifications {\n    isBookAuthor\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment SignInOptions_user on User {\n  id\n  name\n  imageId\n  __typename\n}\n\nfragment SignUpOptions_user on User {\n  id\n  name\n  imageId\n  __typename\n}\n\nfragment SusiModal_user on User {\n  ...SignInOptions_user\n  ...SignUpOptions_user\n  __typename\n  id\n}\n\nfragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {\n  id\n  type\n  slug\n  name\n  collection {\n    slug\n    __typename\n    id\n  }\n  user {\n    id\n    name\n    username\n    newsletterV3 {\n      id\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment useNewsletterV3Subscription_user on User {\n  id\n  username\n  newsletterV3 {\n    ...useNewsletterV3Subscription_newsletterV3\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment useAuthorFollowSubscribeButton_user on User {\n  id\n  name\n  ...useNewsletterV3Subscription_user\n  __typename\n}\n\nfragment useAuthorFollowSubscribeButton_newsletterV3 on NewsletterV3 {\n  id\n  name\n  ...useNewsletterV3Subscription_newsletterV3\n  __typename\n}\n\nfragment AuthorFollowSubscribeButton_user on User {\n  id\n  name\n  imageId\n  ...SusiModal_user\n  ...useAuthorFollowSubscribeButton_user\n  newsletterV3 {\n    id\n    ...useAuthorFollowSubscribeButton_newsletterV3\n    __typename\n  }\n  __typename\n}\n\nfragment UserFollowRow_user on User {\n  __typename\n  id\n  name\n  bio\n  ...UserAvatar_user\n  ...isUserVerifiedBookAuthor_user\n  ...AuthorFollowSubscribeButton_user\n}\n\nfragment FollowsHeader_publisher on Publisher {\n  __typename\n  id\n  name\n  ... on Collection {\n    ...collectionUrl_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...userUrl_user\n    __typename\n    id\n  }\n}\n\nfragment FollowList_publisher on Publisher {\n  id\n  ... on Collection {\n    ...PublicationFollowRow_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...UserFollowRow_user\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment UserCanonicalizer_user on User {\n  id\n  username\n  hasSubdomain\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment FollowersHeader_publisher on Publisher {\n  ...FollowsHeader_publisher\n  ... on Collection {\n    subscriberCount\n    __typename\n    id\n  }\n  ... on User {\n    socialStats {\n      followerCount\n      __typename\n    }\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment NoFollows_publisher on Publisher {\n  id\n  name\n  __typename\n}\n"
            }
        ];

        headers = {
            "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0",
            "Content-Type" => "application/json"
        }

        https = Net::HTTP.new(uri.host, uri.port)
        https.read_timeout = 30
        https.open_timeout = 10
        https.use_ssl = true

        # --- TLS / 証明書検証の設定 ---
        # 一部のOpenSSLのビルド/設定ではCRLチェックが有効になっており、
        # 「certificate verify failed (unable to get certificate CRL)」というエラーが発生することがあります。
        # Net::HTTP/OpenSSLは自動でCRLを取得しないため、デフォルトの
        # 証明書ストアを使用し、CRL関連のフラグをクリアして
        # ピア証明書の検証は行いながらも、致命的な失敗を避けます。
        https.verify_mode = OpenSSL::SSL::VERIFY_PEER

        store = OpenSSL::X509::Store.new
        store.set_default_paths
        # CRLチェックのフラグをデフォルトで無効化
        store.flags = 0
        https.cert_store = store

        # 必要に応じて環境変数でCAバンドルのパスを上書き可能
        if ENV['SSL_CERT_FILE'] && !ENV['SSL_CERT_FILE'].empty?
        https.ca_file = ENV['SSL_CERT_FILE']
        end
        if ENV['SSL_CERT_DIR'] && !ENV['SSL_CERT_DIR'].empty?
        https.ca_path = ENV['SSL_CERT_DIR']
        end

        # (任意) ネットワーク問題でのハングを防ぐためのタイムアウト設定
        https.open_timeout = 10
        https.read_timeout = 30
        # --- TLS設定終了 ---


        req = Net::HTTP::Post.new(uri.request_uri, headers)
        req.body = JSON.dump(payload)

        res = https.request(req)

        json = JSON.parse(res.body)
        count = json&.dig(0, "data", "userResult", "socialStats", "followerCount")
        count ? count.to_i : 0
        return "1K+" unless count.to_i > 0
        return "#{count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse}+"
    rescue => e
        "1K+"
    end
end

Medium Private API — アカウントの全記事を取得する

同じ方法で、ターゲットアカウントの投稿リストも取得できます。

Graphql クエリ本文:

1
2
3
4
5
6
7
8
9
10
11
12
[
      {
        "operationName": "UserProfileQuery",
        "variables": {
          "homepagePostsFrom": null,
          "includeDistributedResponses": true,
          "id": "8854784154b8",
          "homepagePostsLimit": 10
        },
        "query": "query UserProfileQuery($id: ID, $username: ID, $homepagePostsLimit: PaginationLimit, $homepagePostsFrom: String = null, $includeDistributedResponses: Boolean = true) {\n  userResult(id: $id, username: $username) {\n    __typename\n    ... on User {\n      id\n      name\n      viewerIsUser\n      viewerEdge {\n        id\n        isFollowing\n        __typename\n      }\n      homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {\n        posts {\n          id\n          __typename\n        }\n        __typename\n      }\n      ...UserCanonicalizer_user\n      ...UserProfileScreen_user\n      ...EntityDrivenSubscriptionLandingPageScreen_writer\n      ...useShouldShowEntityDrivenSubscription_creator\n      __typename\n    }\n  }\n}\n\nfragment UserCanonicalizer_user on User {\n  id\n  username\n  hasSubdomain\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment UserProfileScreen_user on User {\n  __typename\n  id\n  viewerIsUser\n  ...PublisherHeader_publisher\n  ...PublisherHomePosts_publisher\n  ...UserSubdomainFlow_user\n  ...UserProfileMetadata_user\n  ...SuspendedBannerLoader_user\n  ...ExpandablePost_user\n  ...useAnalytics_user\n}\n\nfragment PublisherHeader_publisher on Publisher {\n  id\n  ...PublisherHeaderBackground_publisher\n  ...PublisherHeaderNameplate_publisher\n  ...PublisherHeaderActions_publisher\n  ...PublisherHeaderNav_publisher\n  __typename\n}\n\nfragment PublisherHeaderBackground_publisher on Publisher {\n  __typename\n  id\n  customStyleSheet {\n    ...PublisherHeaderBackground_customStyleSheet\n    __typename\n    id\n  }\n  ... on Collection {\n    colorPalette {\n      tintBackgroundSpectrum {\n        backgroundColor\n        __typename\n      }\n      __typename\n    }\n    isAuroraVisible\n    legacyHeaderBackgroundImage {\n      id\n      originalWidth\n      focusPercentX\n      focusPercentY\n      __typename\n    }\n    ...collectionTintBackgroundTheme_collection\n    __typename\n    id\n  }\n  ...publisherUrl_publisher\n}\n\nfragment PublisherHeaderBackground_customStyleSheet on CustomStyleSheet {\n  id\n  global {\n    colorPalette {\n      background {\n        rgb\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  header {\n    headerScale\n    backgroundImageDisplayMode\n    backgroundImageVerticalAlignment\n    backgroundColorDisplayMode\n    backgroundColor {\n      alpha\n      rgb\n      ...getHexFromColorValue_colorValue\n      ...getOpaqueHexFromColorValue_colorValue\n      __typename\n    }\n    secondaryBackgroundColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    postBackgroundColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    backgroundImage {\n      ...MetaHeaderBackground_imageMetadata\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment getHexFromColorValue_colorValue on ColorValue {\n  rgb\n  alpha\n  __typename\n}\n\nfragment getOpaqueHexFromColorValue_colorValue on ColorValue {\n  rgb\n  __typename\n}\n\nfragment MetaHeaderBackground_imageMetadata on ImageMetadata {\n  id\n  originalWidth\n  __typename\n}\n\nfragment collectionTintBackgroundTheme_collection on Collection {\n  colorPalette {\n    ...collectionTintBackgroundTheme_colorPalette\n    __typename\n  }\n  customStyleSheet {\n    id\n    ...collectionTintBackgroundTheme_customStyleSheet\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment collectionTintBackgroundTheme_colorPalette on ColorPalette {\n  ...customTintBackgroundTheme_colorPalette\n  __typename\n}\n\nfragment customTintBackgroundTheme_colorPalette on ColorPalette {\n  tintBackgroundSpectrum {\n    ...ThemeUtil_colorSpectrum\n    __typename\n  }\n  __typename\n}\n\nfragment ThemeUtil_colorSpectrum on ColorSpectrum {\n  backgroundColor\n  ...ThemeUtilInterpolateHelpers_colorSpectrum\n  __typename\n}\n\nfragment ThemeUtilInterpolateHelpers_colorSpectrum on ColorSpectrum {\n  colorPoints {\n    ...ThemeUtil_colorPoint\n    __typename\n  }\n  __typename\n}\n\nfragment ThemeUtil_colorPoint on ColorPoint {\n  color\n  point\n  __typename\n}\n\nfragment collectionTintBackgroundTheme_customStyleSheet on CustomStyleSheet {\n  id\n  ...customTintBackgroundTheme_customStyleSheet\n  __typename\n}\n\nfragment customTintBackgroundTheme_customStyleSheet on CustomStyleSheet {\n  id\n  global {\n    colorPalette {\n      primary {\n        colorPalette {\n          ...customTintBackgroundTheme_colorPalette\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment publisherUrl_publisher on Publisher {\n  id\n  __typename\n  ... on Collection {\n    ...collectionUrl_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...userUrl_user\n    __typename\n    id\n  }\n}\n\nfragment collectionUrl_collection on Collection {\n  id\n  domain\n  slug\n  __typename\n}\n\nfragment userUrl_user on User {\n  __typename\n  id\n  customDomainState {\n    live {\n      domain\n      __typename\n    }\n    __typename\n  }\n  hasSubdomain\n  username\n}\n\nfragment PublisherHeaderNameplate_publisher on Publisher {\n  ...PublisherAvatar_publisher\n  ...PublisherHeaderLogo_publisher\n  ...PublisherFollowerCount_publisher\n  __typename\n}\n\nfragment PublisherAvatar_publisher on Publisher {\n  __typename\n  ... on Collection {\n    id\n    ...CollectionAvatar_collection\n    __typename\n  }\n  ... on User {\n    id\n    ...UserAvatar_user\n    __typename\n  }\n}\n\nfragment CollectionAvatar_collection on Collection {\n  name\n  avatar {\n    id\n    __typename\n  }\n  ...collectionUrl_collection\n  __typename\n  id\n}\n\nfragment UserAvatar_user on User {\n  __typename\n  id\n  imageId\n  mediumMemberAt\n  name\n  username\n  ...userUrl_user\n}\n\nfragment PublisherHeaderLogo_publisher on Publisher {\n  __typename\n  id\n  customStyleSheet {\n    id\n    header {\n      logoImage {\n        id\n        originalHeight\n        originalWidth\n        __typename\n      }\n      appNameColor {\n        ...getHexFromColorValue_colorValue\n        __typename\n      }\n      appNameTreatment\n      __typename\n    }\n    __typename\n  }\n  name\n  ... on Collection {\n    isAuroraVisible\n    logo {\n      id\n      originalHeight\n      originalWidth\n      __typename\n    }\n    __typename\n    id\n  }\n  ...CustomHeaderTooltip_publisher\n  ...publisherUrl_publisher\n}\n\nfragment CustomHeaderTooltip_publisher on Publisher {\n  __typename\n  id\n  customStyleSheet {\n    id\n    header {\n      appNameTreatment\n      nameTreatment\n      __typename\n    }\n    __typename\n  }\n  ... on Collection {\n    isAuroraVisible\n    slug\n    __typename\n    id\n  }\n}\n\nfragment PublisherFollowerCount_publisher on Publisher {\n  __typename\n  id\n  ... on Collection {\n    slug\n    subscriberCount\n    __typename\n    id\n  }\n  ... on User {\n    socialStats {\n      followerCount\n      __typename\n    }\n    username\n    __typename\n    id\n  }\n}\n\nfragment PublisherHeaderActions_publisher on Publisher {\n  __typename\n  ...MetaHeaderPubMenu_publisher\n  ... on Collection {\n    ...CollectionFollowButton_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...FollowAndSubscribeButtons_user\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderPubMenu_publisher on Publisher {\n  __typename\n  ... on Collection {\n    ...MetaHeaderPubMenu_publisher_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...MetaHeaderPubMenu_publisher_user\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderPubMenu_publisher_collection on Collection {\n  id\n  slug\n  name\n  domain\n  newsletterV3 {\n    slug\n    __typename\n    id\n  }\n  ...MutePopoverOptions_collection\n  __typename\n}\n\nfragment MutePopoverOptions_collection on Collection {\n  id\n  __typename\n}\n\nfragment MetaHeaderPubMenu_publisher_user on User {\n  id\n  username\n  ...MutePopoverOptions_creator\n  __typename\n}\n\nfragment MutePopoverOptions_creator on User {\n  id\n  __typename\n}\n\nfragment CollectionFollowButton_collection on Collection {\n  __typename\n  id\n  name\n  slug\n  ...collectionUrl_collection\n  ...SusiClickable_collection\n}\n\nfragment SusiClickable_collection on Collection {\n  ...SusiContainer_collection\n  __typename\n  id\n}\n\nfragment SusiContainer_collection on Collection {\n  name\n  ...SignInOptions_collection\n  ...SignUpOptions_collection\n  __typename\n  id\n}\n\nfragment SignInOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment SignUpOptions_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment FollowAndSubscribeButtons_user on User {\n  ...UserFollowButton_user\n  ...UserSubscribeButton_user\n  __typename\n  id\n}\n\nfragment UserFollowButton_user on User {\n  ...UserFollowButtonSignedIn_user\n  ...UserFollowButtonSignedOut_user\n  __typename\n  id\n}\n\nfragment UserFollowButtonSignedIn_user on User {\n  id\n  __typename\n}\n\nfragment UserFollowButtonSignedOut_user on User {\n  id\n  ...SusiClickable_user\n  __typename\n}\n\nfragment SusiClickable_user on User {\n  ...SusiContainer_user\n  __typename\n  id\n}\n\nfragment SusiContainer_user on User {\n  ...SignInOptions_user\n  ...SignUpOptions_user\n  __typename\n  id\n}\n\nfragment SignInOptions_user on User {\n  id\n  name\n  __typename\n}\n\nfragment SignUpOptions_user on User {\n  id\n  name\n  __typename\n}\n\nfragment UserSubscribeButton_user on User {\n  id\n  isPartnerProgramEnrolled\n  name\n  viewerEdge {\n    id\n    isFollowing\n    isUser\n    __typename\n  }\n  viewerIsUser\n  newsletterV3 {\n    id\n    ...useNewsletterV3Subscription_newsletterV3\n    __typename\n  }\n  ...useNewsletterV3Subscription_user\n  ...MembershipUpsellModal_user\n  __typename\n}\n\nfragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {\n  id\n  type\n  slug\n  name\n  collection {\n    slug\n    __typename\n    id\n  }\n  user {\n    id\n    name\n    username\n    newsletterV3 {\n      id\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment useNewsletterV3Subscription_user on User {\n  id\n  username\n  newsletterV3 {\n    ...useNewsletterV3Subscription_newsletterV3\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment MembershipUpsellModal_user on User {\n  id\n  name\n  imageId\n  postSubscribeMembershipUpsellShownAt\n  newsletterV3 {\n    id\n    __typename\n  }\n  __typename\n}\n\nfragment PublisherHeaderNav_publisher on Publisher {\n  __typename\n  id\n  customStyleSheet {\n    navigation {\n      navItems {\n        name\n        ...PublisherHeaderNavLink_headerNavigationItem\n        __typename\n      }\n      __typename\n    }\n    __typename\n    id\n  }\n  ...PublisherHeaderNavLink_publisher\n  ... on Collection {\n    domain\n    isAuroraVisible\n    slug\n    navItems {\n      tagSlug\n      title\n      url\n      __typename\n    }\n    __typename\n    id\n  }\n  ... on User {\n    customDomainState {\n      live {\n        domain\n        __typename\n      }\n      __typename\n    }\n    hasSubdomain\n    username\n    about\n    homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {\n      posts {\n        id\n        __typename\n      }\n      __typename\n    }\n    __typename\n    id\n  }\n}\n\nfragment PublisherHeaderNavLink_headerNavigationItem on HeaderNavigationItem {\n  href\n  name\n  tags {\n    id\n    normalizedTagSlug\n    __typename\n  }\n  type\n  __typename\n}\n\nfragment PublisherHeaderNavLink_publisher on Publisher {\n  __typename\n  id\n  ... on Collection {\n    slug\n    __typename\n    id\n  }\n}\n\nfragment PublisherHomePosts_publisher on Publisher {\n  __typename\n  id\n  homepagePostsConnection(\n    paging: {limit: $homepagePostsLimit, from: $homepagePostsFrom}\n    includeDistributedResponses: $includeDistributedResponses\n  ) {\n    posts {\n      ...PublisherHomePosts_post\n      __typename\n    }\n    pagingInfo {\n      next {\n        from\n        limit\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  ...CardByline_publisher\n  ...NewsletterV3Promo_publisher\n  ...PublisherHomePosts_user\n}\n\nfragment PublisherHomePosts_post on Post {\n  id\n  collection {\n    id\n    name\n    ...collectionUrl_collection\n    __typename\n  }\n  ...ExpandablePost_post\n  __typename\n}\n\nfragment ExpandablePost_post on Post {\n  id\n  creator {\n    ...ExpandablePost_user\n    __typename\n    id\n  }\n  collection {\n    ...CardByline_collection\n    __typename\n    id\n  }\n  ...InteractivePostBody_postPreview\n  firstPublishedAt\n  isLocked\n  isSeries\n  isShortform\n  latestPublishedAt\n  inResponseToCatalogResult {\n    __typename\n  }\n  mediumUrl\n  postResponses {\n    count\n    __typename\n  }\n  previewImage {\n    id\n    focusPercentX\n    focusPercentY\n    __typename\n  }\n  readingTime\n  sequence {\n    slug\n    __typename\n  }\n  title\n  uniqueSlug\n  visibility\n  ...CardByline_post\n  ...ExpandablePostFooter_post\n  ...InResponseToEntityPreview_post\n  ...PostScrollTracker_post\n  ...ReadMore_post\n  ...HighDensityPreview_post\n  __typename\n}\n\nfragment ExpandablePost_user on User {\n  __typename\n  name\n  username\n  ...CardByline_user\n  id\n}\n\nfragment CardByline_user on User {\n  __typename\n  id\n  name\n  username\n  mediumMemberAt\n  socialStats {\n    followerCount\n    __typename\n  }\n  ...userUrl_user\n  ...UserMentionTooltip_user\n}\n\nfragment UserMentionTooltip_user on User {\n  id\n  name\n  username\n  bio\n  imageId\n  mediumMemberAt\n  ...UserAvatar_user\n  ...UserFollowButton_user\n  __typename\n}\n\nfragment CardByline_collection on Collection {\n  __typename\n  id\n  name\n  ...collectionUrl_collection\n}\n\nfragment InteractivePostBody_postPreview on Post {\n  extendedPreviewContent(\n    truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n  ) {\n    bodyModel {\n      ...PostBody_bodyModel\n      __typename\n    }\n    isFullContent\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment PostBody_bodyModel on RichText {\n  sections {\n    name\n    startIndex\n    textLayout\n    imageLayout\n    backgroundImage {\n      id\n      originalHeight\n      originalWidth\n      __typename\n    }\n    videoLayout\n    backgroundVideo {\n      videoId\n      originalHeight\n      originalWidth\n      previewImageId\n      __typename\n    }\n    __typename\n  }\n  paragraphs {\n    id\n    ...PostBodySection_paragraph\n    __typename\n  }\n  ...normalizedBodyModel_richText\n  __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n  name\n  ...PostBodyParagraph_paragraph\n  __typename\n  id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n  name\n  type\n  ...ImageParagraph_paragraph\n  ...TextParagraph_paragraph\n  ...IframeParagraph_paragraph\n  ...MixtapeParagraph_paragraph\n  __typename\n  id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n  href\n  layout\n  metadata {\n    id\n    originalHeight\n    originalWidth\n    focusPercentX\n    focusPercentY\n    alt\n    __typename\n  }\n  ...Markups_paragraph\n  ...ParagraphRefsMapContext_paragraph\n  ...PostAnnotationsMarker_paragraph\n  __typename\n  id\n}\n\nfragment Markups_paragraph on Paragraph {\n  name\n  text\n  hasDropCap\n  dropCapImage {\n    ...MarkupNode_data_dropCapImage\n    __typename\n    id\n  }\n  markups {\n    type\n    start\n    end\n    href\n    anchorType\n    userId\n    linkMetadata {\n      httpStatus\n      __typename\n    }\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n  ...DropCap_image\n  __typename\n  id\n}\n\nfragment DropCap_image on ImageMetadata {\n  id\n  originalHeight\n  originalWidth\n  __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n  id\n  name\n  text\n  __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n  ...PostViewNoteCard_paragraph\n  __typename\n  id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n  type\n  hasDropCap\n  ...Markups_paragraph\n  ...ParagraphRefsMapContext_paragraph\n  __typename\n  id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n  iframe {\n    mediaResource {\n      id\n      iframeSrc\n      iframeHeight\n      iframeWidth\n      title\n      __typename\n    }\n    __typename\n  }\n  layout\n  ...getEmbedlyCardUrlParams_paragraph\n  ...Markups_paragraph\n  __typename\n  id\n}\n\nfragment getEmbedlyCardUrlParams_paragraph on Paragraph {\n  type\n  iframe {\n    mediaResource {\n      iframeSrc\n      __typename\n    }\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n  type\n  mixtapeMetadata {\n    href\n    mediaResource {\n      mediumCatalog {\n        id\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  ...GenericMixtapeParagraph_paragraph\n  __typename\n  id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n  text\n  mixtapeMetadata {\n    href\n    thumbnailImageId\n    __typename\n  }\n  markups {\n    start\n    end\n    type\n    href\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n  paragraphs {\n    markups {\n      type\n      __typename\n    }\n    ...getParagraphHighlights_paragraph\n    ...getParagraphPrivateNotes_paragraph\n    __typename\n  }\n  sections {\n    startIndex\n    ...getSectionEndIndex_section\n    __typename\n  }\n  ...getParagraphStyles_richText\n  ...getParagraphSpaces_richText\n  __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment getSectionEndIndex_section on Section {\n  startIndex\n  __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n  paragraphs {\n    text\n    type\n    __typename\n  }\n  sections {\n    ...getSectionEndIndex_section\n    __typename\n  }\n  __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n  paragraphs {\n    layout\n    metadata {\n      originalHeight\n      originalWidth\n      __typename\n    }\n    type\n    ...paragraphExtendsImageGrid_paragraph\n    __typename\n  }\n  ...getSeriesParagraphTopSpacings_richText\n  ...getPostParagraphTopSpacings_richText\n  __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n  layout\n  type\n  __typename\n  id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n  paragraphs {\n    id\n    __typename\n  }\n  sections {\n    startIndex\n    __typename\n  }\n  __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n  paragraphs {\n    layout\n    text\n    __typename\n  }\n  sections {\n    startIndex\n    __typename\n  }\n  __typename\n}\n\nfragment CardByline_post on Post {\n  ...DraftStatus_post\n  __typename\n  id\n}\n\nfragment DraftStatus_post on Post {\n  id\n  pendingCollection {\n    id\n    creator {\n      id\n      __typename\n    }\n    ...BoldCollectionName_collection\n    __typename\n  }\n  statusForCollection\n  creator {\n    id\n    __typename\n  }\n  isPublished\n  __typename\n}\n\nfragment BoldCollectionName_collection on Collection {\n  id\n  name\n  __typename\n}\n\nfragment ExpandablePostFooter_post on Post {\n  id\n  allowResponses\n  postResponses {\n    count\n    __typename\n  }\n  isLimitedState\n  ...ExpandablePostCardOverflowButton_post\n  ...BookmarkButton_post\n  ...PostFooterSocialPopover_post\n  ...MultiVote_post\n  ...OverflowMenuButtonWithNegativeSignal_post\n  __typename\n}\n\nfragment ExpandablePostCardOverflowButton_post on Post {\n  creator {\n    id\n    __typename\n  }\n  ...ExpandablePostCardEditorWriterButton_post\n  ...ExpandablePostCardReaderButton_post\n  __typename\n  id\n}\n\nfragment ExpandablePostCardEditorWriterButton_post on Post {\n  id\n  collection {\n    id\n    name\n    slug\n    __typename\n  }\n  allowResponses\n  clapCount\n  visibility\n  mediumUrl\n  responseDistribution\n  ...useIsPinnedInContext_post\n  ...CopyFriendLinkMenuItem_post\n  ...NewsletterV3EmailToSubscribersMenuItem_post\n  ...OverflowMenuItemUndoClaps_post\n  __typename\n}\n\nfragment useIsPinnedInContext_post on Post {\n  id\n  collection {\n    id\n    __typename\n  }\n  pendingCollection {\n    id\n    __typename\n  }\n  pinnedAt\n  pinnedByCreatorAt\n  __typename\n}\n\nfragment CopyFriendLinkMenuItem_post on Post {\n  id\n  __typename\n}\n\nfragment NewsletterV3EmailToSubscribersMenuItem_post on Post {\n  id\n  creator {\n    id\n    newsletterV3 {\n      id\n      subscribersCount\n      __typename\n    }\n    __typename\n  }\n  isNewsletter\n  isAuthorNewsletter\n  __typename\n}\n\nfragment OverflowMenuItemUndoClaps_post on Post {\n  id\n  clapCount\n  ...ClapMutation_post\n  __typename\n}\n\nfragment ClapMutation_post on Post {\n  __typename\n  id\n  clapCount\n  ...MultiVoteCount_post\n}\n\nfragment MultiVoteCount_post on Post {\n  id\n  ...PostVotersNetwork_post\n  __typename\n}\n\nfragment PostVotersNetwork_post on Post {\n  id\n  voterCount\n  recommenders {\n    name\n    __typename\n  }\n  __typename\n}\n\nfragment ExpandablePostCardReaderButton_post on Post {\n  id\n  collection {\n    id\n    __typename\n  }\n  creator {\n    id\n    __typename\n  }\n  clapCount\n  ...ClapMutation_post\n  __typename\n}\n\nfragment BookmarkButton_post on Post {\n  visibility\n  ...SusiClickable_post\n  ...AddToCatalogBookmarkButton_post\n  __typename\n  id\n}\n\nfragment SusiClickable_post on Post {\n  id\n  mediumUrl\n  ...SusiContainer_post\n  __typename\n}\n\nfragment SusiContainer_post on Post {\n  id\n  __typename\n}\n\nfragment AddToCatalogBookmarkButton_post on Post {\n  ...AddToCatalogBase_post\n  __typename\n  id\n}\n\nfragment AddToCatalogBase_post on Post {\n  id\n  __typename\n}\n\nfragment PostFooterSocialPopover_post on Post {\n  id\n  mediumUrl\n  title\n  ...SharePostButton_post\n  __typename\n}\n\nfragment SharePostButton_post on Post {\n  id\n  __typename\n}\n\nfragment MultiVote_post on Post {\n  id\n  clapCount\n  creator {\n    id\n    ...SusiClickable_user\n    __typename\n  }\n  isPublished\n  ...SusiClickable_post\n  collection {\n    id\n    slug\n    __typename\n  }\n  isLimitedState\n  ...MultiVoteCount_post\n  __typename\n}\n\nfragment OverflowMenuButtonWithNegativeSignal_post on Post {\n  id\n  ...OverflowMenuWithNegativeSignal_post\n  ...CreatorActionOverflowPopover_post\n  __typename\n}\n\nfragment OverflowMenuWithNegativeSignal_post on Post {\n  id\n  creator {\n    id\n    __typename\n  }\n  collection {\n    id\n    __typename\n  }\n  ...OverflowMenuItemUndoClaps_post\n  __typename\n}\n\nfragment CreatorActionOverflowPopover_post on Post {\n  allowResponses\n  id\n  statusForCollection\n  isLocked\n  isPublished\n  clapCount\n  mediumUrl\n  pinnedAt\n  pinnedByCreatorAt\n  curationEligibleAt\n  mediumUrl\n  responseDistribution\n  visibility\n  inResponseToPostResult {\n    __typename\n  }\n  inResponseToCatalogResult {\n    __typename\n  }\n  pendingCollection {\n    id\n    name\n    creator {\n      id\n      __typename\n    }\n    avatar {\n      id\n      __typename\n    }\n    domain\n    slug\n    __typename\n  }\n  creator {\n    id\n    ...MutePopoverOptions_creator\n    ...auroraHooks_publisher\n    __typename\n  }\n  collection {\n    id\n    name\n    creator {\n      id\n      __typename\n    }\n    avatar {\n      id\n      __typename\n    }\n    domain\n    slug\n    ...MutePopoverOptions_collection\n    ...auroraHooks_publisher\n    __typename\n  }\n  ...useIsPinnedInContext_post\n  ...NewsletterV3EmailToSubscribersMenuItem_post\n  ...OverflowMenuItemUndoClaps_post\n  __typename\n}\n\nfragment auroraHooks_publisher on Publisher {\n  __typename\n  ... on Collection {\n    isAuroraEligible\n    isAuroraVisible\n    viewerEdge {\n      id\n      isEditor\n      __typename\n    }\n    __typename\n    id\n  }\n  ... on User {\n    isAuroraVisible\n    __typename\n    id\n  }\n}\n\nfragment InResponseToEntityPreview_post on Post {\n  id\n  inResponseToEntityType\n  __typename\n}\n\nfragment PostScrollTracker_post on Post {\n  id\n  collection {\n    id\n    __typename\n  }\n  sequence {\n    sequenceId\n    __typename\n  }\n  __typename\n}\n\nfragment ReadMore_post on Post {\n  mediumUrl\n  readingTime\n  ...usePostUrl_post\n  __typename\n  id\n}\n\nfragment usePostUrl_post on Post {\n  id\n  creator {\n    ...userUrl_user\n    __typename\n    id\n  }\n  collection {\n    id\n    domain\n    slug\n    __typename\n  }\n  isSeries\n  mediumUrl\n  sequence {\n    slug\n    __typename\n  }\n  uniqueSlug\n  __typename\n}\n\nfragment HighDensityPreview_post on Post {\n  id\n  title\n  previewImage {\n    id\n    focusPercentX\n    focusPercentY\n    __typename\n  }\n  extendedPreviewContent(\n    truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n  ) {\n    subtitle\n    __typename\n  }\n  ...HighDensityFooter_post\n  __typename\n}\n\nfragment HighDensityFooter_post on Post {\n  id\n  readingTime\n  tags {\n    ...TopicPill_tag\n    __typename\n  }\n  ...BookmarkButton_post\n  ...ExpandablePostCardOverflowButton_post\n  ...OverflowMenuButtonWithNegativeSignal_post\n  __typename\n}\n\nfragment TopicPill_tag on Tag {\n  __typename\n  id\n  displayTitle\n}\n\nfragment CardByline_publisher on Publisher {\n  __typename\n  ... on User {\n    id\n    ...CardByline_user\n    __typename\n  }\n  ... on Collection {\n    id\n    ...CardByline_collection\n    __typename\n  }\n}\n\nfragment NewsletterV3Promo_publisher on Publisher {\n  __typename\n  ... on User {\n    ...NewsletterV3Promo_publisher_User\n    __typename\n    id\n  }\n  ... on Collection {\n    ...NewsletterV3Promo_publisher_Collection\n    __typename\n    id\n  }\n}\n\nfragment NewsletterV3Promo_publisher_User on User {\n  id\n  username\n  name\n  viewerIsUser\n  newsletterV3 {\n    id\n    ...NewsletterV3Promo_newsletterV3\n    __typename\n  }\n  __typename\n}\n\nfragment NewsletterV3Promo_newsletterV3 on NewsletterV3 {\n  slug\n  name\n  description\n  promoHeadline\n  promoBody\n  ...NewsletterV3AmpButton_newsletterV3\n  ...NewsletterV3SubscribeButton_newsletterV3\n  ...NewsletterV3SubscribeByEmail_newsletterV3\n  __typename\n  id\n}\n\nfragment NewsletterV3AmpButton_newsletterV3 on NewsletterV3 {\n  id\n  collection {\n    ...collectionDefaultBackgroundTheme_collection\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment collectionDefaultBackgroundTheme_collection on Collection {\n  colorPalette {\n    ...collectionDefaultBackgroundTheme_colorPalette\n    __typename\n  }\n  customStyleSheet {\n    id\n    ...collectionDefaultBackgroundTheme_customStyleSheet\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment collectionDefaultBackgroundTheme_colorPalette on ColorPalette {\n  ...customDefaultBackgroundTheme_colorPalette\n  __typename\n}\n\nfragment customDefaultBackgroundTheme_colorPalette on ColorPalette {\n  highlightSpectrum {\n    ...ThemeUtil_colorSpectrum\n    __typename\n  }\n  defaultBackgroundSpectrum {\n    ...ThemeUtil_colorSpectrum\n    __typename\n  }\n  tintBackgroundSpectrum {\n    ...ThemeUtil_colorSpectrum\n    __typename\n  }\n  __typename\n}\n\nfragment collectionDefaultBackgroundTheme_customStyleSheet on CustomStyleSheet {\n  id\n  ...customDefaultBackgroundTheme_customStyleSheet\n  __typename\n}\n\nfragment customDefaultBackgroundTheme_customStyleSheet on CustomStyleSheet {\n  id\n  global {\n    colorPalette {\n      primary {\n        colorPalette {\n          ...customDefaultBackgroundTheme_colorPalette\n          __typename\n        }\n        __typename\n      }\n      background {\n        colorPalette {\n          ...customDefaultBackgroundTheme_colorPalette\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment NewsletterV3SubscribeButton_newsletterV3 on NewsletterV3 {\n  id\n  name\n  slug\n  type\n  user {\n    id\n    name\n    username\n    __typename\n  }\n  collection {\n    slug\n    ...SusiClickable_collection\n    ...collectionDefaultBackgroundTheme_collection\n    __typename\n    id\n  }\n  ...SusiClickable_newsletterV3\n  ...useNewsletterV3Subscription_newsletterV3\n  __typename\n}\n\nfragment SusiClickable_newsletterV3 on NewsletterV3 {\n  ...SusiContainer_newsletterV3\n  __typename\n  id\n}\n\nfragment SusiContainer_newsletterV3 on NewsletterV3 {\n  ...SignInOptions_newsletterV3\n  ...SignUpOptions_newsletterV3\n  __typename\n  id\n}\n\nfragment SignInOptions_newsletterV3 on NewsletterV3 {\n  id\n  name\n  __typename\n}\n\nfragment SignUpOptions_newsletterV3 on NewsletterV3 {\n  id\n  name\n  __typename\n}\n\nfragment NewsletterV3SubscribeByEmail_newsletterV3 on NewsletterV3 {\n  id\n  slug\n  type\n  user {\n    id\n    name\n    username\n    __typename\n  }\n  collection {\n    ...collectionDefaultBackgroundTheme_collection\n    ...collectionUrl_collection\n    __typename\n    id\n  }\n  __typename\n}\n\nfragment NewsletterV3Promo_publisher_Collection on Collection {\n  id\n  slug\n  domain\n  name\n  newsletterV3 {\n    id\n    ...NewsletterV3Promo_newsletterV3\n    __typename\n  }\n  __typename\n}\n\nfragment PublisherHomePosts_user on User {\n  id\n  ...useShowAuthorNewsletterV3Promo_user\n  __typename\n}\n\nfragment useShowAuthorNewsletterV3Promo_user on User {\n  id\n  username\n  newsletterV3 {\n    id\n    showPromo\n    slug\n    __typename\n  }\n  __typename\n}\n\nfragment UserSubdomainFlow_user on User {\n  id\n  hasCompletedProfile\n  name\n  bio\n  imageId\n  ...UserCompleteProfileDialog_user\n  ...UserSubdomainOnboardingDialog_user\n  __typename\n}\n\nfragment UserCompleteProfileDialog_user on User {\n  id\n  name\n  bio\n  imageId\n  hasCompletedProfile\n  __typename\n}\n\nfragment UserSubdomainOnboardingDialog_user on User {\n  id\n  customDomainState {\n    pending {\n      status\n      __typename\n    }\n    live {\n      status\n      __typename\n    }\n    __typename\n  }\n  username\n  __typename\n}\n\nfragment UserProfileMetadata_user on User {\n  id\n  username\n  name\n  bio\n  socialStats {\n    followerCount\n    followingCount\n    __typename\n  }\n  ...userUrl_user\n  ...UserProfileMetadataHelmet_user\n  __typename\n}\n\nfragment UserProfileMetadataHelmet_user on User {\n  username\n  name\n  imageId\n  twitterScreenName\n  navItems {\n    title\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment SuspendedBannerLoader_user on User {\n  id\n  isSuspended\n  __typename\n}\n\nfragment useAnalytics_user on User {\n  id\n  imageId\n  name\n  username\n  __typename\n}\n\nfragment EntityDrivenSubscriptionLandingPageScreen_writer on User {\n  name\n  imageId\n  id\n  username\n  isPartnerProgramEnrolled\n  customStyleSheet {\n    ...CustomThemeProvider_customStyleSheet\n    ...CustomBackgroundWrapper_customStyleSheet\n    ...MetaHeader_customStyleSheet\n    __typename\n    id\n  }\n  ...MetaHeader_publisher\n  ...userUrl_user\n  __typename\n}\n\nfragment CustomThemeProvider_customStyleSheet on CustomStyleSheet {\n  id\n  ...customDefaultBackgroundTheme_customStyleSheet\n  ...customStyleSheetFontTheme_customStyleSheet\n  __typename\n}\n\nfragment customStyleSheetFontTheme_customStyleSheet on CustomStyleSheet {\n  id\n  global {\n    fonts {\n      font1 {\n        name\n        __typename\n      }\n      font2 {\n        name\n        __typename\n      }\n      font3 {\n        name\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment CustomBackgroundWrapper_customStyleSheet on CustomStyleSheet {\n  id\n  global {\n    colorPalette {\n      background {\n        ...getHexFromColorValue_colorValue\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeader_customStyleSheet on CustomStyleSheet {\n  id\n  header {\n    headerScale\n    horizontalAlignment\n    __typename\n  }\n  ...MetaHeaderBackground_customStyleSheet\n  ...MetaHeaderEngagement_customStyleSheet\n  ...MetaHeaderLogo_customStyleSheet\n  ...MetaHeaderNavVertical_customStyleSheet\n  ...MetaHeaderTagline_customStyleSheet\n  ...MetaHeaderThemeProvider_customStyleSheet\n  __typename\n}\n\nfragment MetaHeaderBackground_customStyleSheet on CustomStyleSheet {\n  id\n  header {\n    headerScale\n    backgroundImageDisplayMode\n    backgroundImageVerticalAlignment\n    backgroundColorDisplayMode\n    backgroundColor {\n      ...getHexFromColorValue_colorValue\n      ...getOpaqueHexFromColorValue_colorValue\n      __typename\n    }\n    secondaryBackgroundColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    postBackgroundColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    backgroundImage {\n      ...MetaHeaderBackground_imageMetadata\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeaderEngagement_customStyleSheet on CustomStyleSheet {\n  ...MetaHeaderNav_customStyleSheet\n  __typename\n  id\n}\n\nfragment MetaHeaderNav_customStyleSheet on CustomStyleSheet {\n  id\n  navigation {\n    navItems {\n      ...MetaHeaderNav_headerNavigationItem\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeaderNav_headerNavigationItem on HeaderNavigationItem {\n  name\n  tagSlugs\n  ...MetaHeaderNavLink_headerNavigationItem\n  __typename\n}\n\nfragment MetaHeaderNavLink_headerNavigationItem on HeaderNavigationItem {\n  name\n  ...getNavItemHref_headerNavigationItem\n  __typename\n}\n\nfragment getNavItemHref_headerNavigationItem on HeaderNavigationItem {\n  href\n  type\n  tags {\n    id\n    normalizedTagSlug\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeaderLogo_customStyleSheet on CustomStyleSheet {\n  id\n  header {\n    nameColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    nameTreatment\n    postNameTreatment\n    logoImage {\n      ...MetaHeaderLogo_imageMetadata\n      __typename\n    }\n    logoScale\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeaderLogo_imageMetadata on ImageMetadata {\n  id\n  originalWidth\n  originalHeight\n  ...PublisherLogo_image\n  __typename\n}\n\nfragment PublisherLogo_image on ImageMetadata {\n  id\n  originalHeight\n  originalWidth\n  __typename\n}\n\nfragment MetaHeaderNavVertical_customStyleSheet on CustomStyleSheet {\n  id\n  navigation {\n    navItems {\n      ...MetaHeaderNavLink_headerNavigationItem\n      __typename\n    }\n    __typename\n  }\n  ...MetaHeaderNav_customStyleSheet\n  __typename\n}\n\nfragment MetaHeaderTagline_customStyleSheet on CustomStyleSheet {\n  id\n  header {\n    taglineColor {\n      ...getHexFromColorValue_colorValue\n      __typename\n    }\n    taglineTreatment\n    __typename\n  }\n  __typename\n}\n\nfragment MetaHeaderThemeProvider_customStyleSheet on CustomStyleSheet {\n  id\n  ...useMetaHeaderTheme_customStyleSheet\n  __typename\n}\n\nfragment useMetaHeaderTheme_customStyleSheet on CustomStyleSheet {\n  ...customDefaultBackgroundTheme_customStyleSheet\n  global {\n    colorPalette {\n      primary {\n        colorPalette {\n          tintBackgroundSpectrum {\n            ...ThemeUtil_colorSpectrum\n            __typename\n          }\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  header {\n    backgroundColor {\n      colorPalette {\n        tintBackgroundSpectrum {\n          ...ThemeUtil_colorSpectrum\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    postBackgroundColor {\n      colorPalette {\n        tintBackgroundSpectrum {\n          ...ThemeUtil_colorSpectrum\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    backgroundImage {\n      id\n      __typename\n    }\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MetaHeader_publisher on Publisher {\n  __typename\n  name\n  ...MetaHeaderEngagement_publisher\n  ...MetaHeaderLogo_publisher\n  ...MetaHeaderNavVertical_publisher\n  ...MetaHeaderTagline_publisher\n  ...MetaHeaderThemeProvider_publisher\n  ...MetaHeaderActions_publisher\n  ...MetaHeaderTop_publisher\n  ...MetaHeaderNavLink_publisher\n  ... on Collection {\n    id\n    favicon {\n      id\n      __typename\n    }\n    tagline\n    ...CollectionNavigationContextProvider_collection\n    __typename\n  }\n  ... on User {\n    id\n    bio\n    ...UserProfileCatalogsLink_publisher\n    __typename\n  }\n}\n\nfragment MetaHeaderEngagement_publisher on Publisher {\n  __typename\n  ...MetaHeaderNav_publisher\n  ...PublisherAboutLink_publisher\n  ...PublisherFollowButton_publisher\n  ...PublisherFollowerCount_publisher\n  ... on Collection {\n    creator {\n      id\n      __typename\n    }\n    customStyleSheet {\n      id\n      ...CustomThemeProvider_customStyleSheet\n      __typename\n    }\n    __typename\n    id\n  }\n  ... on User {\n    ...UserProfileCatalogsLink_publisher\n    ...UserSubscribeButton_user\n    customStyleSheet {\n      id\n      ...CustomThemeProvider_customStyleSheet\n      __typename\n    }\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderNav_publisher on Publisher {\n  id\n  ...MetaHeaderNavLink_publisher\n  __typename\n}\n\nfragment MetaHeaderNavLink_publisher on Publisher {\n  id\n  ...getNavItemHref_publisher\n  __typename\n}\n\nfragment getNavItemHref_publisher on Publisher {\n  id\n  ...publisherUrl_publisher\n  __typename\n}\n\nfragment PublisherAboutLink_publisher on Publisher {\n  __typename\n  id\n  ... on Collection {\n    slug\n    __typename\n    id\n  }\n  ... on User {\n    ...userUrl_user\n    __typename\n    id\n  }\n}\n\nfragment PublisherFollowButton_publisher on Publisher {\n  __typename\n  ... on Collection {\n    ...CollectionFollowButton_collection\n    __typename\n    id\n  }\n  ... on User {\n    ...UserFollowButton_user\n    __typename\n    id\n  }\n}\n\nfragment UserProfileCatalogsLink_publisher on Publisher {\n  __typename\n  id\n  ... on User {\n    ...userUrl_user\n    homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {\n      posts {\n        id\n        __typename\n      }\n      __typename\n    }\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderLogo_publisher on Publisher {\n  __typename\n  id\n  name\n  ... on Collection {\n    logo {\n      ...MetaHeaderLogo_imageMetadata\n      ...PublisherLogo_image\n      __typename\n      id\n    }\n    __typename\n    id\n  }\n  ...auroraHooks_publisher\n}\n\nfragment MetaHeaderNavVertical_publisher on Publisher {\n  id\n  ...PublisherAboutLink_publisher\n  ...MetaHeaderNav_publisher\n  ...MetaHeaderNavLink_publisher\n  __typename\n}\n\nfragment MetaHeaderTagline_publisher on Publisher {\n  __typename\n  ... on Collection {\n    tagline\n    __typename\n    id\n  }\n  ... on User {\n    bio\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderThemeProvider_publisher on Publisher {\n  __typename\n  customStyleSheet {\n    ...MetaHeaderThemeProvider_customStyleSheet\n    __typename\n    id\n  }\n  ... on Collection {\n    colorPalette {\n      ...customDefaultBackgroundTheme_colorPalette\n      __typename\n    }\n    __typename\n    id\n  }\n}\n\nfragment MetaHeaderActions_publisher on Publisher {\n  __typename\n  ...MetaHeaderPubMenu_publisher\n  ...SearchWidget_publisher\n  ... on Collection {\n    id\n    creator {\n      id\n      __typename\n    }\n    customStyleSheet {\n      navigation {\n        navItems {\n          name\n          __typename\n        }\n        __typename\n      }\n      __typename\n      id\n    }\n    ...CollectionAvatar_collection\n    ...CollectionMetabarActionsPopover_collection\n    ...MetaHeaderActions_collection_common\n    __typename\n  }\n  ... on User {\n    id\n    ...UserAvatar_user\n    __typename\n  }\n}\n\nfragment SearchWidget_publisher on Publisher {\n  __typename\n  ... on Collection {\n    id\n    slug\n    name\n    domain\n    __typename\n  }\n  ... on User {\n    id\n    name\n    __typename\n  }\n  ...algoliaSearch_publisher\n}\n\nfragment algoliaSearch_publisher on Publisher {\n  __typename\n  id\n}\n\nfragment CollectionMetabarActionsPopover_collection on Collection {\n  id\n  slug\n  isAuroraEligible\n  isAuroraVisible\n  newsletterV3 {\n    id\n    slug\n    __typename\n  }\n  ...collectionUrl_collection\n  __typename\n}\n\nfragment MetaHeaderActions_collection_common on Collection {\n  creator {\n    id\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MetaHeaderTop_publisher on Publisher {\n  __typename\n  ... on Collection {\n    slug\n    ...CollectionMetabarActionsPopover_collection\n    ...CollectionAvatar_collection\n    ...MetaHeaderTop_collection\n    __typename\n    id\n  }\n  ... on User {\n    username\n    id\n    __typename\n  }\n}\n\nfragment MetaHeaderTop_collection on Collection {\n  id\n  creator {\n    id\n    __typename\n  }\n  __typename\n}\n\nfragment CollectionNavigationContextProvider_collection on Collection {\n  id\n  domain\n  slug\n  isAuroraVisible\n  __typename\n}\n\nfragment useShouldShowEntityDrivenSubscription_creator on User {\n  id\n  __typename\n}\n"
      }
]
  • id : アカウントに対応するユーザーIDで、上記のフォロワー取得エンドポイントで取得可能です

  • homepagePostsFrom : ページネーションパラメータ。レスポンス内の from を参照して、すべてのページデータを取得できます。

Postman :

Response:

response[0]["data"]["userResult"]["homepagePostsConnection"]["posts"] で記事リストの情報を取得できます。

Medium Private API — 記事内容の取得

次に最も重要な、記事の元の内容を取得します。

Graphql クエリ本文:

1
2
3
4
5
6
7
8
9
[
      {
        "operationName": "PostViewerEdgeContentQuery",
        "variables": {
          "postId": "c008a9e8ceca"
        },
        "query": "query PostViewerEdgeContentQuery($postId: ID!, $postMeteringOptions: PostMeteringOptions) {\n  post(id: $postId) {\n    ... on Post {\n      id\n      viewerEdge {\n        id\n        fullContent(postMeteringOptions: $postMeteringOptions) {\n          isLockedPreviewOnly\n          validatedShareKey\n          bodyModel {\n            ...PostBody_bodyModel\n            __typename\n          }\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment PostBody_bodyModel on RichText {\n  sections {\n    name\n    startIndex\n    textLayout\n    imageLayout\n    backgroundImage {\n      id\n      originalHeight\n      originalWidth\n      __typename\n    }\n    videoLayout\n    backgroundVideo {\n      videoId\n      originalHeight\n      originalWidth\n      previewImageId\n      __typename\n    }\n    __typename\n  }\n  paragraphs {\n    id\n    ...PostBodySection_paragraph\n    __typename\n  }\n  ...normalizedBodyModel_richText\n  __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n  name\n  ...PostBodyParagraph_paragraph\n  __typename\n  id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n  name\n  type\n  ...ImageParagraph_paragraph\n  ...TextParagraph_paragraph\n  ...IframeParagraph_paragraph\n  ...MixtapeParagraph_paragraph\n  ...CodeBlockParagraph_paragraph\n  __typename\n  id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n  href\n  layout\n  metadata {\n    id\n    originalHeight\n    originalWidth\n    focusPercentX\n    focusPercentY\n    alt\n    __typename\n  }\n  ...Markups_paragraph\n  ...ParagraphRefsMapContext_paragraph\n  ...PostAnnotationsMarker_paragraph\n  __typename\n  id\n}\n\nfragment Markups_paragraph on Paragraph {\n  name\n  text\n  hasDropCap\n  dropCapImage {\n    ...MarkupNode_data_dropCapImage\n    __typename\n    id\n  }\n  markups {\n    type\n    start\n    end\n    href\n    anchorType\n    userId\n    linkMetadata {\n      httpStatus\n      __typename\n    }\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n  ...DropCap_image\n  __typename\n  id\n}\n\nfragment DropCap_image on ImageMetadata {\n  id\n  originalHeight\n  originalWidth\n  __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n  id\n  name\n  text\n  __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n  ...PostViewNoteCard_paragraph\n  __typename\n  id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n  type\n  hasDropCap\n  codeBlockMetadata {\n    mode\n    lang\n    __typename\n  }\n  ...Markups_paragraph\n  ...ParagraphRefsMapContext_paragraph\n  __typename\n  id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n  iframe {\n    mediaResource {\n      id\n      iframeSrc\n      iframeHeight\n      iframeWidth\n      title\n      __typename\n    }\n    __typename\n  }\n  layout\n  ...getEmbedlyCardUrlParams_paragraph\n  ...Markups_paragraph\n  __typename\n  id\n}\n\nfragment getEmbedlyCardUrlParams_paragraph on Paragraph {\n  type\n  iframe {\n    mediaResource {\n      iframeSrc\n      __typename\n    }\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n  type\n  mixtapeMetadata {\n    href\n    mediaResource {\n      mediumCatalog {\n        id\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n  ...GenericMixtapeParagraph_paragraph\n  __typename\n  id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n  text\n  mixtapeMetadata {\n    href\n    thumbnailImageId\n    __typename\n  }\n  markups {\n    start\n    end\n    type\n    href\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment CodeBlockParagraph_paragraph on Paragraph {\n  codeBlockMetadata {\n    lang\n    mode\n    __typename\n  }\n  __typename\n  id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n  paragraphs {\n    markups {\n      type\n      __typename\n    }\n    codeBlockMetadata {\n      lang\n      mode\n      __typename\n    }\n    ...getParagraphHighlights_paragraph\n    ...getParagraphPrivateNotes_paragraph\n    __typename\n  }\n  sections {\n    startIndex\n    ...getSectionEndIndex_section\n    __typename\n  }\n  ...getParagraphStyles_richText\n  ...getParagraphSpaces_richText\n  __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n  name\n  __typename\n  id\n}\n\nfragment getSectionEndIndex_section on Section {\n  startIndex\n  __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n  paragraphs {\n    text\n    type\n    __typename\n  }\n  sections {\n    ...getSectionEndIndex_section\n    __typename\n  }\n  __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n  paragraphs {\n    layout\n    metadata {\n      originalHeight\n      originalWidth\n      id\n      __typename\n    }\n    type\n    ...paragraphExtendsImageGrid_paragraph\n    __typename\n  }\n  ...getSeriesParagraphTopSpacings_richText\n  ...getPostParagraphTopSpacings_richText\n  __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n  layout\n  type\n  __typename\n  id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n  paragraphs {\n    id\n    __typename\n  }\n  sections {\n    startIndex\n    __typename\n  }\n  __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n  paragraphs {\n    layout\n    text\n    codeBlockMetadata {\n      lang\n      mode\n      __typename\n    }\n    __typename\n  }\n  sections {\n    startIndex\n    __typename\n  }\n  __typename\n}\n"
      }
]
  • posdId : 記事のIDで、上記で取得した記事リストのAPIから取得できます。

Postman

Response:

文章のソースは上の図の通りです:

  • response[0]["data"]["post"]["viewerEdge"]["fullContent"]["bodyModel"]["paragraphs"] :記事全体の段落で、組み合わせると全文になります

  • ["markups"] : 段落の文字スタイル、例えば太字やハイパーリンクなど…

この JSON 形式の記述を Markdown に変換する方法は、私が以前開発したオープンソースツール — ZMediumToMarkdown — を直接参照または使用してください

  • 自分の有料記事をスクレイピングするには、sid と uid のログイントークン情報を付ける必要があります(以下を参照)。

Medium x Cloudflare 攻防

本記事の第二のテーマは Cloudflare です。2025年下半期頃から、Medium の Cloudflare 防御設定はほぼ最高レベルになり、クラウドサービスからのすべてのリクエストがブロックされるようになりました(私が使っている Google Apps Script や GitHub Actions もすべてブロックされました)、そのためデータの取得に失敗しました。

ブロックメッセージ:403

1
<!DOCTYPE html><html lang="en-US"><head><title>少々お待ちください...</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">....

これには非常に悩まされました。なぜなら、私のミラーサイト(https://zhgchg.li)は数年間安定して稼働しており、Mediumに新しい記事が公開されると、そこですぐにスクリプトが自動で実行されて同期されるからです(Private API Graphqlを通じて)。もしクラウドサービスがブロックされるなら、手動でローカルのパソコンでスクリプトを実行するしかありません

Header Cookies に sid, uid を追加する ❌

最初は Header Cookies に sid や uid を追加し、Medium のログイン状態を示すことで問題なく通過できましたが、数ヶ月後にはこの方法も通用しなくなりました。

  • uid : あなたのユーザーID

  • sid : あなたのアクセス トークン (必ず秘密にしてください)

Cloudflare Worker リクエストブリッジ ✅

いろいろ試して方法が見つからなかったが、ふと思いついて Cloudflare の魔法で魔法を打ち破り、Cloudflare を使ったら、なんと成功した!Cloudflare のボット防護にブロックされなかった。

Demo Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
export default {

  async fetch(request, env, ctx) {

    const url = new URL(request.url);
    const path = url.pathname;

    if (path == "/graphql") {
      let body;
      try {
        body = await request.json();
      } catch {
        return new Response("Invalid JSON body", { status: 400 }); // 無効なJSONボディ
      }

      let apiURL = "https://medium.com/_/graphql";

      const apiResponse = await fetch(apiURL, {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Content-Type": "application/json",
          "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0",
          "Cookie": request.headers.get("cookie") // リクエストのCookieを渡す
        },
        body: JSON.stringify(body),
      });

      const json = await apiResponse.json();
      return new Response(JSON.stringify(json), {
        status: apiResponse.status,
        headers: {
          "Content-Type": "application/json; charset=utf-8",
        },
      });
    }

    return new Response("Not Found", { status: 404 }); // 見つかりません
  },
};

Request:

https://medium.com/_/graphql をあなたがデプロイした Cloudflare Worker の URL に変更するだけです。

まとめ

以上が私と Medium API との長年の戦いの軌跡です。現在、すべての記事(Markdown ファイル)と画像は https://zhgchg.li にバックアップされています。すべての添付画像ファイルも含まれており、GitHub リポジトリにも一部、パソコンのハードディスクにも一部保存しているため、とても安全です。

やはり Medium が長く続くことを願っています!本記事の内容はあくまで実験参考用であり、著者は一切の使用責任を負いません。

ご質問やご意見がありましたら、こちらからご連絡ください

PostZMediumToMarkdown によって Medium から変換されました。


🍺 Buy me a beer on PayPal

👉👉👉 Follow Me On Medium! (1,053+ Followers) 👈👈👈

本記事は Medium にて初公開されました(こちらからオリジナル版を確認)。ZMediumToMarkdown による自動変換・同期技術を使用しています。

Improve this page on Github.

本記事は著者により CC BY 4.0 に基づき公開されています。