竹磬网-邵珠庆の日记 生命只有一次,你可以用它来做些更多伟大的事情–Make the world a little better and easier


261月/141

解析12306订票流程

发布在 邵珠庆

前言

每当春节临近时,因为网络的方便,访问12306购买火车票回家过年成了很多人的首选。但由于12306的种种不给力,给那些在官网刷票的人带来了很多的不便。从2011年未12306上线起,连续几年回家我都是靠网上购票,今年也不例外;我记得11年时我使用的是官网直接购票,到了12年则使用了新出的木鱼抢票助手,而今年我用了360与猎豹两款主流抢票浏览器,还发动了几位朋友一起帮忙,才买到了一张差强人意的票,现在感觉买票是越来越困难。而就在前几天媒体还曝出了商业黄牛使用假身份证生成器10分钟钞杀1000多张票的新闻,让人吃惊不已。于是就萌生了自己写一个抢票应用的念头,最开始设想的就是本地桌面应用,而非浏览器插件,个人觉得本地应用始终比浏览器插件敏捷,因为本地应用可以精确稳定的请求有用的链接,过滤图片和CSS等前台无用请求,可以节省网络消耗时间。于是我花了一段时间将12306的整体订票流程解析了一遍,其间还经历了一次12306的改版,幸好主体流程改动不是很大,终算有点收获。

粗略的将12306的流程划分为:登录、查询和订票三大模块,下面就这三大模块逐一说明:

1.登录

登录12306请求的URL是:https://kyfw.12306.cn/otn/login/init,可以使用Firbug抓取一下它的请求头,得到的response响应内容如下:

从中可以看到Set-Cookie信息,也就是说,如果想要登录就必须先请求https://kyfw.12306.cn/otn/login/init这个链接,以获取服务端设置的Cookie信息,而有了该Cookie信息就可以将其保存,以备下步的请求使用。

