天天看點

有道翻譯的實踐

在網上看了好幾個用python調用網易有道翻譯的教學貼,可惜對我等小白來說,放眼望去都是不懂的坑,感覺python還是需要實踐來學習,就先把找到的填平這些坑的辦法記錄一下,可能涉及轉載。

1. 各參數的獲得

各參數的獲得,是這個項目的關鍵點。

  • 使用chrome-檢查-network,然後在網頁中輸入需要翻譯的内容,點選翻譯,在檢查中找到新生成的tranlate開頭的name,進入
有道翻譯的實踐
  • 進入translate以後,點選headers,會發現真實的request url以及form data,這時會發現form data中的salt、sign、lts、bv這四個參數都是長串無規則字元串。多生成幾次translate以後,發現bv字元串是固定的,lts13位,salt比lts多一位無規則數字,sign一直變化。推測應該是在js中用了加密
  • 點選network-Initiator,發現全部指向同一個js
    有道翻譯的實踐
  • 點選進入 fanyi.min.js:1,點選pretty print(頁面下方的{} 也行),直接進入fanyi.min.js:1所在行數,同時也把js代碼格式化了。
  • 通過breakpoint的形式尋找,連結就是真實的request url,在XHR/fetch Breakpoint中填入。再次在網頁上點選翻譯,檢查中頁面會自動停下。
    有道翻譯的實踐
  • 點選上圖中右側的call stack,尋找t.translate,會看到salt,ts,bv,sign生成之後的資料,點選generatesSaltSign(n)回溯過去。最終找到。
    有道翻譯的實踐

2. 時間戳

python中時間戳是10位,但是在java中是13位,需要用到時間戳的時候代碼如下:

import time

t = time.time() # 生成一個以秒為機關的10位時間戳,float類型的浮點數
t =int(t)  # 對t取整,不要小數
print(t)

# jave中需要13位時間戳,可以把秒轉換為毫秒
millis = int(round(time.time()* 1000))  # round()是四省五入,其實不要round也沒事
print(millis)

# 13位時間戳轉換成現在時間
now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(millis/1000))
print(now)
           

3. salt的構成

salt的值是i,而i = r + parseInt(10* Math.random(), 10)。

Math.random()是js中的函數,随機生成[0,1)随機數,而10*Math.random() 就是生成[0,10)中的一個随機數。

parseInt(string, radix), 其中string就是字元串,radix表示進制,可選從2到32,譬如10就是十進制,16就是十六進制,2就是二進制。

4. sign的構成

sign的值是 n.md5(“fanyideskweb” + e + i +‘Y2FYu%TNSbMCxc3t2u^XT’),(最後一個值可能每人都不一樣),i是salt,e是輸入的值。關于e到底是怎麼來的,我覺得應該從function(e)引出的,說明e是function的輸入變量。

5. bv的構成

bv的值是n.md5(navigator.appVersion),是固定的,實際就是浏覽器版本。appVersion可以通過滑鼠懸停的方式獲得

有道翻譯的實踐

6. 最終的代碼

最終的代碼基本複制了大神的,确實寫的簡潔有力,記錄一下。

# 向有道詞典發送資料并獲得翻譯結果
import requests
import json
import time
import hashlib
import random

class YoudaoCrawl(object):

    def __init__(self, word):
        self.url = " http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
            "Cookie": '[email protected]; JSESSIONID=aaa6iBvh3WJaGgn8RPGBx; OUTFOX_SEARCH_USER_ID_NCOO=1574258758.1928287; ___rl__test__cookies=1610087834528',
            "Referer": "http://fanyi.youdao.com/"
        }
        self.word = word
        self.post_body = {}

    def generator_post_body(self):
        ts = str(int(time.time()*1000))    # ts是string型資料

        num = int(random.random() * 10)
        salt = ts + str(num)    # salt是string型資料


        str_sign = 'fanyideskweb' + self.word + salt + 'Y2FYu%TNSbMCxc3t2u^XT'
        md5 = hashlib.md5()  # md5加密
        md5.update(str_sign.encode('utf-8'))
        sign = md5.hexdigest()  # sign是string型資料


        appVersion = '5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome' \
                     '/92.0.4515.159 Safari/537.36'
        m_bv = hashlib.md5()
        m_bv.update(appVersion.encode('utf-8'))
        bv = m_bv.hexdigest()  # bv是string型資料


        self.post_body = {
            "i": self.word,
            "from": "AUTO",
            "to": "AUTO",
            "smartresult": "dict",
            "client": "fanyideskweb",
            "salt": salt,
            "sign": sign,
            "lts": ts,
            "bv": bv,
            "doctype": "json",
            "version": "2.1",
            "keyfrom": "fanyi.web",
            "action": "FY_BY_REALTIME",
        }


    def send_request(self):
        response =requests.post(self.url, headers=self.headers, data=self.post_body)
        return response.json()

    def parser_response(self, ret_dict):
        """從字典中提取翻譯結果"""
        ret = ret_dict['translateResult'][0][0]['tgt']
        print("翻譯的文本: {} , 翻譯的結果 :{}".format(self.word, ret))

    def run(self):
        """運作函數"""
        self.generator_post_body()
        ret_dict = self.send_request()    # ???函數也能用self/???
        self.parser_response(ret_dict)

if __name__ == "__main__" :
    data = input("請輸入需要翻譯的内容: ")
    spider = YoudaoCrawl(data)
    spider.run()
           

時間戳參考連結

打點參考

代碼參考