[譯] 我是如何使用 Python 在 Medium 上找到並關注有趣的人

Park-ma發表於2018-08-14

封面圖來源:Old Medium logo

Medium 上有大量的內容、使用者和不計其數的帖子。當你試圖尋找有趣的使用者來關注時,你會發現自己不知所措。

我對於有趣的使用者的定義是來自你的社交網路,保持活躍狀態並經常在 Medium 社群發表高質量評論的使用者。

我檢視了我關注的使用者的最新的帖子來看看是誰在回覆他們。我認為如果他們回覆了我關注的使用者,這就說明他們可能和我志趣相投。

這個過程很繁瑣,這就讓我想起了我上次實習期間學到的最有價值的一課:

任何繁瑣的任務都能夠並且應該是自動化完成的。

我想要我的自動化程式能夠做下面的事情:

  1. 從我的關注中獲取所有的使用者
  2. 從每一個使用者中獲取最新的帖子
  3. 獲取每一個帖子的所有評論
  4. 篩選出30天以前的回覆
  5. 篩選出少於最小推薦數的回覆
  6. 獲取每個回覆的作者的使用者名稱

讓我們開始吧

我首先看了看 Medium's API,卻發現它很有限。它給我提供的功能太少了。通過它,我只能獲取關於我自己的賬號資訊,而不能獲取其他使用者的資訊。

最重要的是,Medium's API 的最後一次更新是一年多前,最近也沒有要開發的跡象。

我意識到我只能依靠 HTTP 請求來獲取我的資料,所以我開始使用我的 Chrome 開發者工具

第一個目標是獲取我的關注列表。

我開啟我的開發者工具並進入 Network 選項卡。我過濾了除了 XHR 之外的所有內容以檢視 Medium 是從什麼地方來獲取我的關注的。我重新整理了我的個人資料頁面,但是什麼有趣的事情都沒發生。

如果我點選我的個人資料上的關注按鈕怎麼樣?成功啦!

[譯] 我是如何使用 Python 在 Medium 上找到並關注有趣的人

我找到使用者關注列表的連結。

在這個連結中,我發現了一個非常大的 JSON 響應。它是一個格式很好的 JSON,除了在響應開頭的一串字元:])}while(1);</x>

我寫了一個函式整理了格式並把 JSON 轉換成一個 Python 字典。

import json

def clean_json_response(response):
    return json.loads(response.text.split('])}while(1);</x>')[1])
複製程式碼

我已經找到了一個入口點,讓我們開始編寫程式碼吧。

從我的關注列表中獲取所有使用者

為了查詢端點,我需要我的使用者 ID(儘管我早就知道啦,這樣做是出於教育目的)。

我在尋找獲取使用者 ID 的方法時發現可以通過新增 ?format=json 給 Medium 的 URL 地址來獲取這個網頁的 JSON 響應。我在我的個人主頁上試了試。