再来分析一下它的页面HTML与其对应处理登录的Javascript脚本文件(https://kyfw.12306.cn/otn/resources/merged/login_js.js),得到如下流程:

1.用户点击登录提交时先要验证请求一下:https://kyfw.12306.cn/otn/login/loginAysnSuggest链接,用于判断当前网络环境是否可以登录,得到JSON数据(通过Firebug抓包):

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. {  
  2.     "validateMessagesShowId":"_validatorMessage"  
  3.     "status":true  
  4.     "httpstatus":200,  
  5.     "data":{  
  6.         "loginCheck":"Y"  
  7.     },  
  8.     "messages":[],  
  9.     "validateMessages":{}  
  10. }  

 

这里通过判断data.loginCheck是否为字符串Y判断用户是否可以登录,如不能登录,则显示messages中的内容.

2.当用户登录信息检查成功时,则POST请求https://kyfw.12306.cn/otn/login/userLogin,得到登录请求后的HTML,对应请求的参数为:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. "loginUserDTO.user_name":  // 用户名  
  2. "userDTO.password":        // 密码  
  3. "randCode":                // 验证码  

注:登录图片验证码的获取地址可以从登录页面的HTML中得到为:https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=login&rand=sjrand

 

3.通过解析获取的HTML可以根据id为login-txt的<span>标签来判断是否登录成功,登录成功的对应的HTML内容为:

 

[html] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. <span class="login-txt" style="color: #666666">  
  2.     <span>意见反馈:  
  3.          <a class="cursor colorA" href="mailto:12306yjfk@rails.com.cn">  
  4.              12306yjfk@rails.com.cn  
  5.          </a>您好,  
  6.     </span>  
  7.     <a id="login_user" href="/otn/index/initMy12306"   
  8.        class="colorA" style="margin-left:-0.5px;"><span>登录成功用户名</span></a>|  
  9.     <a id="regist_out" href="/otn/login/loginOut">退出</a>  
  10. </span>  

失败的内容为:

 

[html] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. <span class="login-txt" style="color: #666666">  
  2.     <span>意见反馈:  
  3.          <a class="cursor colorA" href="mailto:12306yjfk@rails.com.cn">  
  4.                12306yjfk@rails.com.cn  
  5.           </a>您好,请  
  6.     </span>  
  7.     <a id="login_user" href="/otn/login/init"  
  8.        class="colorA" style="margin-left:-0.5px;">登录</a> |  
  9.     <a id="regist_out" href="/otn/regist/init">注册</a>  
  10. </span>  

 

如上登录成功即可进行下一步的操作:对于车次的查询。

2,车次查询

新版车次预订的查询(这里单指单程票查询)大大减化了请求参数,只接收出发地编码,到达地编码,出发日期与旅客编码四个参数,所有的过滤操作都扔给了前台Javascript,这也说明了车次查询流程的简单,只需请求一个链接地址:

查询车次是通过GET:https://kyfw.12306.cn/otn/leftTicket/query链接获取的,对应的查询参数为(GET请求注意查询参数的顺序):

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. leftTicketDTO.train_date=2014-01-23  // 出发日期  
  2. leftTicketDTO.from_station=BJP       // 出发站编码  
  3. leftTicketDTO.to_station=SHH         // 到达站编码  
  4. purpose_codes=ADULT                  // 旅客编码:成人为ADULT,学生为:0X00  

 

对应的获取的JSON信息格式如下:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. {"validateMessagesShowId": "_validatorMessage",  
  2.     "status": true,  
  3.     "httpstatus": 200,  
  4.     "data": [  
  5.         {"queryLeftNewDTO": {  
  6.                 "train_no": "240000G14104",          // 列车编号  
  7.                 "station_train_code": "G141",        // 车次  
  8.                 "start_station_telecode": "VNP",     // 始发站编码  
  9.                 "start_station_name": "北京南",      // 始发站名  
  10.                 "end_station_telecode": "AOH",       // 终到站编码  
  11.                 "end_station_name": "上海虹桥",      // 终到站名  
  12.                 "from_station_telecode": "VNP",      // 查询输入经过站编码  
  13.                 "from_station_name": "北京南",       // 查询输入经过站名  
  14.                 "to_station_telecode": "AOH",        // 查询输入到站编码  
  15.                 "to_station_name": "上海虹桥",       // 查询输入到站名  
  16.                 "start_time": "14:16",               // 出发时间  
  17.                 "arrive_time": "19:47",              // 到站时间  
  18.                 "day_difference": "0",               // 花费天数  
  19.                 "train_class_name": "",  
  20.                 "lishi": "05:31",                    // 历时  
  21.                 "canWebBuy": "Y",                    // 是否可以预定  
  22.                 "lishiValue": "331",  
  23.                 "yp_info": "O055300094M0933000999174800017",  
  24.                 "control_train_day": "20301231",  
  25.                 "start_train_date": "20140123",  
  26.                 "seat_feature": "O3M393",  
  27.                 "yp_ex": "O0M090",  
  28.                 "train_seat_feature": "3",  
  29.                 "seat_types": "OM9",  
  30.                 "location_code": "P3",  
  31.                 "from_station_no": "01",  
  32.                 "to_station_no": "09",  
  33.                 "control_day": 19,  
  34.                 "sale_time": "1400",                // 出票时间点hhmm  
  35.                 "is_support_card": "1",  
  36.                 "gg_num": "--",  
  37.                 "gr_num": "--",          // 高级软卧座剩余数  
  38.                 "qt_num": "--",          // 其他座剩余数  
  39.                 "rw_num": "--",          // 软卧座剩余数  
  40.                 "rz_num": "--",          // 软座座剩余数  
  41.                 "tz_num": "--",          // 特等座剩余数  
  42.                 "wz_num": "--",          // 无座座剩余数  
  43.                 "yb_num": "--",  
  44.                 "yw_num": "--",          // 硬卧座剩余数  
  45.                 "yz_num": "--",          // 硬座座剩余数  
  46.                 "ze_num": "有",          // 二等座剩余数  
  47.                 "zy_num": "有",          // 一等座剩余数  
  48.                 "swz_num": "17"          // 商务座剩余数  
  49.             },  
  50.             "secretStr": "预定请求令牌字符串",  
  51.             "buttonTextInfo": "预订或开售日期"  
  52.         },  
  53.         ..........                       // 省略其它车次,信息同上  
  54.     ],  
  55.     "messages": [],  
  56.     "validateMessages": {}  
  57. }  

注意这里的canWebBuy属性,用于标记该趟列车是否可以预订,还有对应列车的secretStr字符,它用于请求预订确认页面的令牌,

 

对于其中一直提到的列车站点编码,可以通过请求https://kyfw.12306.cn/otn/resources/js/framework/station_name.js链接,通过得到JS脚本中的station_names变量获取,对应的站点以@字符分隔,而每一个站点信息如下,这里以北京北为例:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. bjb|北京北|VAP|beijingbei|bjb|0  

用于提取其中有用的信息是:北京北与VAP,使用查询北京北的编码就是VAP,其它站点的解析同理。

 

如上即可以查询指定出发地与到达地的车次预定信息,紧接着进行预订流程的分析。

3,车票预订

在12306的解析中,就属车票预订的解析最为费神,也是最核心的一个流程,我现在只掌握了成人单程票的预订流程,其他的比如返程,学生票等都还没有分析出来,如下讲解的就是关于成人单程票的预定基本流程:

3.1,获取预定确认页面

车票预定首先要请求获取车票的预订确认页面,如下流程图所示:


分析:该流程是在用户单击车次的“预订”按钮时触发的,如图所示,获取预订确认页面,先要判断用户是否登录,POST请求的地址是:https://kyfw.12306.cn/otn/login/checkUser,这个请求无参数,然后通过判断得到的JSON信息中的data.flag属性是否为true判断用户是否已登录,接着再根据对应列车查询时所获得的secretStr字符与用户输入的查询信息POST请求https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest,判断用户是否可以访问预定确认画面,通过得到JSON信息的status属性判断是否允许访问,如果为true说明可以访问,最后依据旅行类型为单程(dc)POST跳转获取单程车票的预订确认画面:https://kyfw.12306.cn/otn/confirmPassenger/initDc。如果登录用户不进行上述判断,直接POST请求https://kyfw.12306.cn/otn/confirmPassenger/initDc提示非法请求,只有成功获取预订确认页面后才能进行下一步的操作。

注:该流程可以查看对应JS脚本:https://kyfw.12306.cn/otn/resources/merged/queryLeftTicket_end_js.js,function L(b4, bX)方法获知。

从请求订单的确认画面还可以得到获取当前登录用户常用联系人的链接地址为:https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs

3.2,预订提交

在车票的预定提交之前必先要获取预定确认画面的原因是因为预订确认HTML中声明的orderRequestDTOticketInfoForPassengerForm两个Javascript变量,含有预订提交的时的必需参数信息,下面就预订提交给出粗略的流程分析图,如下:

注:图片可以右击后查看大图,该流程对应的JS文件地址为:https://kyfw.12306.cn/otn/resources/merged/passengerInfo_js.js

分析:如上图显示了车票预定提交的大体流程,可以依据请求的链接数将其分为四大块:

1.检查用户选择的乘客信息的合法性,POST请求:https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo,通过分析得到的JSON中的data.submitStatus属性是否为true判断,同时这一步的JSON信息中还会包含有一个data.isCheckOrderInfo属性将会作为下一步判断当前用户是否可排队请求的参数。对应请求参数有如下5个:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. cancel_flag: "2",                                         // 固定值  
  2. bed_level_order_num: "000000000000000000000000000000",    // 固定值  
  3. passengerTicketStr: getpassengerTickets(),                // 旅客信息字符串  
  4. oldPassengerStr: getOldPassengers(),                      // 旅客信息字符串  
  5. tour_flag: ticketInfoForPassengerForm.tour_flag,  // 从ticketInfoForPassengerForm中获取  
  6. randCode: $("#randCode").val()                            // 前台输入验证码  

这五个参数中,有两个参数需要注意passengerTicketStroldPassengersStr

passengerTicketStr是以下划线"_"分隔当每一个乘客信息组成的字符串,对应每个乘客信息字符串组成如下:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. 座位编号,0,票类型,乘客名,证件类型,证件号,手机号码,保存常用联系人(Y或N)  

同样oldPassengersStr也是以下划线"_"分隔每个乘客信息组成的字符串,对应每个乘客信息字符串组成如下:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. 乘客名,证件类型,证件号,乘客类型  

在上面的信息中座位编号指的是,一等座、二等座等的编码,从ticketInfoForPassengerForm.limitBuySeatTicketDTO.seat_type_codes属性中选择获取。

 

票类型指的是,成人票,学生票等的编码,可以从ticketInfoForPassengerForm.limitBuySeatTicketDTO.ticket_type_codes属性中选择获取。

证件类型指的是二代身份证,学生证,签证等的编码,可以从ticketInfoForPassengerForm.cardTypes属性中选择获取。

最后oldPassengersStr中的乘客类型主要有如下信息:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. adult: "1",  
  2. child: "2",  
  3. student: "3",  
  4. disability: "4"  

取上面对应的数字编码。

 

注意:在组合oldPassengersStr乘客信息字符串时,未尾会多一个下划线,提交请求是一定要补上,从上也可以看出所有的一些参数都是通过ticketInfoForPassengerForm变量获取的,这也是为什么要事先获取预定确认画面HTML的原因。

 

2.检查乘合信息合法后,接下来就会结合返回的data.isCheckOrderInfo属性,POST请求:https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue,判断当前乘客是否可以排队,对应的参数如下:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. train_date: new Date(orderRequestDTO.train_date.time).toString(),  // 列车日期  
  2. train_no: orderRequestDTO.train_no,                                // 列车号  
  3. stationTrainCode: orderRequestDTO.station_train_code,  
  4. seatType: limit_tickets[0].seat_type,                            // 座位类型  
  5. fromStationTelecode: orderRequestDTO.from_station_telecode,      // 发站编号  
  6. toStationTelecode: orderRequestDTO.to_station_telecode,          // 到站编号  
  7. leftTicket: ticketInfoForPassengerForm.queryLeftTicketRequestDTO.ypInfoDetail,  
  8. purpose_codes: n,         // 默认取ADULT,表成人,学生表示为:0X00  
  9. isCheckOrderInfo: m       // data.isCheckOrderInfo  

 

这里的参数要注意传递列车日期的方式,及座位类型编码,这里选择的是第一个乘客的座位类型编码。最后还要确保orderRequestDTO变量的准确性。

通过返回的JSON信息的data属性值来判断是否允许当前用户进行排队下单,并提示当前的剩余票数。

其中的data属性会包含有两个重要的参数,countTticket,(ticket的格式为:1*****30314*****00001*****00003*****0000的形式):

countT表示的是排队人数,而ticket指的是当前列车对应座位的剩余票数,可以通过https://kyfw.12306.cn/otn/resources/merged/passengerInfo_js.js文件中的function L(l, m) 函数解析获取:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. function L(l, m) {  
  2.             rt = "";  
  3.             seat_1 = -1;  
  4.             seat_2 = -1;  
  5.             i = 0;  
  6.             while (i < l.length) {  
  7.                 s = l.substr(i, 10);  
  8.                 c_seat = s.substr(0, 1);  
  9.                 if (c_seat == m) {  
  10.                     count = s.substr(6, 4);  
  11.                     while (count.length > 1 && count.substr(0, 1) == "0") {  
  12.                         count = count.substr(1, count.length)  
  13.                     }  
  14.                     count = parseInt(count);  
  15.                     if (count < 3000) {  
  16.                         seat_1 = count  
  17.                     } else {  
  18.                         seat_2 = (count - 3000)  
  19.                     }  
  20.                 }  
  21.                 i = i + 10  
  22.             }  
  23.             if (seat_1 > -1) {  
  24.                 rt += seat_1  
  25.             }  
  26.             if (seat_2 > -1) {  
  27.                 rt += "," + seat_2  
  28.             }  
  29.             return rt  
  30.         }  

