淘宝网的搜索关键字的编码与解码-URL的编码与解码

Google
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
之前对于 URL的编码只是有了个大概的了解.

知道为什么要对URL进行 Ecode.Decode而已.

今天看了JAVAEYE上的贴子,印象特别深刻. 使用base64编码,对于SEO的好处

截取一些关键的部分来备份.

原文出处: 作者gembler 确实比较善于动手去研究.赞一个.
 
http://www.javaeye.com/topic/286240?page=1

很久之前发现淘宝网的搜索关键字在编码后有点可爱,小写英文字母+数字的组合。

想去研究一下,但是后面好像没了回事似的。。记性不好

然后这两天不知道为什么,突然间抽起条脑筋,跑去研究。。。。

 

--------------------------开始分割线--------------------------

 

在淘宝网上用关键字“gembler”搜索一下商品,得出以下URL:

 

Java代码 复制代码
  1. http://search1.taobao.com/browse/0/n-0-----------------g,m5sw2ytmmvza----------------40--commend-0-all-0.htm?at_topsearch=1  
http://search1.taobao.com/browse/0/n-0-----------------g,m5sw2ytmmvza----------------40--commend-0-all-0.htm?at_topsearch=1

                                                                                           (留意上面这里:“m5sw2ytmmvza”

 

经过一轮 天昏地暗、沙尘滚滚 的分析、研究之后,得出以下结论:

 

  在Base64中,码表是由 [A-Z,a-z,0-9,+,/,=(pad)] 组成的。
  然后自己也弄个码表,由 [a-z,2-7(这个2-7是在
淘宝网上搜索了n次而得出的结论] 组成的:

 

abcdefghijklmnopqr
01234567891011121314151617

 

-------------------------------愚蠢分割线---------------------------------

 

stuvwxyz234567
1819202122232425262728293031

 
  在Base64中,是将二进制连成一串,然后再按6位来分割,分割完后在前面补0,这个地球人都知道,不多说了。
  而我呢,哈哈,捣蛋一下,按5位来分割,如果刚好够分,那就爽了,

  但是,世事往往不如人意,位数不够,那咋办呢?看下面 :)
 
  在Base64中,是用"="来解决的吧。
  现在呢,就是在前面补0,然后在后面再补0,其实就是前后补0,

  但是,按5位来分,前面3个0是补定的了,后面的0就看上帝怎么安排了。
 
  举个小例子:字符串 "aaa",(编码/加密)后是 "mfqwc"
 

二进制:011000010110000101100001

转换后:(000)01100(000)00101(000)10000(000)10110(000)0001(0)
十进制:12516222
码表对应:mfqwc

 
  反过来,(解码/解密):
 

码表对应:mfqwc
十进制:12516222
二进制:0000110000000101000100000001011000000010
去掉前3个0后:0110000101100001011000010
合并后:0110000101100001011000010

 
  然后把合并后的串的长度除一下8,发现多了个0:
 

合并后的二进制码:0110000101100001011000010


  多了就算了,不要了(其实是在{编码/加密}的分割时候,在分剩的余数的后面补的0)。
  然后再将 byte[] 转回字符串

 

  OK!又见回"aaa"了。    :)
 
  *有一点值得注意的是:UTF-8、GBK、GB18030 一般都没什么问题,但是 GB2312 可能字符集不够丰富,繁体字在decode的时候成问号了。

 

