最近要整點自動翻譯的東東,發現最親民的Google的翻譯API關閉了。那么擺在碼農的面前大概也只有2條路,
一是,直接使用web版本的的google翻譯,然后分析URL,和結果得到翻譯結果。然后直接調用,網上好像已經有兄弟們這樣干了。
二是,投奔微軟的陣營,使用Bing的API,
由於我要翻譯的東東好像在bing這兒的翻譯效果還可以,另外也訓練一下自己的Python能力,做點開源的事情。打算寫個Python使用Bing的API代碼測試一下。
Bing的翻譯API也不是絕對免費,每個月可以翻譯2M的字符的數據。這個當然不多,但一般人也夠用了。(純文本的聖經4M多)。超出后如果要再使用也必須付費,
費用好像是2M 40美刀。
另外Bing的API有一些有特色的地方,比如你可以在里面增加一些自己的翻譯,幫助修正你的翻譯結果,另外其還有語音能力。可以將你輸入的語音轉換為wav或者mp3文件。上流。
網上有些兄弟寫過Bing的API,但是大部分是是對於V1的版本的,V2版本改了驗證方式
現在微軟已經使用了新的驗證方式,使用APPID的方法已經玩不轉了。每次使用前必須用一個access token的東東,你必須自己申請一個ClientID和Client密碼。有興趣了解詳情的請參考
http://blogs.msdn.com/b/translation/p/gettingstarted1.aspx
http://api.microsofttranslator.com
Bing的API有3種調用接口,Ajax,SOAP,HTTP。我用Python搞,估計HTTP的方式更加合適我一點。我的只實現了部分接口,
我實現的Bing 翻譯V2的接口包括,翻譯,得到翻譯候選列表,(一次翻譯多個語句)設置翻譯內容,得到語音等幾個,其他的感覺有點多余,未必用得到。
如果沒有token或者Token超時,會自己重新去獲取Token,使用起來也比較簡單。
還是多說無益,上代碼,注釋應該足夠豐富,大家肯定看的明白。
1 # coding=utf-8 2 ''' 3 網上有些兄弟寫過,但是大部分是是對於V1的版本的,V2版本改了驗證方式 4 現在微軟已經使用了新的驗證方式,使用APPID的方法已經玩不轉了。必須用一個access token的東東 5 6 清參考 7 http://blogs.msdn.com/b/translation/p/gettingstarted1.aspx 8 http://api.microsofttranslator.com 9 10 ''' 11 12 import urllib 13 import urllib2 14 import json 15 import time 16 import unittest 17 import xml.etree.ElementTree 18 19 20 21 class Get_Translate_Data(object): 22 ''' 23 Get Translate 操作取回的數據的,解析后的得到的數據字段, 24 ''' 25 def __init__(self): 26 # #翻譯結果匹配程度 27 self._match_degree = 0 28 # #翻譯結果被用戶選擇的次數 29 self._selected_count = 0 30 # #翻譯結果可以認可的比率,MS自己有一套規則,自己上參考網址看 31 self._rating = 0 32 # #返回的結果 33 self._translated_text = "" 34 35 def __str__(self): 36 return ("match_degree:%s selected_count:%s rating:%s translated_text:%s")\ 37 % (self._match_degree, self._selected_count, self._rating, self._translated_text) 38 39 40 class Bing_Translator_API(object): 41 ''' 42 此工具用於使用bing的翻譯API,讓你快速的得到翻譯的結果 43 我是按照2012年12月22日看到的API V2的要求實現的, 44 實現的Bing 翻譯V2的接口包括,翻譯,得到翻譯候選列表,(一次翻譯多個語句)設置翻譯內容,得到語音, 45 ''' 46 # 最大請求的字符長度,微軟的要求 47 REQ_STR_MAX_LEN = 10000 48 # add trascation 增加翻譯的原文最大長度 49 ADD_ORIGINALTEXT_LEN = 1000 50 # add trascation 增加翻譯的翻譯文字長度 51 ADD_TRANSLATEDTEXT_LEN = 2000 52 # SPEEK string的最大值 53 SPEAK_STRING_LEN = 2000 54 55 # 最大返回的結果個數 56 RSP_RESULT_MAX_NUMBER = 10 57 58 # 取得acess token的兩個參數,常量 59 ACCESS_TOKEN_REQ_SCOPE = "http://api.microsofttranslator.com" 60 ACCESS_TOKEN_REQ_GRANT_TYPE = "client_credentials" 61 62 # POST取得ACESS TOKEN的URL 63 ACCESS_TOKEN_REQ_URL = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13" 64 # GET方法得到翻譯的結果,只得到一個結果,估計這個最常用 65 TRANSLATE_REQ_URL = "http://api.microsofttranslator.com/V2/Http.svc/Translate" 66 # POST取得翻譯結果的結果的URL,這個是一次可以取回多個翻譯結果 67 GET_TRANSLATE_REQ_URL = "http://api.microsofttranslator.com/V2/Http.svc/GetTranslations" 68 # 檢測語句的語言 69 DETECT_REQ_URL = "http://api.microsofttranslator.com/V2/Http.svc/Detect" 70 # 增加翻譯的URL 71 ADD_TRANSLATION_URL = "http://api.microsofttranslator.com/V2/Http.svc/AddTranslation" 72 # 發音的請求 73 SPEAK_REQ_URL = "http://api.microsofttranslator.com/V2/Http.svc/Speak" 74 75 # LC=language code,常用的幾個都寫在這兒,免得你還要查詢 76 LC_CHINESE_SIMPLIFIED = "zh-CHS" 77 LC_CHINESE_TRADITIONAL = "zh-CHT" 78 LC_ENGLISH = "en" 79 LC_JAPANESE = "ja" 80 LC_KOREAN = "ko" 81 LC_FRENCH = "fr" 82 LC_GERMAN = "de" 83 84 def __init__ (self, client_id, client_secret, proxy_conf=None): 85 ''' 86 @param client_id 客戶端ID,你在MS網址注冊得到的ID 87 @param client_secret 客戶端密鑰 88 @param proxy_conf 代理配置,默認None,不配置,如果配置http,https都要寫, 89 比如{'http': 'http://proxy.a.com:8080/','https': 'http://proxy.a.com:8080/'},折騰了我一個下午 90 ''' 91 92 # 你請求獲得acess token的兩個參數,客戶端ID,和一個驗證密碼 93 self._client_id = client_id 94 self._client_secret = client_secret 95 96 self._token_opener = None 97 self._api_opener = None 98 99 # 如果有代理,配置代理 100 if proxy_conf == None : 101 self._token_opener = urllib2.build_opener() 102 self._api_opener = urllib2.build_opener() 103 else: 104 self._token_opener = urllib2.build_opener(urllib2.ProxyHandler(proxy_conf), urllib2.HTTPSHandler()) 105 self._api_opener = urllib2.build_opener(urllib2.ProxyHandler(proxy_conf), urllib2.HTTPHandler()) 106 107 self._access_token = "" 108 self._expires_time = 0 109 110 111 112 113 def __get_acess_token(self, retry_num=3): 114 ''' 115 @brief 得到訪問的access token,如果已經有了token,而且沒有超時,就不繼續使用原有的token 116 @retry_num 重試的次數 117 ''' 118 # 檢查超時與否,如果沒有超時,什么都不做 119 if (time.time() - 10 < self._expires_time): 120 return 0 121 122 post_data = urllib.urlencode({'client_id':self._client_id, \ 123 'client_secret':self._client_secret, \ 124 'scope':Bing_Translator_API.ACCESS_TOKEN_REQ_SCOPE, 125 'grant_type':Bing_Translator_API.ACCESS_TOKEN_REQ_GRANT_TYPE }) 126 127 retry_count = 0 128 resp_data = None 129 130 # 進行N詞重試 131 while (retry_count < retry_num) : 132 try : 133 resp_data = self._token_opener.open(fullurl=Bing_Translator_API.ACCESS_TOKEN_REQ_URL, \ 134 data=post_data, \ 135 timeout=10) 136 except Exception, e: 137 retry_count += 1 138 self._token_opener.close() 139 print str(e), Bing_Translator_API.ACCESS_TOKEN_REQ_URL, retry_count 140 continue 141 break 142 143 self._token_opener.close() 144 if (retry_count == retry_num): 145 return -1 146 147 str_data = unicode(resp_data.read()) 148 149 # 分析json得到數據和超時時間, 150 token_data = json.loads(str_data) 151 # 注意,不要解碼,我畫蛇添足的搞了1個小時才發現這個錯誤 152 self._access_token = token_data["access_token"] 153 self._expires_time = time.time() + int(token_data["expires_in"]) 154 155 return 0 156 157 def translate(self, from_language, to_language , want_translate, content_type="text/plain", category="general", retry_num=3): 158 ''' 159 @brief 得到翻譯,只有一個翻譯結果,作為返回值返回, 160 @notice 這個方法使用的是GET 請求 161 @param from_language 翻譯的語言,參考前面的LC_XXX定義 162 @param to_language 163 @param want_translate 要翻譯的的文本信息,必須用UTF8的編碼, 164 @param content_type "text/plain" 或者"text/html" 165 @param category 分類,估計可以提高翻譯的准確度,但MS也沒有告知我們可以填寫啥,默認"general" 166 @return 一個元組,第一個數值是int,==0表示成功,第二個是成功后翻譯的語句, 167 ''' 168 ret_text = "" 169 170 if (len(want_translate) > Bing_Translator_API.REQ_STR_MAX_LEN): 171 return (-1, ret_text) 172 # 檢查token還是否有效,以及是否需要重新獲取 173 ret = self.__get_acess_token(retry_num) 174 if (ret != 0): 175 return (ret, ret_text) 176 177 # print self._api_opener.addheaders 178 # 得到請求的URL 179 url_all = Bing_Translator_API.TRANSLATE_REQ_URL + "?" + urllib.urlencode({'text':want_translate, \ 180 'from':from_language, \ 181 'to':to_language, \ 182 'contentType':content_type, \ 183 'category':category }) 184 url_req = urllib2.Request(url=url_all) 185 url_req.add_header('Authorization', 'bearer ' + self._access_token) 186 187 retry_count = 0 188 resp_data = None 189 # 進行N次重試 190 while (retry_count < retry_num) : 191 try : 192 resp_data = self._token_opener.open(url_req, timeout=10) 193 except Exception, e: 194 retry_count += 1 195 print str(e), url_req, retry_count 196 continue 197 else: 198 break 199 finally: 200 self._token_opener.close() 201 202 if (retry_count == retry_num): 203 return (-1, ret_text) 204 205 # 解析XML結果得到數據 206 xml_str = resp_data.read() 207 tag = xml.etree.ElementTree.fromstring(xml_str) 208 ret_text = tag.text 209 210 return (0, ret_text) 211 212 def get_translate(self, from_language, to_language , want_translate, result_list, \ 213 content_type="text/plain", category="general", user="", uri="", retry_num=3): 214 ''' 215 @brief 得到翻譯,可能有多個翻譯的結果返回,返回的是一個列表, 216 @notice 這個方法使用的是GET 請求 217 @param result_list 返回參數,返回的翻譯list的Get_Translator_Data 218 @param user 用戶名稱,默認為"",如果對翻譯效果不滿意,可以改為all 219 @param uri URI 220 @param 其他參數同translate函數,不多解釋 221 @return 返回0表示成功,其他表示失敗 222 ''' 223 224 if (len(want_translate) > Bing_Translator_API.REQ_STR_MAX_LEN): 225 return -1 226 # 檢查token還是否有效,以及是否需要重新獲取 227 ret = self.__get_acess_token(retry_num) 228 if (ret != 0): 229 return ret 230 231 232 # 得到請求的URL 233 url_all = Bing_Translator_API.GET_TRANSLATE_REQ_URL + "?" + urllib.urlencode({'text':want_translate, \ 234 'from':from_language, \ 235 'to':to_language, \ 236 'maxTranslations':Bing_Translator_API.RSP_RESULT_MAX_NUMBER }) 237 238 # 其實不發送下面Post數據也可以發送請求(發送參數="",不能不寫,否則是GET請求),也可以得到結果,我測試過。增加下面這段反而又讓我調試了半天 239 # Post 請求的參數,里面的State是一個事務ID,請求帶過去,返回的結果,MS給你帶回來,你要希望使用,自己改造 240 # 這樣的到的翻譯很多,但效果感覺比較糟糕,估計有些是用戶添加的,如果你不喜歡,可以在User的值改為all 241 post_data = "<TranslateOptions xmlns=\"http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2\">"\ 242 "<Category>%s</Category>"\ 243 "<ContentType>%s</ContentType>"\ 244 "<ReservedFlags></ReservedFlags>"\ 245 "<State>20121221</State>"\ 246 "<Uri>%s</Uri>"\ 247 "<User>%s</User>"\ 248 "</TranslateOptions>" % (category, content_type, uri, user) 249 250 251 url_req = urllib2.Request(url=url_all, data=post_data) 252 url_req.add_header('Authorization', 'bearer ' + self._access_token) 253 # 如果要加post數據,這行必須加,否返回500的錯誤,看了半天C#的例子 254 url_req.add_header('Content-Type', 'text/xml') 255 retry_count = 0 256 resp_data = None 257 # 進行N次重試 258 while (retry_count < retry_num) : 259 try : 260 resp_data = self._token_opener.open(url_req, timeout=10) 261 except Exception, e: 262 retry_count += 1 263 print str(e), url_req, retry_count 264 continue 265 else: 266 break 267 finally: 268 self._token_opener.close() 269 270 if (retry_count == retry_num): 271 return -1 272 273 # 倒霉的XML namespace,麻煩死了 274 MS_GETTRNS_NAMESPACES = "http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" 275 276 xml_str = resp_data.read() 277 tag = xml.etree.ElementTree.fromstring(xml_str) 278 # 前面非要家那個.//,而且還必須有那個XML的名字空間{http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2} 279 tans_list = tag.findall(".//{%s}TranslationMatch" % MS_GETTRNS_NAMESPACES) 280 for tag_item in tans_list: 281 trans_data = Get_Translate_Data() 282 for iter_item in tag_item.iter(): 283 if iter_item.tag == "{%s}Count" % MS_GETTRNS_NAMESPACES: 284 trans_data._selected_count = int(iter_item.text) 285 if iter_item.tag == "{%s}MatchDegree" % MS_GETTRNS_NAMESPACES: 286 trans_data._match_degree = int(iter_item.text) 287 if iter_item.tag == "{%s}Rating" % MS_GETTRNS_NAMESPACES: 288 trans_data._rating = int(iter_item.text) 289 if iter_item.tag == "{%s}TranslatedText" % MS_GETTRNS_NAMESPACES: 290 trans_data._translated_text = iter_item.text 291 # print trans_data 292 result_list.append(trans_data) 293 return 0 294 295 def detect(self, detect_text, retry_num=3): 296 ''' 297 @brief 檢查語句的語言 298 @notice 這個方法使用的是GET 請求 299 @param detect_text 檢測的語句,必須用UTF8的編碼, 300 @return 一個元組,第一個數值是int,==0表示成功,否則表示失敗,第二個是成功后返回語言的標識,如果失敗,返回'' 301 ''' 302 ret_text = "" 303 304 if (len(detect_text) > Bing_Translator_API.REQ_STR_MAX_LEN): 305 return (-1, ret_text) 306 # 檢查token還是否有效,以及是否需要重新獲取 307 ret = self.__get_acess_token(retry_num) 308 if (ret != 0): 309 return (ret, ret_text) 310 311 # print self._api_opener.addheaders 312 # 得到請求的URL 313 url_all = Bing_Translator_API.DETECT_REQ_URL + "?" + urllib.urlencode({'text':detect_text}) 314 url_req = urllib2.Request(url=url_all) 315 url_req.add_header('Authorization', 'bearer ' + self._access_token) 316 317 retry_count = 0 318 resp_data = None 319 # 進行N次重試 320 while (retry_count < retry_num) : 321 try : 322 resp_data = self._token_opener.open(url_req, timeout=10) 323 except Exception, e: 324 retry_count += 1 325 print str(e), url_req, retry_count 326 continue 327 else: 328 break 329 finally: 330 self._token_opener.close() 331 332 if (retry_count == retry_num): 333 return (-1, ret_text) 334 335 # 解析XML結果得到數據 336 xml_str = resp_data.read() 337 tag = xml.etree.ElementTree.fromstring(xml_str) 338 ret_text = tag.text 339 340 return (0, ret_text) 341 342 def add_translation(self, original_text, translated_text, from_language, to_language, user, \ 343 rating=1, content_type="text/plain", category="general", uri="", retry_num=3): 344 ''' 345 @brief 增加翻譯內容,用於改善后面的翻譯 346 @notice 這個方法使用的是GET 請求 347 @param original_text 原文 348 @param translated_text 已經翻譯的原文 349 @param from_language 350 @param to_language 351 @param usr 用戶名稱,估計在get的時候會有一些作用 352 @param rating -10~10 353 @param content_type "text/plain" 或者"text/html" 354 @param category 分類,估計可以提高翻譯的准確度,但MS也沒有告知我們可以填寫啥,默認"general" 355 @param uri 356 @param retry_num 重試次數 357 @return 返回0表示成功,其他表示失敗 358 ''' 359 360 if (len(original_text) > Bing_Translator_API.ADD_ORIGINALTEXT_LEN): 361 return -1 362 if (len(translated_text) > Bing_Translator_API.ADD_TRANSLATEDTEXT_LEN): 363 return -1 364 # 檢查token還是否有效,以及是否需要重新獲取 365 ret = self.__get_acess_token(retry_num) 366 if (ret != 0): 367 return (ret, ret_text) 368 369 # print self._api_opener.addheaders 370 # 得到請求的URL 371 url_all = Bing_Translator_API.ADD_TRANSLATION_URL + "?" + urllib.urlencode({'originalText':original_text, \ 372 'translatedText':translated_text, \ 373 'from':from_language, \ 374 'to':to_language, \ 375 'rating':rating, \ 376 'contentType':content_type, \ 377 'category':category, \ 378 'user':user, 379 'uri':uri }) 380 url_req = urllib2.Request(url=url_all) 381 url_req.add_header('Authorization', 'bearer ' + self._access_token) 382 383 retry_count = 0 384 resp_data = None 385 # 進行N次重試 386 while (retry_count < retry_num) : 387 try : 388 resp_data = self._token_opener.open(url_req, timeout=10) 389 except Exception, e: 390 retry_count += 1 391 print str(e), url_req, retry_count 392 continue 393 else: 394 break 395 finally: 396 self._token_opener.close() 397 398 if (retry_count == retry_num): 399 return -1 400 401 return 0 402 403 404 def speak(self, speak_text, language, format="audio/wav", options="MinSize", retry_num=3): 405 ''' 406 @brief 得到翻譯,可能有多個翻譯的結果返回,返回的是一個列表, 407 @notice 這個方法使用的是GET 請求 408 @param speak_text 409 @param language 語言 410 @param format 為 audio/wav 或者 audio/mp3 411 @param options 為“MaxQuality” 或者 "MinSize" 412 @return 返回0表示成功,其他表示失敗 413 ''' 414 415 if (len(speak_text) > Bing_Translator_API.SPEAK_STRING_LEN): 416 return -1 417 # 檢查token還是否有效,以及是否需要重新獲取 418 ret = self.__get_acess_token(retry_num) 419 if (ret != 0): 420 return ret 421 422 ret_speak = "" 423 # 得到請求的URL 424 url_all = Bing_Translator_API.SPEAK_REQ_URL + "?" + urllib.urlencode({'text':speak_text, \ 425 'language':language, \ 426 'format':format, \ 427 'options':options }) 428 url_req = urllib2.Request(url=url_all) 429 url_req.add_header('Authorization', 'bearer ' + self._access_token) 430 431 retry_count = 0 432 resp_data = None 433 # 進行N次重試 434 while (retry_count < retry_num) : 435 try : 436 resp_data = self._token_opener.open(url_req, timeout=10) 437 except Exception, e: 438 retry_count += 1 439 print str(e), url_req, retry_count 440 continue 441 else: 442 break 443 finally: 444 self._token_opener.close() 445 446 if (retry_count == retry_num): 447 return (-1, ret_speak) 448 449 # 解析XML結果得到數據 450 ret_speak = resp_data.read() 451 452 return (0, ret_speak) 453 454 #單元測試,單元測試 455 class Test_Bing_Translator_API(unittest.TestCase): 456 457 def setUp(self): 458 # self._test_api = Bing_Translator_API(client_id="你的客戶端ID",\ 459 # client_secret="你的客戶端密鑰") 460 self._test_api = Bing_Translator_API(client_id="你的客戶端ID", \ 461 client_secret="你的客戶端密鑰", \ 462 proxy_conf={'http': '你的代理服務器:端口/', 'https': '你的代理服務器:端口'})
463 464 465 def test_translate(self): 466 ret, trans_str = self._test_api.translate(Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 467 Bing_Translator_API.LC_ENGLISH, \ 468 "中華人民共和國"); 469 print ret , trans_str 470 471 test_string = "中華人民共和國" 472 test_gb2312 = test_string.encode('gb2312') 473 # 這個地方翻譯會錯誤,所以編碼不能用GB2312這類編碼 474 ret, trans_str = self._test_api.translate(Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 475 Bing_Translator_API.LC_ENGLISH, \ 476 test_gb2312); 477 print ret , trans_str 478 479 def test_get_translate(self): 480 result_list = [] 481 ret = self._test_api.get_translate(Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 482 Bing_Translator_API.LC_ENGLISH, \ 483 "中華人民共和國", 484 result_list); 485 for trans_data in result_list : 486 print trans_data 487 result_list = [] 488 ret = self._test_api.get_translate(Bing_Translator_API.LC_ENGLISH, \ 489 Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 490 "to", 491 result_list); 492 for trans_data in result_list : 493 print trans_data 494 495 def test_detect(self): 496 ret, detect_lan = self._test_api.detect("中華人民共和國") 497 print ret, detect_lan 498 ret, detect_lan = self._test_api.detect("made in china") 499 print ret, detect_lan 500 501 def test_add_translation(self): 502 ret = self._test_api.add_translation(original_text="china", \ 503 translated_text="瓷器", \ 504 from_language=Bing_Translator_API.LC_ENGLISH, \ 505 to_language=Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 506 user="fullsail") 507 print ret 508 result_list = [] 509 ret = self._test_api.get_translate(Bing_Translator_API.LC_ENGLISH, \ 510 Bing_Translator_API.LC_CHINESE_SIMPLIFIED, \ 511 "china", 512 result_list, 513 user="fullsail"); 514 for trans_data in result_list : 515 print trans_data 516 517 518 def test_speek(self): 519 ret, ret_speak = self._test_api.speak(speak_text="人一走,茶就涼,是自然規律;人沒走,茶就涼,是世態炎涼。一杯茶,佛門看到的是禪,道家看到的是氣,儒家看到的是禮,商家看到的是利。茶說:我就是一杯水,給你的只是你的想像,你想什么,什么就是你。心即茶,茶即心。", \ 520 language=Bing_Translator_API.LC_CHINESE_SIMPLIFIED); 521 522 523 file_hdl = open("D:\\123.wav", "wb+") 524 file_hdl.write(ret_speak) 525 file_hdl.close() 526 527 528 if __name__ == '__main__': 529 unittest.main() 530
最早看python應該是8年前了,但直到最近2個月才使用,計算機是實踐科學,看了不用,等於浪費,沒有任何效果 這次算是一個小小的進步。
【本文作者是雁渡寒潭,本着自由的精神,你可以在無盈利的情況完整轉載此文檔,轉載時請附上BLOG鏈接:http://www.cnblogs.com/fullsail/ 或者http://blog.csdn.net/fullsail,否則每字一元,每圖一百不講價。對Baidu文庫。360doc加價一倍】
想得卻不可得 你奈人生何 該捨的捨不得 只顧著跟往事瞎扯