函数中的l指的就是ticket,而m指的是第一位乘客所选择的座位编号。

 

如果计算的余票信息还有剩余,则会提示用户点击确认按进行订单的提交请求,如果没有充实的票,则会提示用户选择其它车次,处理该请求的方法详情见https://kyfw.12306.cn/otn/resources/merged/passengerInfo_js.js文件中的function M(n, m) 方法。

3.当提示的有充足的余票,且用户点击了确定按钮,则接下来会POST请求:https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue,进行单程票(dc)类型的排队下单操作,通过判断返回的JSON信息data.submitStatus属性判断订单是否以成功提交至服务器,对应的请求参数为:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. passengerTicketStr: getpassengerTickets(),  
  2. oldPassengerStr: getOldPassengers(),  
  3. randCode: $("#randCode").val(),  
  4. purpose_codes: ticketInfoForPassengerForm.purpose_codes,  
  5. key_check_isChange: ticketInfoForPassengerForm.key_check_isChange,  
  6. leftTicketStr: ticketInfoForPassengerForm.leftTicketStr,  
  7. train_location: ticketInfoForPassengerForm.train_location  

这里的参数没有新意,主要是注意获取ticketInfoForPassengerForm变量的准确性。

 

 

4.订单提交至服务器后不一定说明订单已经成功了,还需要GET请求:https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime,判断系统是否已根据提交的订单信息为相应的乘客占位成功,并提示预估出票等待时间,这一步只有一个参数,就是旅行类型,由于我们主要考虑的是单程票,故提交时POST dc就行了,如下:

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片

 
  1. tourFlag: "dc"  

