Blog

由于各种各样的原因,大家会有想要爬取 twitter 的用户的信息的想法,但申请官方 api 的那几篇小作文不是谁都能写得出的(本人就写不出),所以需要直接开始爬取内容,而爬 twitter 有时就是一个大坑。这里就讲讲本人处理 Twitter Monitor 期间遇过的坑。

Javascript 文件

Twitter 会在首页引入 5 个 JavaScript 文件,由于 webpack 打包的原因,它们的名字不一定与本文的相同,但可以提供参考,也为下文的说明提供参考

name link
polyfills https://abs.twimg.com/responsive-web/web/polyfills.321d1c14.js
vendors https://abs.twimg.com/responsive-web/web/vendors~main.483e4ab4.js
i18n-rweb/zh https://abs.twimg.com/responsive-web/web/i18n-rweb/zh.322c7be4.js
i18n-horizon/zh https://abs.twimg.com/responsive-web/web/i18n-horizon/zh.15b97c64.js
main https://abs.twimg.com/responsive-web/web/main.f18fcbb4.js

第 3、4 位的是语言文件

还有一些其他可能有用的文件

name link
bundle.UserProfile https://abs.twimg.com/responsive-web/web/bundle.UserProfile.e36cd9b4.js

鉴权

爬内容的 api 来来去去就是那几个,已经好几年没有更新过了,但是大多数人会遇到一个问题,那就是鉴权。举个例子

1
curl 'https://api.twitter.com/2/timeline/profile/783214.json?tweet_mode=extended&count=20' -H 'x-guest-token: 1232704521454999999' -H 'authorization: Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA' --compressed

上面是爬取一个用户推文时间线 (timeline) 的最低限度的请求,其中链接不需要多说都能理解,这里出现了 x-guest-tokenauthorization。虽然这两个值看起来让人毫无头绪,其实都是可以自行取得的。

x-guest-token

x-guest-token 决定你的 rate-limit,当此值为空或者不正确时 twitter 会返回如

1
2
3
4
5
6
7
8
{
"errors": [
{
"message": "Rate limit exceeded",
"code": 88
}
]
}

的错误,此值会在用户第一次访问 twitter 的时候在网页上赋予,所以直接构造一个请求

1
curl 'https://twitter.com' --compressed

此时得到的网页会有以下几行赋予 x-guest-token

1
2
3
<script nonce="MDRjZmJlNWItYWNmOC00MTdiLWIxYjUtYTFhZTUyYTc2ODg4">
document.cookie = decodeURIComponent("gt=1232704521454999999; Max-Age=10800; Domain=.twitter.com; Path=/; Secure");
</script>

因此可以构建正则表达式 /gt=([0-9]+)/ 取得此值。

authorization

1
AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA

这没什么好说的,此值固定,出现在 https://abs.twimg.com/responsive-web/web/main.f18fcbb4.js ,即使未来 twitter 更新了前端也会出现在类似文件名称的 js 文件中。

1
<link rel="preload" as="script" crossorigin="anonymous" href="https://abs.twimg.com/responsive-web/web/main.f18fcbb4.js" nonce="OGE1NzNmZTQtNzQxMS00Y2FiLTllYTItMDFlNGZlNTM1ZDFh" />

差不多是这样的。


rate-limit

rate-limit 限制了用户在一定时间内请求的次数,并且会在相对时间后重置,在 twitter,这个时间是 15 分钟。

根据上文我们可以知道 twitter 是通过 x-guest-token 判断 rate-limit 的,在用户的每次请求所返回的 header 上都会有以下内容

1
2
3
x-rate-limit-limit: 180
x-rate-limit-remaining: 179
x-rate-limit-reset: 1567401449

很好理解对吧,https://api.twitter.com/1.1/application/rate_limit_status.json 这个文件详细说明了各个 api 的 rate-limit。


用户信息