搞了半天,“可视化编辑器”的 Java Code不好使,骨干代码在回帖里贴出................(完整的代码,有兴趣的同志们请见附件!





编码:

Java代码 复制代码
  1. /** 
  2.  * (编码/加密)字节数组 
  3.  *  
  4.  * @author gembler 
  5.  * @version 2008-12-3 下午03:14:43 
  6.  *  
  7.  * @param keyBytes 
  8.  *            需要(编码/加密)的字节数组 
  9.  *  
  10.  * @return (编码/加密)后的字符串 
  11.  */  
  12. private static String encode(byte[] keyBytes) {  
  13.   
  14.     if (keyBytes == null || keyBytes.length < 1) {  
  15.   
  16.         return "";  
  17.   
  18.     }  
  19.   
  20.     /* 
  21.      * 合并二进制码,  
  22.      * 如:  
  23.      *     00101010 11010011 00101101 10100011  
  24.      *   to 
  25.      *     00101010110100110010110110100011 
  26.      */  
  27.   
  28.     StringBuilder mergrd = new StringBuilder();  
  29.   
  30.     for (int i = 0; i < keyBytes.length; i++) {  
  31.   
  32.         FormatUtil.formatBinary(keyBytes[i], mergrd);  
  33.   
  34.     }  
  35.   
  36.     /* 
  37.      * 以5个bit为单位,计算能分多少组, 
  38.      * 如: 
  39.      *     00101010110100110010110110100011 
  40.      *   to 
  41.      *     00101 01011 01001 10010 11011 01000 11 
  42.      *                                          | 
  43.      *                                   (这个11为余下的位) 
  44.      */  
  45.   
  46.     int groupCount = mergrd.length() / FIVE_BIT;  
  47.   
  48.     // 计算余下的位数  
  49.     int lastCount = mergrd.length() % FIVE_BIT;  
  50.   
  51.     // 类似数据分页的算法,有余数的情况下需要加 1。  
  52.     if (lastCount > 0) {  
  53.   
  54.         groupCount += 1;  
  55.   
  56.     }  
  57.   
  58.     /* 
  59.      * (编码/加密) 
  60.      */  
  61.   
  62.     StringBuilder sbEncoded = new StringBuilder();  
  63.   
  64.     // 循环所需的条件  
  65.     int forMax = groupCount * FIVE_BIT;  
  66.   
  67.     // 每次递增5位来截取  
  68.     for (int i = 0; i < forMax; i += FIVE_BIT) {  
  69.   
  70.         // 结束点  
  71.         int end = i + FIVE_BIT;  
  72.   
  73.         /* 
  74.          * 如果结束点比已合并的二进制码串的长度要大, 
  75.          * 相当于有余数, 
  76.          * 并且表示当前循环到了(已合并的二进制码串的长度 % FIVE_BIT)的那一截。 
  77.          */  
  78.   
  79.         // 标记是否到了余数的那一截  
  80.         boolean flag = false;  
  81.   
  82.         if (end > mergrd.length()) {  
  83.   
  84.             /* 
  85.              * 如果结束点比已合并的二进制码串的长度要大, 
  86.              * 结束点需要被重设为:  
  87.              * 已合并的二进制码串的长度,等价于(i + lastCount). 并且重设标记。 
  88.              */  
  89.   
  90.             end = (i + lastCount);  
  91.   
  92.             flag = true;  
  93.   
  94.         }  
  95.   
  96.         // 截取  
  97.         String strFiveBit = mergrd.substring(i, end);  
  98.   
  99.         // 截取后从二进制转为十进制  
  100.         int intFiveBit = Integer.parseInt(strFiveBit, BINARY);  
  101.   
  102.         if (flag) {  
  103.   
  104.             /* 
  105.              * 如果结束点比已合并的二进制码串的长度要大, 
  106.              * 或者是到了余数的那一截:  
  107.              * 需要左移操作,假设余下的二进制位为:11, 
  108.              * 那么需要从后面补0,左移操作后为 (000)11(000) 
  109.              */  
  110.   
  111.             intFiveBit <<= (FIVE_BIT - lastCount);  
  112.   
  113.         }  
  114.   
  115.         // 利用该十进制数作为码表的索引获取对应的字符,并追加到sbEncoded  
  116.         sbEncoded.append(CODEC_TABLE.charAt(intFiveBit));  
  117.   
  118.     }  
  119.   
  120.     return sbEncoded.toString();  
  121.   

解码:

Java代码 复制代码
  1. /** 
  2.  * (解码/解密)字符串 
  3.  *  
  4.  * @author gembler 
  5.  * @version 2008-12-3 下午03:15:00 
  6.  *  
  7.  * @param code 
  8.  *            需要(解码/解密)的字符串 
  9.  * @param characterSet 
  10.  *            字符集 
  11.  *  
  12.  * @return (解码/解密)后的字符串 
  13.  */  
  14. public static String decode(String code, String characterSet) {  
  15.   
  16.     if (code == null || code.length() < 1) {  
  17.   
  18.         return "";  
  19.   
  20.     }  
  21.   
  22.     /* 
  23.      * 拆除每一个字符,从码表里获取相应的索引。 
  24.      */  
  25.   
  26.     StringBuilder sbBinarys = new StringBuilder();  
  27.   
  28.     for (int i = 0; i < code.length(); i++) {  
  29.   
  30.         // 从码表里获取相应的索引  
  31.         int index = getCodecTableIndex(code.charAt(i));  
  32.   
  33.         // 将十进制的索引转换为二进制串  
  34.         String indexBinary = Integer.toBinaryString(index);  
  35.   
  36.         // 去掉前3个0,并且追加到sbBinarys  
  37.         FormatUtil.formatBinary(indexBinary, sbBinarys, FIVE_BIT);  
  38.   
  39.     }  
  40.   
  41.     /* 
  42.      * 按8个bit拆分,剩下的余数扔掉。 
  43.      * 扔掉的余数是在(编码/加密)的分割时候,在分剩的余数的后面补的0 
  44.      */  
  45.   
  46.     byte[] binarys = new byte[sbBinarys.length() / EIGHT_BIT];  
  47.   
  48.     for (int i = 0, j = 0; i < binarys.length; i++) {  
  49.   
  50.         // 每8个bit截取一份  
  51.         String sub = sbBinarys.substring(j, j += EIGHT_BIT);  
  52.   
  53.         // 将截取下来的二进制串转换为十进制  
  54.         Integer intBinary = Integer.valueOf(sub, BINARY);  
  55.   
  56.         binarys[i] = intBinary.byteValue();  
  57.   
  58.     }  
  59.   
  60.     String decoded = null;  
  61.   
  62.     if (characterSet == null || characterSet.length() < 1) {  
  63.   
  64.         // 采用默认语言环境的 character set。  
  65.         decoded = new String(binarys);  
  66.   
  67.     } else {  
  68.   
  69.         try {  
  70.   
  71.             // 采用指定的 character set。  
  72.             return new String(binarys, characterSet);  
  73.   
  74.         } catch (UnsupportedEncodingException e) {  
  75.             // ignore...  
  76.         }  
  77.     }  
  78.     return decoded;  


测试:

Java代码 复制代码
  1. BufferedReader br = new BufferedReader(new InputStreamReader(  
  2.         System.in));  
  3.   
  4. while (true) {  
  5.   
  6.     System.out.print("输入字符号串:");  
  7.   
  8.     String in = br.readLine();  
  9.   
  10.     if ("exit".equalsIgnoreCase(in)) {  
  11.         break;  
  12.     }  
  13.   
  14.     String enCode = Codec.encode(in);  
  15.   
  16.     String deCode = Codec.decode(enCode);  
  17.     System.out.println();  
  18.     System.out.println("original: " + in);  
  19.     System.out.println("encode: " + enCode);  
  20.     System.out.println("decode: " + deCode);  
  21.     System.out.println();  
  22. }  
			BufferedReader br = new BufferedReader(new InputStreamReader(
					System.in));

			while (true) {

				System.out.print("输入字符号串:");

				String in = br.readLine();

				if ("exit".equalsIgnoreCase(in)) {
					break;
				}

				String enCode = Codec.encode(in);

				String deCode = Codec.decode(enCode);
				System.out.println();
				System.out.println("original: " + in);
				System.out.println("encode: " + enCode);
				System.out.println("decode: " + deCode);
				System.out.println();
			}


测试结果:

      输入:JavaEye论坛频道
      输出:

        original: JavaEye论坛频道
        encode: jjqxmykfpfs4fw6mwpdllnoa
        decode: JavaEye论坛频道


      然后跑去淘宝网敲入"JavaEye论坛频道"搜索。


      得出:


      http://search1.taobao.com/browse/0/n-g,jjqxmykfpfs4fw6mwpdllnoa----------------40--commend-0-all-0.htm?at_topsearch=1

      比较一下:

        jjqxmykfpfs4fw6mwpdllnoa - console输出
        jjqxmykfpfs4fw6mwpdllnoa - 淘宝网URL

下面是一些高手的回答:

得,首先,不用普通的参数,我想是这样,淘宝的服力很大,对应用的性能要求应该很苛刻,搜索的候你看起来你入只是一个关键字,实际远远不止,有几十个参数,如果用一事先定好位置的数据来表示各参数,服器不用解析那多的字符串,不用理那多的参数名=参数,开发维护起 来会增加一些工作量,但是服力会小一些,流量会小一些。

至于编码方式,回楼上某位同学的base64编码不是淘宝自己搞出来的,是email现时特殊期的物,具体的可以google一下,base64编码后数据量比urlencode60%左右 



 

LZ害。我TB好像习惯对URLBASE64URL有保密信息的会再来个BF加密。使用BASE64怕非英文的URL后会生服器操作的命令。BASE64编码快,解也快。

 

我来大家剖析一下吧,淘宝的原来一帮人是从阿里出来的。而阿里最悍的就是SEO

 

这样大家是否有点清楚了。

 

SEOURL是非常的看重的,而url中如果包含太多中文等字符的SEO是不友好。

 

搞一个英文加字符的编码对机器来说还是比容易好搞的。

 

至于非要自己搞一个,那其实这也不是什别难东东,增加一些神秘感不是更好。

 


跟效率没有系, URL的并不能短, BASE64编码是字符编码有限度。这么做完全让复杂URL看上去好看点, 方便SEO。 其他的没有什么问题。 另外一个是URL的具体参数的含个需要有个大的正表达规则来解析URL开发候,URLPARAMXX的形式。 问题没有好追究的, 一个易的URL们总是比而已。

  另外, 有一个开发经验问题 URL实际上大小写, 在EMAIL送, 都存在各种问题, 完全使用ASCII也可以避免编码问题。 反正URL都通一个URL生成的工具完成的。 URL经验是通血的教得到的, 那些RFC推荐的范未必适合做用。

  不是说这个方法是完美的, 可是能解决很多问题。 网站的性能不是在文本理上, 目前的CPU算能力已很多开发的想象了。

  了, 我不是TAOBAO的, PM我了。。。。

 



 
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值