这一步占位的操作在12306的官网中是将其封装在了一个名为OrderQueueWaitTime的对象中,可以解压https://kyfw.12306.cn/otn/resources/merged/passengerInfo_js.js文件获知,对应的如果判断系统占位成功,将会从返的JSON信息中获取data.orderId属性,即为下单成功时的订单号。

 

如上4次请求就可以准确的模拟出12306官网订单提交的整套流程,其中其实还忽略了验证码的获取与判断操作,而这一步仅仅是判断验证码的合法性,与主体流程无关。对应订单确定页面的验证码获取链接为:https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=randp,从中与登录页面的验证码链接对比,可知新版12306的验证码管理统一为了一个方法,登录与订单确认的验证码链接只是传递的module和rand参数不一样而已。

4,结束语:

根据上面的操作,基本可以全程模拟官网的订单操作,编写出一个属于自己的抢票助手。在写这篇文章时,我一直在想这样做是否有意义,因为12306随时都有可能变更,由于23:00点~07:00点的维护时间段的设置,也许今天写出来的东西明天马上就会失效过期。但仔细考虑后还是打算将他分享出来,就当是一种学习吧。同时在这里公布GitHub上使用Python3编写的一个订票项目源码:https://github.com/lzqwebsoft/trainticket,对应window下独立运行exe文件下载地址为:https://code.google.com/p/lzqwebsoft-projects/source/browse/#svn%2Ftrunk,软件运行效果如下:

261月/140

买笔记本的8个小技巧 最适合自己才最好

发布在 邵珠庆

显然,智能手机和平板在一定程度上可以替代传统电脑,让我们可以随时随地上网、使用各种应用。不过,传统电脑也拥有它的不可替代性,比如移动办公、视频编辑、玩游戏,笔记本电脑可能是个更好的选择。

作为一种成熟的电脑类型,笔记本电脑的定位、种类已经十分丰富,尺寸、性能、大小也都各不相同,还拥有几种主流的系统,该如何选择呢?下面我们就来分析分析。

你需要用笔记本电脑做什么?

多用途:如果你想要电脑能够胜任一切基本应用,比如处理文档、看看视频、玩玩休闲游戏,低端的15英寸笔记本会是一个好选择。如果再需要一点移动性,那就选择13英寸的入门机型。这种类型的笔记本,售价一般在3000至5000元之间,选择还是很丰富的。

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

商务:不论是商务人士还是学生,如果主要使用笔记本处理文档、表格,那么要注意选择轻薄、键盘手感好以及耐用的笔记本,屏幕效果也很重要。每个品牌都有商务笔记本类型,大家可以参考一下。

游戏:如果要使用笔记本电脑玩高端PC游戏,那么首选拥有高端酷睿i7处理器、高端独立显卡(甚至双显卡)以及高分辨率屏幕的Windows机型。此类产品价格普遍较高,所以至少要准备7000元及以上的预算。

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

创意工作:如果想要使用笔记本编辑视频、照片,需要一个强大的处理器、独立显卡、固态硬盘和大尺寸的高分辨率屏幕,比如苹果的Macbook Pro Retina等机型。

上网冲浪/电子邮件:如果你要给孩子购买笔记本、或是将它作为辅助电脑使用,可以选择类似Chromebook这样的低成本产品。

选择合适的尺寸

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

搞清楚要用笔记本电脑做什么之后,就可以选择尺寸了。通常来说,11至12英寸的产品最轻薄,还拥有一些采用了灵活设计的平板/电脑二合一产品,不过它们的屏幕和键盘设计稍微局促一些。

13到14英寸的笔记本电脑是最为易用和平衡的选择,大部分商务、学生本都使用了这个尺寸,所以拥有非常多的选择,可以轻松放在膝盖上使用。

15英寸也是极为流行的尺寸,通常以低成本产品为主,但重量偏重,不太适合外出携带。另外,15英寸笔记本通常也配备了光驱。

17至18英寸的笔记本电脑基本上以影音、游戏应用为主,性能达到了工作站级别,机身沉重性能强大,可以作为台式机的替代品使用。

检查键盘和触摸板

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

显然,我们选择笔记本电脑,是希望获得良好的输入感受,所以键盘和触摸板是非常重要的。如果你去笔记本电脑卖场进行试用,一定要亲自在键盘上打打字、并测试一下触摸板的滑动感和左右键按压感,以及多点触摸缩放功能,看看手感是否出色。一般情况下,苹果Macbook的键盘和触摸板是业界最为出色的,大家可以以此为标准进行测试。

了解硬件规格

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

笔记本电脑是一种硬件,所以硬件配置是非常重要的,能够决定你的电脑可以做什么。以下是你需要注意的硬件配置。

核心组件:处理器、内存和显卡基本上是计算机的核心组件,所以首先需要考虑这些部分。处理器方面以英特尔酷睿为主,目前最新的Haswell拥有最好的性能及电池寿命。除非你购买一个低端产品,否则不要考虑酷睿i3和AMD A系列的处理器。

至于内存,目前普遍达到了4GB标准,但随着64位系统的普及,内存自然是越大越好,8GB-16GB能够让你获得更好的多任务性能。至于显卡,如果需要运行大型3D游戏,需要配备AMD及Nvidia的独立显卡;如果只是办公,一般的集成显卡完全够用,并且更省电。