爬取用户信息很轻松,这里有几个接口

  • 第一次加载用户信息时使用

    1
    curl 'https://api.twitter.com/graphql/P8ph10GzBbdMqWZxulqCfA/UserByScreenName?variables=%7B%22screen_name%22%3A%22twitter%22%2C%22withHighlightedLabel%22%3Afalse%7D' -H 'x-guest-token: 1232704521454999999' -H 'authorization:  Bearer  AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA' --compressed

    其中 P8ph10GzBbdMqWZxulqCfA 出现在前文提到的那个 main 开头名称的 js 文件内,此值是否固定未知,故不推荐

    1
    2
    3
    4
    5
    {
    queryId: "P8ph10GzBbdMqWZxulqCfA",
    operationName: "UserByScreenName",
    operationType: "query"
    }

    此处的变量是 urlencode 化的 json

    1
    2
    3
    4
    {
    "screen_name": "twitter",
    "withHighlightedLabel": false
    }
  • 刷新时间线时使用

    1
    curl 'https://api.twitter.com/1.1/users/show.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&screen_name=twitter' -H 'x-guest-token: 1232704521454999999' -H 'authorization:  Bearer  AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA' --compressed
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    include_profile_interstitial_type: 1
    include_blocking: 1
    include_blocked_by: 1
    include_followed_by: 1
    include_want_retweets: 1
    include_mute_edge: 1
    include_can_dm: 1
    include_can_media_tag: 1
    skip_status: 1
    user_id: 783214
    screen_name: twitter

    其中,user_idscreen_name 需要二选一即可。

    返回的数据大同小异,在以后都以第二种形式返回的为参考

    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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    {
    "id": 783214,
    "id_str": "783214",
    "name": "Twitter",
    "screen_name": "Twitter",
    "location": "Everywhere",
    "profile_location": null,
    "description": "What\u2019s happening?!",
    "url": "https:\/\/t.co\/TAXQpsHa5X",
    "entities": {
    "url": {
    "urls": [
    {
    "url": "https:\/\/t.co\/TAXQpsHa5X",
    "expanded_url": "https:\/\/about.twitter.com\/",
    "display_url": "about.twitter.com",
    "indices": [0,23]
    }
    ]
    },
    "description": {
    "urls":[]
    }
    },
    "protected": false,
    "followers_count": 57276889,
    "fast_followers_count": 7785,
    "normal_followers_count": 57269104,
    "friends_count": 28,
    "listed_count": 90682,
    "created_at": "Tue Feb 20 14:35:54 +0000 2007",
    "favourites_count": 6404,
    "utc_offset": null,
    "time_zone": null,
    "geo_enabled": true,
    "verified": true,
    "statuses_count": 13096,
    "media_count": 1993,
    "lang": null,
    "contributors_enabled": false,
    "is_translator": false,
    "is_translation_enabled": false,
    "profile_background_color": "ACDED6",
    "profile_background_image_url": "http:\/\/abs.twimg.com\/images\/themes\/theme18\/bg.gif",
    "profile_background_image_url_https": "https:\/\/abs.twimg.com\/images\/themes\/theme18\/bg.gif",
    "profile_background_tile": true,
    "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/1111729635610382336\/_65QFl7B_normal.png",
    "profile_image_url_https": "https:\/\/pbs.twimg.com\/profile_images\/1111729635610382336\/_65QFl7B_normal.png",
    "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/783214\/1556918042",
    "profile_link_color": "1B95E0",
    "profile_sidebar_border_color": "FFFFFF",
    "profile_sidebar_fill_color": "F6F6F6",
    "profile_text_color": "333333",
    "profile_use_background_image": true,
    "has_extended_profile": true,
    "default_profile": false,
    "default_profile_image": false,
    "pinned_tweet_ids": [],
    "pinned_tweet_ids_str": [],
    "has_custom_timelines": true,
    "can_dm": false,
    "can_media_tag": true,
    "following": false,
    "follow_request_sent": false,
    "notifications": false,
    "muting": false,
    "blocking": false,
    "blocked_by": false,
    "want_retweets": false,
    "advertiser_account_type": "promotable_user",
    "advertiser_account_service_levels": [
    "media_studio",
    "dso",
    "analytics",
    "dso",
    "dso"
    ],
    "profile_interstitial_type": "",
    "business_profile_state": "none",
    "translator_type": "regular",
    "followed_by": false,
    "require_some_consent": false
    }

    有几点比较有意思

    • entities 中不含 hashtag 的信息,所以我也不知道它使用什么骚套路实现的

    • 用户的的背景图的格式是 https://pbs.twimg.com/profile_banners/:userid/:bannerid,所以保存的时候其实可以把它拆开到使用的时候再组装……

    • profile_interstitial_type 是一个很有意思的字段,它留空代表正常用户,其他都会得到 twitter 的警告,以下有对应警告,仅供参考

    • 注:此表格内容来自 bundle.UserProfile*。

      value message
      fake_account ** 警告:此账号暂时受限。**
      你看到这则警告,因为该账号有异常活动。是否仍要查看?
      sensitive_media * 警告:此个人资料可能包含敏感内容。**
      你看到这则警告,因为其中涉嫌使用不良图片或语言。是否仍要查看?

      注:这种警告经常在各种 NSFW 号上出现,为了找例子本人心灵受到了莫大的震撼 *
      1
      2
      3
      4
      5
      6
      {
      FakeAccount: "fake_account",
      OffensiveProfileContent: "offensive_profile_content",//What this is?
      SensitiveMedia: "sensitive_media",
      Timeout: "timeout"
      }
    • 用户有几种状态

      • 锁推:好吧这是我的说法,官方的说法叫做 “保护”,被保护的帐号的帐号信息的 protect 字段为 true,访问被保护的用户的页面会显示

        1
        2
         这些推文受到保护 
        只有经过批准的关注者才可查看 @baristabar 的推文。若要申请访问,点击关注。
      • 删号或账号不存在

        • 帐号不存在:请求用户信息会返回

          1
          2
          3
          4
          5
          6
          7
          8
          {
          "errors": [
          {
          "code": 50,
          "message": "User not found."
          }
          ]
          }
        • 自删:即自己删号,这只是一种相对的说法,因为直接请求此用户的信息所返回的内容同上条,但若要修改用户名 (screen_name) 到该自删帐号的用户最后的用户名会被提示 ** 该用户名已被占用。请另选一个。**

      • 被冻结:顾名思义,就是被封了

        1
        2
        3
        4
        5
        6
        7
        8
        {
        "errors": [
        {
        "code": 63,
        "message": "User has been suspended."
        }
        ]
        }
    • 因为 Twitter 有一个 non_username_paths,顾名思义,就是不可做为用户名的目录,即便如此,那个列表并不是一定可靠的,因为即使需要用户名在列表上也是可以买的…… 下面列一个这个列表的现状,全表请参阅参考网页。(2020-3-8 0:44 UTC+8)

      用户名 返回代码 返回信息 / 全名 备注
      accounts 63 User has been suspended.
      all 0 ALL - Accor Live Limitless 认证用户
      anywhere 0 Anywhere 一般用户
      blog 0 steve 跳转 https://blog.twitter.com/
      business 0 Bloomberg 认证用户
      faq 0 Th\ufffderry Twitter FAQ 页面
      followers 63 User has been suspended.
      friends 0 Friends 一般用户
      home 0 [email protected] 登录用户显示用户时间线,未登录用户跳转登录界面
      jobs 63 User has been suspended.
      list 0 ya 认证用户
      logout 0 Waterfall 登录用户显示登出确认,未登录用户跳转登录界面
      me 0 Maine.com 一般用户
      retweets 0 All the crap I get on Whatsapp 一般用户
      sent 0 Sent 一般用户
      settings 0 Settings 用户设置
      signup 0 Feanamacatangay 跳转到 https://twitter.com/i/flow/signup
      signin 0 Signin 一般用户
      terms 0 Terms 页面不存在
      tos 63 User has been suspended.
      twttr 0 - 已锁
      welcome 63 User has been suspended.

推文内容

// TODO

参考网页


 评论

 无法加载Disqus评论系统,请确保您的网络能够正常访问。

Copyright © 2018 - 2020 バンカ の メモ帳

本站使用 Material X 作为主题 。