看看,這就是我的使用者 ID。

])}while(1);</x>{"success":true,"payload":{"user":{"userId":"d540942266d0","name":"Radu Raicea","username":"Radu_Raicea",
...
複製程式碼

我寫了一函式從給出的使用者名稱中提取使用者 ID 。同樣,我使用了 clean_json_response 函式來去除響應開頭的不想要的字串。

我還定義了一個叫 MEDIUM 的常量,它用來儲存所有 Medium 的 URL 地址都包含的字串。

import requests

MEDIUM = 'https://medium.com'

def get_user_id(username):

    print('Retrieving user ID...')

    url = MEDIUM + '/@' + username + '?format=json'
    response = requests.get(url)
    response_dict = clean_json_response(response)
    return response_dict['payload']['user']['userId']
複製程式碼

通過使用者 ID ,我查詢了 /_/api/users/<user_id>/following 端點,從我的關注列表裡獲取了使用者名稱列表。

當我在開發者工具中做這時,我注意到 JSON 響應只有八個使用者名稱。很奇怪!

當我點選 “Show more people”,我找到了缺少的使用者名稱。原來 Medium 使用分頁的方式來展示關注列表。

[譯] 我是如何使用 Python 在 Medium 上找到並關注有趣的人

Medium 使用分頁的方式來展示關注列表。

分頁通過指定 limit(每頁元素)和 to(下一頁的第一個元素)來工作,我必須找到一種方式來獲取下一頁的 ID。

在從 /_/api/users/<user_id>/following 獲取的 JSON 響應的尾部,我看到了一個有趣的 JSON 鍵值對。

...
"paging":{"path":"/_/api/users/d540942266d0/followers","next":{"limit":8,"to":"49260b62a26c"}}},"v":3,"b":"31039-15ed0e5"}
複製程式碼

到了這一步,很容易就能寫出一個迴圈從我的關注列表裡面獲取所有的使用者名稱。

def get_list_of_followings(user_id):

    print('Retrieving users from Followings...')
    
    next_id = False
    followings = []
    while True:

        if next_id:
            # 如果這不是關注列表的第一頁
            url = MEDIUM + '/_/api/users/' + user_id
                  + '/following?limit=8&to=' + next_id
        else:
            # 如果這是關注列表的第一頁
            url = MEDIUM + '/_/api/users/' + user_id + '/following'

        response = requests.get(url)
        response_dict = clean_json_response(response)
        payload = response_dict['payload']

        for user in payload['value']:
            followings.append(user['username'])

        try:
            # 如果找不到 "to" 鍵,我們就到達了列表末尾,
            # 並且異常將會丟擲。
            next_id = payload['paging']['next']['to']
        except:
            break

    return followings
複製程式碼

獲取每個使用者最新的帖子

我得到了我關注的使用者列表之後,我就想獲取他們最新的帖子。我可以通過傳送這個請求 [https://medium.com/@<username>/latest?format=json](https://medium.com/@username/latest?format=json) 來實現這個功能。

於是我寫了一個函式,這個函式的引數是使用者名稱列表,然後返回一個包含輸入進來的所有使用者最新發表的帖子 ID 的 Python 列表。

def get_list_of_latest_posts_ids(usernames):

    print('Retrieving the latest posts...')

    post_ids = []
    for username in usernames:
        url = MEDIUM + '/@' + username + '/latest?format=json'
        response = requests.get(url)
        response_dict = clean_json_response(response)

        try:
            posts = response_dict['payload']['references']['Post']
        except:
            posts = []

        if posts:
            for key in posts.keys():
                post_ids.append(posts[key]['id'])

    return post_ids
複製程式碼

獲取每個帖子的所有評論

有了帖子的列表,我通過 https://medium.com/_/api/posts/<post_id>/responses 提取了所有的評論。

這個函式引數是帖子 ID Python 列表然後返回評論的Python列表。

def get_post_responses(posts):

    print('Retrieving the post responses...')

    responses = []

    for post in posts:
        url = MEDIUM + '/_/api/posts/' + post + '/responses'
        response = requests.get(url)
        response_dict = clean_json_response(response)
        responses += response_dict['payload']['value']

    return responses
複製程式碼

篩選這些評論

一開始,我希望評論達到點讚的最小值。但是我意識到這可能並不能很好的表達出社群對於評論的讚賞程度,因為一個使用者可以對同一條評論進行多次點贊。

相反,我使用推薦數來進行篩選。推薦數和點贊數差不多,但它不能多次推薦。

我希望這個最小值是可以動態調整的。所以我傳遞了名為 recommend_min 的變數。

下面的函式的引數是每一條評論和 recommend_min 變數。它用來檢查評論的推薦數是否到達最小值。

def check_if_high_recommends(response, recommend_min):
    if response['virtuals']['recommends'] >= recommend_min:
        return True
複製程式碼

我還希望得到最近的評論。因此我通過這個函式過濾掉超過 30 天的評論。

from datetime import datetime, timedelta

def check_if_recent(response):
    limit_date = datetime.now() - timedelta(days=30)
    creation_epoch_time = response['createdAt'] / 1000
    creation_date = datetime.fromtimestamp(creation_epoch_time)

    if creation_date >= limit_date:
        return True
複製程式碼

獲取評論作者的使用者名稱

在完成評論的篩選工作之後,我使用下面的函式來抓取所有作者的使用者 ID。

def get_user_ids_from_responses(responses, recommend_min):

    print('Retrieving user IDs from the responses...')

    user_ids = []

    for response in responses:
        recent = check_if_recent(response)
        high = check_if_high_recommends(response, recommend_min)

        if recent and high:
            user_ids.append(response['creatorId'])

    return user_ids
複製程式碼

當你試圖訪問某個使用者的個人資料時,你會發現使用者 ID 是沒用的。這時我寫了一個函式通過查詢 /_/api/users/<user_id> 端點來獲取使用者名稱。

def get_usernames(user_ids):

    print('Retrieving usernames of interesting users...')

    usernames = []

    for user_id in user_ids:
        url = MEDIUM + '/_/api/users/' + user_id
        response = requests.get(url)
        response_dict = clean_json_response(response)
        payload = response_dict['payload']

        usernames.append(payload['value']['username'])

    return usernames
複製程式碼

把所以函式組合起來

在完成所有函式之後,我建立了一個管道來獲取我的推薦使用者列表。

def get_interesting_users(username, recommend_min):

    print('Looking for interesting users for %s...' % username)

    user_id = get_user_id(username)

    usernames = get_list_of_followings(user_id)

    posts = get_list_of_latest_posts_ids(usernames)

    responses = get_post_responses(posts)

    users = get_user_ids_from_responses(responses, recommend_min)

    return get_usernames(users)
複製程式碼

這個指令碼程式終於完成啦!為了測試這個程式,你必須呼叫這個管道。

interesting_users = get_interesting_users('Radu_Raicea', 10)
print(interesting_users)
複製程式碼

[譯] 我是如何使用 Python 在 Medium 上找到並關注有趣的人

圖片來源: Know Your Meme

最後,我新增了一個選項,可以把結果和時間戳儲存在一個 CSV 檔案裡面。

import csv

def list_to_csv(interesting_users_list):
    with open('recommended_users.csv', 'a') as file:
        writer = csv.writer(file)

        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        interesting_users_list.insert(0, now)
        
        writer.writerow(interesting_users_list)
        
interesting_users = get_interesting_users('Radu_Raicea', 10)
list_to_csv(interesting_users)

複製程式碼

關於這個專案的原始檔可以在這裡找到

如果你還不會 Python,閱讀 TK 的 Python 教程:Learning Python: From Zero to Hero

如果你對其他讓使用者感興趣的標準有建議,請在下面留言!

總結···

  • 我編寫一個適用於 Medium 的 Python 指令碼
  • 這個指令碼返回一個使用者列表,裡面的使用者都是活躍的且在你的關注的使用者的最新帖子下面發表過有趣的評論
  • 你可以從列表裡取出使用者,用他的使用者名稱而不是你的來執行這個指令碼。

點選我的關於開源許可的初級教程以及如何把它們新增到你的專案中!

更多更新,請關注我的 Twitter

掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。

相關文章