硬盘及闪存缓存:硬盘的种类,也是决定笔记本性能的重要部分。目前,我们更推荐固态硬盘选项,虽然容量偏低一些,但是寿命、速度都极具优势;如果预算不够,那么尽量选择7200转的大容量机械硬盘。如果选择“超极本”,通常它们会配备闪存缓存作为硬盘辅助,提供系统性能,尽量选择更大容量的缓存吧。

屏幕及分辨率:大多数的低价笔记本采用1366*768像素的屏幕,如果有可能尽量选择1920*1080像素甚至更高的分辨率。另外,如果选择了Windows 8笔记本,最好选择具备触摸屏的款式,毕竟这是一款针对触摸屏设计的系统。

光驱:目前配备光驱的笔记本已经越来越少了,但是如果你需要刻录或是观看蓝光碟片,就需要考虑一下具备光驱的机型了。

混合设计还是传统笔记本?

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

自Windows 8推出以来,我们看到越来越多拥有混合设计的PC产品出现,比如多模式笔记本、平板/笔记本二合一产品,比如联想Yoga系列、Suface Pro等等。但大多数情况下,它们都无法实现两全的体验。多模式电脑虽然能够变为平板模式,但是由于键盘不能拆卸,显然不如iPad好用;而平板/笔记本二合一产品的外接键盘,又比较狭窄、手感较差,所以还是要根据自己最经常的使用形式来选择。

电池寿命非常重要

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

如果要选择13、14英寸的便携式笔记本,那么电池寿命是非常重要的。我们建议用户选择Haswell平台、可拆卸电池设计的产品,如ThinkPad X240,最长可实现20小时的续航,十分惊人。虽然苹果Macbook系列的续航能力不低,但是电池不能拆卸,所以也需要考虑一下。

品牌的选择

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

品牌对于任何商品来说都非常重要,笔记本电脑也不例外。我们可以参考一些媒体的屏蔽数据,比如Laptop,在他们的“2013年笔记本品牌技术排行”中,索尼、苹果和三星位列前三;而在“2013年最佳笔记本品牌”中,苹果、联想和华硕位列前三。这些数据可以作为一些参考。

选择最适合自己的操作系统

 

转播到腾讯微博

新年买笔记本的8个小技巧 最适合自己才最好

 

目前市场中的笔记本电脑以Windows为主,另外还拥有苹果Mac和谷歌Chromebook,大多数用户都会选择Windows产品,目前包括少量Windows 7及大量Windows 8笔记本。其中,Windows 8/8.1笔记本通常搭载触摸屏、产品种类也最为丰富,可以应用在办公、娱乐、商务等多种领域。

苹果Macbook的总销量在2013年实际上是呈下降趋势的,但是最新的Mavericks OS和Haswell版Macbook Air、Pro也具备非常好的使用体验。相比Windows,它们更适合上网、屏幕设计以及视频编辑等应用,价格则偏高,预算需要至少7000元。

最后的谷歌Chromebook,有些类似早年的上网本,配置通常较低,但是由于Chrome OS基于互联网的特性,所以使用起来还是很流畅的。当然,它的功能稍有局限,比如不支持本地打印、Office应用、本地应用有限等等,但是价格通常极为低廉,1500元左右就可以买到。

161月/140

2014年互联网IT公司产品、技术类人员工资待遇大全

发布在 邵珠庆

以下均为应届毕业生的起薪待遇

一、民企

1. 百度 13k*14.6,special 14~17k*14.6

开发类 13K*14.6 (2014)

测试类、前端类 12K*14.6 (2014)

2. 腾讯 11.5k*16,special 12~14k*16

技术类研究生 11.5K*16 (2014)

终端开发本科生 10K*16 (2014)

3. 阿里 13k*15~15k*15,有17k*15,18k*15,有传说中的60w。

技术类 13K or 15K*15 (2014)

阿里星 25K*15+若干股票 总和接近60W

4. 360 13k*14~16k*14

5. 美团 13k*15~16k*15,也有更高的。

6. 去哪儿 11k*16~15k*16

7. 人人 技术类 (12K-14K)*14 (2014)

8. 58同城 20w+

9. 网易游戏 非清北17k~18k*16 测试 13k~14k*16 策划 16k*16

10. 网易互联网 11k~13k*16

11. 搜狐 13k~14k*14,更低的不知道

12. 新浪 微博 11k*16 (2014)

技术类 12.5K*14 (2014)

测试类开发 11.5K*14 (2014)

产品类 8.5K*14 (2014)

13. 创新工场 【不含涂鸦、豌豆荚】10k*13

14. 完美世界 最低12k*12+其他 【出乎意料地低】

15. 巨人 9k【低】

16. 大众点评 13k*14~16,有special

17. 豌豆荚 20w+

18. 携程旅行 11k*15

19. 盛大游戏 10k~12k*15

20. 爱奇艺 招人不多,一般13k*14

21. 豆瓣 21w+。

22. 凤凰网 视频开发10K*14 (2014)

23. 4399 技术类10K*14 (2014) 据说年终奖很多。

24. 赶集网 技术类 本科12K*15.2 (2014)

25. 酷派 技术类10K*14

26. 杭州同花顺 技术类6K*15

27. 锐捷 技术类11K*13

28. 兰亭集势 产品类 10K*12

29. 携程 技术类 6~9k*16

30. 小米 技术类硕士 13k*(13-15),special多1k

31. 陌陌 技术类11k×14+ 一些福利

32. 风云直播 研发22w

33. 北京风行网络 技术类 9000×15

34. 京东商城 技术类 9K*13

35. uc浏览器 技术类 8k×14+每天四顿大概12w

36. 科大讯飞 技术类 合肥硕士7.5k×13 上海杭州北京8500×13+1400住房补贴

37. 广州捷游 技术类 6.9*13 包三餐

38. 苏宁云商 技术类 南京/7000+400(房补)+200(饭补)

