我们在使用新浪微博API时,有时需要得到一个微博的url,但是如statuses/public_timeline等接口中取得的微博status的字段中并没有包含。不过,status中包含了一个mid字段,通过mid,我们实际上是可以通过计算得到url的。
在开始计算之前有必要说明一下,什么是base62编码。它实际上就是十进制和62位进制的互换。对于62进制,从0数到9以后,10用小写字母a表示,接着数完26个字母,到z为35,然后36为大写字母A,一直到61为大写字母Z。所以,我们可以实现十进制数字base62编码的encode和decode。下面的代码实际上来自stackoverflow:
1 ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 2 3 def base62_encode(num, alphabet=ALPHABET): 4 """Encode a number in Base X 5 6 `num`: The number to encode 7 `alphabet`: The alphabet to use for encoding 8 """ 9 if (num == 0): 10 return alphabet[0] 11 arr = [] 12 base = len(alphabet) 13 while num: 14 rem = num % base 15 num = num // base 16 arr.append(alphabet[rem]) 17 arr.reverse() 18 return ''.join(arr) 19 20 def base62_decode(string, alphabet=ALPHABET): 21 """Decode a Base X encoded string into the number 22 23 Arguments: 24 - `string`: The encoded string 25 - `alphabet`: The alphabet to use for encoding 26 """ 27 base = len(alphabet) 28 strlen = len(string) 29 num = 0 30 31 idx = 0 32 for char in string: 33 power = (strlen - (idx + 1)) 34 num += alphabet.index(char) * (base ** power) 35 idx += 1 36 37 return num
下面先说url到mid的转换。对于一个新浪微博url,它是形如:http://weibo.com/2991905905/z579Hz9Wr,中间的数字是用户的uid,重要的是后面的字符串“z579Hz9Wr”。它的计算其实也很简单,从后向前四个字符一组,就得到:
z
579H
z9Wr
将每个字符串用base62编码来decode,就可以得到它们的十进制数字分别为:
35
1219149
8379699
将它们拼起来就可以得到mid为:“3512191498379699”。这里要强调的是:对于除了开头的字符串,如果得到的十进制数字不足7位,需要在前面补足0。比如得到的十进制数分别为:35,33040,8906190,则需要在33040前面添上两个0。
代码如下:
1 def url_to_mid(url): 2 ''' 3 >>> url_to_mid('z0JH2lOMb') 4 3501756485200075L 5 >>> url_to_mid('z0Ijpwgk7') 6 3501703397689247L 7 >>> url_to_mid('z0IgABdSn') 8 3501701648871479L 9 >>> url_to_mid('z08AUBmUe') 10 3500330408906190L 11 >>> url_to_mid('z06qL6b28') 12 3500247231472384L 13 >>> url_to_mid('yCtxn8IXR') 14 3491700092079471L 15 >>> url_to_mid('yAt1n2xRa') 16 3486913690606804L 17 ''' 18 url = str(url)[::-1] 19 size = len(url) / 4 if len(url) % 4 == 0 else len(url) / 4 + 1 20 result = [] 21 for i in range(size): 22 s = url[i * 4: (i + 1) * 4][::-1] 23 s = str(base62_decode(str(s))) 24 s_len = len(s) 25 if i < size - 1 and s_len < 7: 26 s = (7 - s_len) * '0' + s 27 result.append(s) 28 result.reverse() 29 return int(''.join(result))
mid转为url也就很简单了,对于一个mid,我们从后向前每7位一组,用base62编码来encode,拼起来即可。同样要注意的是,每7个一组的数字,除了开头一组,如果得到的62进制数字不足4位,需要补足0。
1 def mid_to_url(midint): 2 ''' 3 >>> mid_to_url(3501756485200075) 4 'z0JH2lOMb' 5 >>> mid_to_url(3501703397689247) 6 'z0Ijpwgk7' 7 >>> mid_to_url(3501701648871479) 8 'z0IgABdSn' 9 >>> mid_to_url(3500330408906190) 10 'z08AUBmUe' 11 >>> mid_to_url(3500247231472384) 12 'z06qL6b28' 13 >>> mid_to_url(3491700092079471) 14 'yCtxn8IXR' 15 >>> mid_to_url(3486913690606804) 16 'yAt1n2xRa' 17 ''' 18 midint = str(midint)[::-1] 19 size = len(midint) / 7 if len(midint) % 7 == 0 else len(midint) / 7 + 1 20 result = [] 21 for i in range(size): 22 s = midint[i * 7: (i + 1) * 7][::-1] 23 s = base62_encode(int(s)) 24 s_len = len(s) 25 if i < size - 1 and len(s) < 4: 26 s = '0' * (4 - s_len) + s 27 result.append(s) 28 result.reverse() 29 return ''.join(result)
运行doctest可以看到所有的测试用例都通过了。
最后其实我不太明白为什么新浪微博不直接把url包含在字段中,而新浪微博的开放平台也有很多不符合标准的地方,其实本文的内容并没有什么技术含量,不过就是让开发人员折腾一下。还有比如refresh token的问题等等,这里就不一一枚举了。