39. 福州锐捷网络 技术类硕士10k*13

40. 金山网络 C++开发(研究生)10~11K*14

测试(研究生) 8k*14

41. 创新工厂 技术类 10k×13大概13w

42. 盛大创新研究院 基础研发26W

43. 完美时空 技术类18W+

其他小互联网不列了,一般很少给得起【18w+】。

二、外企

1. 微软 【实习留用,校招少】,15~16k*12+奖金若干万。

2. IBM 【实习留用,校招极少】,11.5/12/12.5k*14,签约奖5w分两年给

3. 微策略 分档次,20~25w都可能。

4. 英特尔 【招人很少】 一般11k*13,其他不知

5. 思科 【缩招】18~20w

6. EMC 【招人很少】12k*14~15+其他约等于18w+

7. vmware 上海测试、研发校招名额不超过5人,18~20w

8. ebay 【缩招,往年校招已结束,今年刚开始,无网申,邮箱投递】,19w

9. EA【今年不招,去年26w+奖金】

10. 英伟达 18w+

11. ARM 【无校招】11k*13+其他

12. oracle 【招聘进行中】

13. Freewheel 16k*14

14. AMD、Autodesk,基本无校招,待遇不超过15w

15. 摩根IT base 由18涨至21

16. SAP 今年基本无校招 去年 ~11k*13+福利

17. 趋势 技术 12W一年

18. TP-Link

技术类本科 7.2K*16 3年拿分红

技术类研究生 10K*16 2年拿分红

其他外企不列,很多外企今年缩招甚至不招人

三、超级招人大户

1. 华为 10~13k*14,大多数是10*14.

2. 中兴 周围投得同学太少,请补充

四、金融机构

1. 招行信用卡 14w

2. 建行软开

3. 农行总行软开 6.5K*16个月+1500×12

4. 交行软开

5. 工行软开 待遇四大行中基本垫底

6. 中行软开 待遇四大行中基本垫底

7. 中国证券登记结算中心 20w+

8. 平安科技IT 技术15w,咨询18w+

9. 上海清算所,18w+

10. 银联商务 约17w

11. 银联数据 6000*18+高公积金+高福利,约等于15~16w

12. 南方基金IT IT部门 25W?

141月/140

Git tag简介

发布在 邵珠庆

一、轻量级标签
我们可以用 git tag不带任何参数创建一个标签(tag)指定某个提交(commit):
$ git tag stable-1 1b2e1d63ff
这样,我们可以用stable-1 作为提交(commit) "1b2e1d63ff" 的代称(refer)。
前面这样创建的是一个“轻量级标签",这种分支通常是从来不移动的。
如果你想为一个标签(tag)添加注释,或是为它添加一个签名(sign it cryptographically), 那么我们就需要创建一个 ”标签对象".
二、标签对象
如果有 "-a", "-s" 或是 "-u " 中间的一个命令参数被指定,那么就会创建 一个标签对象,并且需要一个标签消息(tag message). 如果没有"-m " 或是 "-F " 这些参数,那么就会启动一个编辑器来让用户输入标签消息(tag message).
当这样的一条命令执行后,一个新的对象被添加到Git对象库中,并且标签引用就指向了 一个标签对象,而不是指向一个提交(commit). 这样做的好处就是:你可以为一个标签 打处签名(sign), 方便你以后来查验这是不是一个正确的提交(commit).
下面是一个创建标签对象的例子:
$ git tag -a stable-1 1b2e1d63ff
标签对象可以指向任何对象,但是在通常情况下是一个提交(commit). (在Linux内核代 码中,第一个标签对象是指向一个树对象(tree),而不是指向一个提交(commit)).
三、签名的标签
如果你配有GPG key,那么你就很容易创建签名的标签.首先你要在你的 .git/config 或 ~.gitconfig里配好key.
下面是示例:
[user]
signingkey =
你也可以用命令行来配置:
$ git config (--global) user.signingkey
现在你可以直接用"-s" 参数来创“签名的标签”。
$ git tag -s stable-1 1b2e1d63ff
如果没有在配置文件中配GPG key,你可以用"-u“ 参数直接指定。
$ git tag -u stable-1 1b2e1d63ff
四、语法详解
语法
git tag [-a | -s | -u ] [-f] [-m | -F ] [ |

Unless -f is given, the tag to be created must not yet exist in the .git/refs/tags/ directory.

If one of -a, -s, or -u is passed, the command creates a tag object, and requires a tag message. Unless -m or -F is given, an editor is started for the user to type in the tag message.

If -m or -F is given and -a, -s, and -u are absent, -a is implied.

Otherwise just a tag reference for the SHA1 object name of the commit object is created (i.e. a lightweight tag).

A GnuPG signed tag object will be created when -s or -u is used. When -u is not used, the committer identity for the current user is used to find the GnuPG key for signing. The configuration variable gpg.program is used to specify custom GnuPG binary.

OPTIONS
-a
--annotate
Make an unsigned, annotated tag object

-s
--sign
Make a GPG-signed tag, using the default e-mail address’s key.

-u
--local-user=
Make a GPG-signed tag, using the given key.

-f
--force
Replace an existing tag with the given name (instead of failing)

-d
--delete
Delete existing tags with the given names.

-v
--verify
Verify the gpg signature of the given tag names.

-n
specifies how many lines from the annotation, if any, are printed when using -l. The default is not to print any annotation lines. If no number is given to -n, only the first line is printed. If the tag is not annotated, the commit message is displayed instead.

-l --list List tags with names that match the given pattern (or all if no pattern is given). Running "git tag" without arguments also lists all tags. The pattern is a shell wildcard (i.e., matched using fnmatch(3)). Multiple patterns may be given; if any of them matches, the tag is shown.

--contains
Only list tags which contain the specified commit.

--points-at

Only list tags of the given object.

-m
--message=
Use the given tag message (instead of prompting). If multiple -m options are given, their values are concatenated as separate paragraphs. Implies -a if none of -a, -s, or -u is given.

-F
--file=
Take the tag message from the given file. Use - to read the message from the standard input. Implies -a if none of -a, -s, or -u is given.

--cleanup=
This option sets how the tag message is cleaned up. The can be one of verbatim, whitespace and strip. The strip mode is default. Theverbatim mode does not change message at all, whitespace removes just leading/trailing whitespace lines and strip removes both whitespace and commentary.


The name of the tag to create, delete, or describe. The new tag name must pass all checks defined by git-check-ref-format(1). Some of these checks may restrict the characters allowed in a tag name.

CONFIGURATION
By default, git tag in sign-with-default mode (-s) will use your committer identity (of the form "Your Name ") to find a key. If you want to use a different default key, you can specify it in the repository configuration as follows:

[user]
signingkey =
DISCUSSION
On Re-tagging
What should you do when you tag a wrong commit and you would want to re-tag?

If you never pushed anything out, just re-tag it. Use "-f" to replace the old one. And you’re done.

But if you have pushed things out (or others could just read your repository directly), then others will have already seen the old tag. In that case you can do one of two things:

The sane thing. Just admit you screwed up, and use a different name. Others have already seen one tag-name, and if you keep the same name, you may be in the situation that two people both have "version X", but they actually have different "X"'s. So just call it "X.1" and be done with it.

The insane thing. You really want to call the new version "X" too, even though others have already seen the old one. So just use git tag -fagain, as if you hadn’t already published the old one.

However, Git does not (and it should not) change tags behind users back. So if somebody already got the old tag, doing a git pull on your tree shouldn’t just make them overwrite the old one.

If somebody got a release tag from you, you cannot just change the tag for them by updating your own one. This is a big security issue, in that people MUST be able to trust their tag-names. If you really want to do the insane thing, you need to just fess up to it, and tell people that you messed up. You can do that by making a very public announcement saying:

Ok, I messed up, and I pushed out an earlier version tagged as X. I

then fixed something, and retagged the *fixed* tree as X again.

If you got the wrong tag, and want the new one, please delete

the old one and fetch the new one by doing:

git tag -d X

git fetch origin tag X

to get my updated tag.

You can test which tag you have by doing

git rev-parse X

which should return 0123456789abcdef.. if you have the new version.

Sorry for the inconvenience.

Does this seem a bit complicated? It should be. There is no way that it would be correct to just "fix" it automatically. People need to know that their tags might have been changed.

On Automatic following
If you are following somebody else’s tree, you are most likely using remote-tracking branches (refs/heads/origin in traditional layout, orrefs/remotes/origin/master in the separate-remote layout). You usually want the tags from the other end.

On the other hand, if you are fetching because you would want a one-shot merge from somebody else, you typically do not want to get tags from there. This happens more often for people near the toplevel but not limited to them. Mere mortals when pulling from each other do not necessarily want to automatically get private anchor point tags from the other person.

Often, "please pull" messages on the mailing list just provide two pieces of information: a repo URL and a branch name; this is designed to be easily cut&pasted at the end of a git fetch command line:

Linus, please pull from

git://git..../proj.git master

to get the following updates...

becomes:

$ git pull git://git..../proj.git master
In such a case, you do not want to automatically follow the other person’s tags.

One important aspect of git is its distributed nature, which largely means there is no inherent "upstream" or "downstream" in the system. On the face of it, the above example might seem to indicate that the tag namespace is owned by the upper echelon of people and that tags only flow downwards, but that is not the case. It only shows that the usage pattern determines who are interested in whose tags.

A one-shot pull is a sign that a commit history is now crossing the boundary between one circle of people (e.g. "people who are primarily interested in the networking part of the kernel") who may have their own set of tags (e.g. "this is the third release candidate from the networking group to be proposed for general consumption with 2.6.21 release") to another circle of people (e.g. "people who integrate various subsystem improvements"). The latter are usually not interested in the detailed tags used internally in the former group (that is what "internal" means). That is why it is desirable not to follow tags automatically in this case.

It may well be that among networking people, they may want to exchange the tags internal to their group, but in that workflow they are most likely tracking each other’s progress by having remote-tracking branches. Again, the heuristic to automatically follow such tags is a good thing.

On Backdating Tags
If you have imported some changes from another VCS and would like to add tags for major releases of your work, it is useful to be able to specify the date to embed inside of the tag object; such data in the tag object affects, for example, the ordering of tags in the gitweb interface.

To set the date used in future tag objects, set the environment variable GIT_COMMITTER_DATE (see the later discussion of possible values; the most common form is "YYYY-MM-DD HH:MM").

For example:

$ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
DATE FORMATS
The GIT_AUTHOR_DATE, GIT_COMMITTER_DATE environment variables support the following date formats:

Git internal format
It is , where is the number of seconds since the UNIX epoch. is a positive or negative offset from UTC. For example CET (which is 2 hours ahead UTC) is +0200.

RFC 2822
The standard email format as described by RFC 2822, for example Thu, 07 Apr 2005 22:13:13 +0200.

ISO 8601
Time and date specified by the ISO 8601 standard, for example 2005-04-07T22:13:13. The parser accepts a space instead of the T character as well.

Note
In addition, the date part is accepted in the following formats: YYYY.MM.DD, MM/DD/YYYY and DD.MM.YYYY.
结束

http://blog.csdn.net/hudashi/article/details/7664468

21月/140

从腾讯新晋大员看其管理与政治脉络

发布在 邵珠庆

腾讯地图最新的发布会,出来宣讲的是新晋副总裁马喆人,却不是那个执掌无线业务事业群(MIG)的老资格副总裁丁珂。有人就问这是为什么?就此邀请“深喉”统一答疑。不担保精准,您就当八卦看。

目前无线事业群有两个管业务的副总裁,丁珂和马喆人,同时向COO任宇昕汇报。马喆人管的是腾讯地图,剩下的虽然貌似丁珂都能管,但确乎其重点只在安全和手机助手。浏览器有总经理钟翔平管,渠道是另一总经理谢平章管,还有一个管市场的乐露萍,再加上另一个管人事的副总裁唐毅斌,以“六人会议汇报”的形式,每双周向任宇昕汇报决策。

所以丁珂势力范围在安全和手机助手。还有谣传,手机助手这一块可能被划给社交网络事业群(SNG),因这个业务所在的移动应用平台部由林松涛兼任总经理,而林一直是社交网络事业群的开放平台部总经理。再考虑到社交网络事业群也是向任宇昕汇报,想来转移的难度不大,左手倒右手。

那丁珂手里剩下的就是安全了。对面是360,且非腾讯擅长,这不是好干的活儿。知道你也可能自然会联想到一个词,鸡肋。

下面说看点。

往好了说,腾讯这家公司越发的鼓励内部良性竞争。

第一,老大走人,下一级不一定能顺位接班。无线事业群的老大,执行副总裁刘成敏离职,按理说一直作为这个事业群唯一副总裁的丁珂可顺位接班。你看人家社交网络事业群就是这样干的,汤道生接了吴宵光的班。可丁珂没接成班。说明国企那一套不是腾讯的玩法。你要足够硬才行。无线事业群业绩不好,副总裁没资格接班。

第二,做得不够好,别说接班,连本来的地盘都保不住。所以来了个马喆人,在腾讯资历比丁珂浅不少的一个新晋的副总裁,把腾讯地图这一块最具前景的地盘接管了。可能你也一样觉得,地图比安全有前景、有诱惑。有人压力肯定小不。

第三,想上位,先竞争。执行副总裁这个位子就这么空着,两个副总裁各管一摊,再加上负责浏览器、渠道、市场的几个总经理也是向COO汇报。几架马车同时跑,看谁彪得猛,跑得快。赛马中相马。

好的说完了,现在往八卦的方向说。大公司嘛,总免不了政治。政治的核心问题是一句话,“你是谁的人”。政治这玩意是避不开的,有人就会有政治。

既然避不开,就尽量扬长避短。对大公司而言,政治虽有极恶劣的影响,却也在客观效果上有利于制衡。两派相争,如民主党和共和党,总架构能更稳定,防止只手翻天,一派独大。因此在现实里也被各层级的老大们广泛容忍,以及利用。比如,总裁在副总裁下面的总经理这一级安插心腹和盯梢,那副总裁就顺理在总经理下面的总监这一级安插心腹和盯梢。层层嵌套,既合作又竞争。大家都有安全感。

大公司人事的一举一动,都最好从两条线来看。一是业务,一是政治。抛开任何一条线,都难窥全貌。

第一,马喆人上位,在帅位空缺的无线事业群安营扎寨。之前没具体做产品的经历,却一上来就接手最有前景的业务,大风光。马喆人何许人也?从咨询公司空降腾讯做战略发展部总经理,隶属于企业发展事业群(CDG),一度向总裁刘炽平直接汇报。被坊间称为“大内总管”。

大内总管外放任四川总督,史书里常有的段子。四川总督能否过渡为封疆大吏,节制西南三省党政军权,得看各人实力和造化。不过机会是给到位了,要放出去的信号是明确了。

第二,丁珂在刘成敏离职后处于走下坡路的势头。据说早年丁珂跟刘成敏关系很好,但最近一两年因种种不可说的缘由,急剧恶化。跟老大闹翻,在公司政治的个人信用史上是个污点。其它老大会怎么看?你对他就这样搞,将来对我也难免如此。

若你没老大罩了,那下面人会怎么干?良禽择木而栖,既然要抱大腿,肯定力所能及抱个最结实、最上流的。若换了你,也该不会去抱丁珂,怎么着也得抱任宇昕或者刘炽平吧。

第三,总裁刘炽平勤劳、务实,求贤若渴,人脉渐丰。举几个例。空降的首席战略官、总裁办成员james mitchell接管企业发展事业群。这老外跟刘炽平同样是资本界起家,同出于高盛。接替吴宵光执掌社交网络事业群帅印、总裁办成员汤道生,是刘炽平的同学。执掌财付通的赖智明也是刘炽平的同学。点到为止,其它就不多说。

第四,无线事业群以后怎么搞?是个有趣问题。按各事业群的惯例架构,该有个执行副总裁统管,向任宇昕汇报。不过现在显然没合适人选。丁珂不OK,历史问题难获得诸位老大的信任,况且江湖上还谣传任宇昕跟离任的刘成敏私交很好,这一关丁珂难过。马喆人不OK,管产品、管业务的资历还挺浅,况且是刘炽平的大内总管,任宇昕也得做好制衡的本份工作。其它几个总经理,短期内也难当此大任。

这一摊子很考验任宇昕。当然,这个事业群也许根本就不需要一个执行副总裁。无线的大局和命脉都在微信那儿呢。将来微信和无线事业群可能会有整合。这是未来一两年,腾讯最好看也最重要的一幕剧情。

若是整合,两个核心问题:张小龙向谁汇报?谁做副手制衡张小龙?能回答这个问题的,除了当事人张小龙,最关键是看三个人的默契以及博弈状况:马化腾,刘炽平,任宇昕。

大公司的故事真的蛮有趣。于无声处听惊雷。

本自媒体帐号:孕峰。

科技不断提高效率同时也引发焦虑,物极必反,我们开始从传统文化里寻找平衡,观照人本身。

越科技,越人文。