-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
1566 lines (1547 loc) · 211 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[【附源码】小程序初窥之简单查单词]]></title>
<url>http://pengtianhao.com/2018/02/17/%E3%80%90%E9%99%84%E6%BA%90%E7%A0%81%E3%80%91%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%88%9D%E7%AA%A5%E4%B9%8B%E7%AE%80%E5%8D%95%E6%9F%A5%E5%8D%95%E8%AF%8D/</url>
<content type="html"><![CDATA[<blockquote>
<p>新年假期百无聊赖,于是就看了一下微信小程序的开发方法,花了两天时间入了个门,这里记录一下。</p>
</blockquote>
<p>阅读之前,先确定你知道基本的<code>html+css+js</code>语法,这样就能更好地和我一样,以一个新手的视角来理解小程序。</p>
<h2 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h2><p>目标是编写一个查单词的小程序,很明显需要一个输入框(如果有一个placehoder就更好了),然后再加上一个按钮,点击之后如果成功就显示结果,如果失败就提示失败。查词api使用<a href="https://www.shanbay.com/help/developer/api_v1/" target="_blank" rel="external">扇贝api</a>。</p>
<p>所以最后为了简单起见,界面的最终形态就是这样:</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/2/17/161a2fdc552a5ffe?w=640&h=1136&f=jpeg&s=147261" alt=""></p>
<h2 id="文件结构"><a href="#文件结构" class="headerlink" title="文件结构"></a>文件结构</h2><p>小程序里的最主要的文件有四种:<code>.js</code> <code>.json</code> <code>.wxml</code> <code>.wxss</code></p>
<p>简单理解就是:</p>
<ul>
<li><code>.js</code> 用于控制页面逻辑。</li>
<li><code>.json</code> 用于页面配置,不必须,也可以全局配置,但是页面配置权重高于全局配置。</li>
<li><code>.wxml</code> 类似于<code>.html</code>,用于设置页面内容</li>
<li><code>.wxss</code> 类似于<code>.css</code>,用于设置页面样式,不必须,代码也可以直接写在<code>.wxml</code>内</li>
</ul>
<p>由此,这个迷你项目的项目结构就如下图所示:</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/2/17/161a309b31455970?w=590&h=430&f=jpeg&s=29690" alt=""></p>
<p>唯一的页面是<code>index</code>,<code>app.js</code> <code>app.json</code> <code>project.config.json</code> 应用于全局。</p>
<h2 id="开始编码"><a href="#开始编码" class="headerlink" title="开始编码"></a>开始编码</h2><h3 id="基本设置"><a href="#基本设置" class="headerlink" title="基本设置"></a>基本设置</h3><p>首先,我们要告诉小程序有哪些页面。<br>其次,需要设置小程序的导航栏标题,因为只有一页,所以只需要设置这一页的内容就可以了,所以统一写在<code>app.json</code>里,当然,你也可以新建一个<code>index.json</code>。</p>
<figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// app.json</span></div><div class="line"></div><div class="line">{</div><div class="line"> <span class="string">"pages"</span>: [</div><div class="line"> <span class="string">"src/pages/index/index"</span></div><div class="line"> ],</div><div class="line"> <span class="string">"window"</span>: {</div><div class="line"> <span class="string">"navigationBarBackgroundColor"</span>: <span class="string">"#ffffff"</span>,</div><div class="line"> <span class="string">"navigationBarTextStyle"</span>: <span class="string">"black"</span>,</div><div class="line"> <span class="string">"navigationBarTitleText"</span>: <span class="string">"简单查单词"</span>,</div><div class="line"> <span class="string">"backgroundColor"</span>: <span class="string">"#eeeeee"</span>,</div><div class="line"> <span class="string">"backgroundTextStyle"</span>: <span class="string">"light"</span>,</div><div class="line"> <span class="string">"enablePullDownRefresh"</span>: <span class="literal">false</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="页面内容"><a href="#页面内容" class="headerlink" title="页面内容"></a>页面内容</h3><ol>
<li>首先我们需要一个输入框,从<a href="https://mp.weixin.qq.com/debug/wxadoc/dev/component/input.html" target="_blank" rel="external">微信文档</a>可以看到,我们需要一个<code>input</code>控件。通过<code>placeholder</code>属性添加占位符,然后通过<code>bindinput</code>与输入事件绑定,每当有输入事件的时候,就调用<code>wordInput</code>函数。这里的<code>focus</code> 与 <code>confirm-type</code> 含义你可以<a href="https://mp.weixin.qq.com/debug/wxadoc/dev/component/input.html" target="_blank" rel="external">查看文档</a>来了解。</li>
<li>第二步我们需要一个按钮,点击调用处理函数。所以这里我们用<code>bindtap</code>绑定一个<code>btnClick</code>函数。</li>
<li>最后我们需要显示翻译和例句,使用<code>{{}}</code>来绑定变量,此处的控件为<code>text</code>,它的具体的使用可以<a href="https://mp.weixin.qq.com/debug/wxadoc/dev/component/text.html" target="_blank" rel="external">看这里</a></li>
</ol>
<p>由此,我们得到了如下代码</p>
<figure class="highlight django"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="xml"><span class="comment"><!-- index.wxml --></span></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">view</span> <span class="attr">class</span>=<span class="string">"section"</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">placeholder</span>=<span class="string">"请输入英文单词"</span> <span class="attr">bindinput</span>=<span class="string">'wordInput'</span> <span class="attr">focus</span>=<span class="string">"true"</span> <span class="attr">confirm-type</span>=<span class="string">"done"</span>/></span></div><div class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">type</span>=<span class="string">'primary'</span> <span class="attr">bindtap</span>=<span class="string">'btnClick'</span>></span>查询<span class="tag"></<span class="name">button</span>></span></div><div class="line"><span class="tag"></<span class="name">view</span>></span></div><div class="line"><span class="tag"><<span class="name">view</span> <span class="attr">class</span>=<span class="string">"textView"</span>></span></div><div class="line"> <span class="tag"><<span class="name">text</span>></span><span class="template-variable">{{text}}</span><span class="xml"><span class="tag"></<span class="name">text</span>></span></span></div><div class="line"><span class="tag"></<span class="name">view</span>></span></div><div class="line"><span class="tag"><<span class="name">view</span> <span class="attr">class</span>=<span class="string">"senView"</span>></span></div><div class="line"> <span class="tag"><<span class="name">text</span> ></span><span class="template-variable">{{sentext}}</span><span class="xml"><span class="tag"></<span class="name">code</span>></span><span class="tag"></<span class="name">text</span>></span></span></div><div class="line"><span class="tag"></<span class="name">view</span>></span></div></pre></td></tr></table></figure>
<h3 id="页面样式"><a href="#页面样式" class="headerlink" title="页面样式"></a>页面样式</h3><p>这个不多说了,大家都能看懂</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/* index.wxss */</span></div><div class="line"></div><div class="line"><span class="selector-tag">input</span>{</div><div class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid grey;</div><div class="line"> <span class="attribute">margin</span>: <span class="number">5%</span>;</div><div class="line"> <span class="attribute">padding</span>: <span class="number">5px</span>;</div><div class="line"> <span class="attribute">border-radius</span>:<span class="number">3px</span>;</div><div class="line">}</div><div class="line"><span class="selector-tag">button</span>{</div><div class="line"> <span class="comment">/* width: 80%; */</span></div><div class="line"> <span class="attribute">margin</span>: <span class="number">5%</span>;</div><div class="line">}</div><div class="line"><span class="selector-class">.textView</span>{</div><div class="line"> <span class="attribute">margin</span>: <span class="number">5%</span>;</div><div class="line">}<span class="selector-class">.senView</span>{</div><div class="line"> <span class="attribute">margin</span>: <span class="number">5%</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="编写函数"><a href="#编写函数" class="headerlink" title="编写函数"></a>编写函数</h3><h4 id="获取输入框内容"><a href="#获取输入框内容" class="headerlink" title="获取输入框内容"></a>获取输入框内容</h4><p>之前我们已经为<code>bindinput</code>这个输入事件绑定了<code>wordInput</code>函数,现在就来实现它。这个函数的目标是捕捉输入的内容,并保存下来。</p>
<p>在<code>index.js</code>里,我们写下第一个函数,先注册一个变量,然后通过函数赋值。</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// index.js</span></div><div class="line"></div><div class="line"><span class="selector-tag">Page</span>({</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 页面的初始数据</div><div class="line"> */</div><div class="line"> <span class="attribute">data</span>: {</div><div class="line"> <span class="attribute">text</span>:<span class="string">""</span>,</div><div class="line"> <span class="attribute">sentext</span>:<span class="string">""</span>,</div><div class="line"> <span class="attribute">checkWord</span>:null</div><div class="line"> },</div><div class="line"> <span class="attribute">wordInput</span>: function(e){</div><div class="line"> console.log(e);</div><div class="line"> <span class="selector-tag">this</span><span class="selector-class">.setData</span>({<span class="attribute">checkWord</span>:e.detail.value});</div><div class="line"> }</div><div class="line">})</div></pre></td></tr></table></figure>
<p>由此,我们将每一次输入结果实时地保存了起来。</p>
<h4 id="网络请求"><a href="#网络请求" class="headerlink" title="网络请求"></a>网络请求</h4><p>根据<a href="https://www.shanbay.com/help/developer/api_v1/" target="_blank" rel="external">api文档</a>,我们要先写两个网络请求函数,发送待翻译的信息,接收结果。这次在<code>app.js</code>里写这个函数,这样未来如果有需要可以复用。这里的参数<code>cb</code>是一个函数,用于接收返回值。</p>
<figure class="highlight actionscript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// app.js</span></div><div class="line"></div><div class="line">App({</div><div class="line"> getInfo: <span class="function"><span class="keyword">function</span> <span class="params">(words,cb)</span></span>{</div><div class="line"> <span class="keyword">const</span> requestTask = wx.request({</div><div class="line"> url: <span class="string">'https://api.shanbay.com/bdc/search/'</span>,</div><div class="line"> data: {</div><div class="line"> word: words</div><div class="line"> },</div><div class="line"> header: {</div><div class="line"> <span class="string">'content-type'</span>: <span class="string">'application/json'</span></div><div class="line"> },</div><div class="line"> success: <span class="function"><span class="keyword">function</span> <span class="params">(res)</span> </span>{</div><div class="line"> cb(res.data);</div><div class="line"> }</div><div class="line"> })</div><div class="line"> },</div><div class="line"> getSen: <span class="function"><span class="keyword">function</span> <span class="params">(wordsid,cb)</span></span>{</div><div class="line"> <span class="keyword">const</span> requestTask = wx.request({</div><div class="line"> url: <span class="string">'https://api.shanbay.com/bdc/example/'</span>,</div><div class="line"> data: {</div><div class="line"> vocabulary_id: wordsid,</div><div class="line"> <span class="string">"type"</span>: <span class="string">"sys"</span></div><div class="line"> },</div><div class="line"> header: {</div><div class="line"> <span class="string">'content-type'</span>: <span class="string">'application/json'</span></div><div class="line"> },</div><div class="line"> success: <span class="function"><span class="keyword">function</span> <span class="params">(res)</span> </span>{</div><div class="line"> cb(res.data);</div><div class="line"> }</div><div class="line"> })</div><div class="line"> }</div><div class="line">})</div></pre></td></tr></table></figure>
<h4 id="按钮点击事件"><a href="#按钮点击事件" class="headerlink" title="按钮点击事件"></a>按钮点击事件</h4><p>上面我们已经说过,我们为按钮点击事件绑定了一个<code>btnClick</code>函数,这个函数将负责调用<code>app.js</code>里的两个请求函数,获取返回值,在页面上显示内容。这里要注意的是,因为请求函数位置问题,所以要写<code>var app= getApp()</code> 和 <code>var thispage = this</code>。</p>
<p>通过<a href="https://www.shanbay.com/help/developer/api_v1/#query_word" target="_blank" rel="external">api返回示例</a>,我们看到:在查询单词意思时,需要发送英文翻译<code>word</code>,得到返回值中的 <code>data->cn_definition->defn</code> 和 <code>data->id</code>;在查询例句时,需要发送上面获得的 <code>id</code>,得到返回值中的 <code>data->annotation</code> 和 <code>data->translation</code>。在这里值得注意的是,例句部分,扇贝在对应单词处加了<code><vocab></vocab></code>标签,这里可以用正则去掉。</p>
<p>于是我们的<code>index.js</code>就变成了:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// index.js</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> app= getApp();</div><div class="line"></div><div class="line">Page({</div><div class="line"> <span class="keyword">data</span>: {</div><div class="line"> text:<span class="string">""</span>,</div><div class="line"> sentext:<span class="string">""</span>,</div><div class="line"> checkWord:<span class="literal">null</span></div><div class="line"> },</div><div class="line"> wordInput: function(e){</div><div class="line"> console.log(e);</div><div class="line"> <span class="keyword">this</span>.setData({checkWord:e.detail.value});</div><div class="line"> },</div><div class="line"> btnClick: function (){</div><div class="line"> <span class="keyword">var</span> thispage = <span class="keyword">this</span>;</div><div class="line"> app.getInfo(<span class="keyword">this</span>.<span class="keyword">data</span>.checkWord,function (<span class="keyword">data</span>){</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">data</span>.<span class="keyword">data</span>.cn_definition){</div><div class="line"> console.log(<span class="keyword">data</span>.<span class="keyword">data</span>.id);</div><div class="line"> thispage.setData({ text: <span class="keyword">data</span>.<span class="keyword">data</span>.cn_definition.defn });</div><div class="line"> app.getSen(<span class="keyword">data</span>.<span class="keyword">data</span>.id,function (<span class="keyword">data</span>){</div><div class="line"> <span class="keyword">var</span> sen = (<span class="keyword">data</span>.<span class="keyword">data</span>)[<span class="number">0</span>].<span class="keyword">annotation</span>;</div><div class="line"> sen = sen.replace(/<[^>]+>/g, <span class="string">""</span>);</div><div class="line"> <span class="keyword">var</span> tran = (<span class="keyword">data</span>.<span class="keyword">data</span>)[<span class="number">0</span>].translation;</div><div class="line"> <span class="keyword">var</span> showText = <span class="string">"例句:"</span>+<span class="string">"\n"</span>+sen+<span class="string">"\n"</span>+tran;</div><div class="line"> thispage.setData({ sentext: showText});</div><div class="line"> console.log(sen);</div><div class="line"> })</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> thispage.setData({ text: <span class="string">"查询不到这个单词"</span> });</div><div class="line"> thispage.setData({ sentext: <span class="string">""</span> });</div><div class="line"> }</div><div class="line"> })</div><div class="line"> }</div><div class="line"></div><div class="line">})</div></pre></td></tr></table></figure>
<p>这样,我们就完成了整个小程序的编码工作。</p>
<h2 id="体验"><a href="#体验" class="headerlink" title="体验"></a>体验</h2><p>小程序已经上架,欢迎体验。</p>
<p><img src="https://github.com/pthtc/EasyDictionary-Mini-Program/blob/master/image/qr.jpg?raw=true" alt="小程序码"></p>
<h2 id="源码及使用指南"><a href="#源码及使用指南" class="headerlink" title="源码及使用指南"></a>源码及使用指南</h2><p>源码地址(喜欢的话请点赞):<a href="https://github.com/pthtc/EasyDictionary-Mini-Program/tree/master" target="_blank" rel="external">EasyDictionary-Mini-Program</a></p>
<ul>
<li>下载<a href="https://github.com/pthtc/EasyDictionary-Mini-Program/tree/master" target="_blank" rel="external">源码</a></li>
<li>打开<a href="https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html" target="_blank" rel="external">微信开发者工具</a>,新建项目</li>
</ul>
<p><img src="https://github.com/pthtc/EasyDictionary-Mini-Program/blob/master/image/1.jpg?raw=true" alt="1"></p>
<ul>
<li>选择项目地址为下载的文件夹</li>
</ul>
<p><img src="https://github.com/pthtc/EasyDictionary-Mini-Program/blob/master/image/2.png?raw=true" alt="2"></p>
<ul>
<li>编译、扫码预览</li>
</ul>
<p><img src="https://github.com/pthtc/EasyDictionary-Mini-Program/blob/master/image/3.png?raw=true" alt="3"></p>
]]></content>
</entry>
<entry>
<title><![CDATA[【翻译】想帮助用户做决定?你的APP可以这样设计!]]></title>
<url>http://pengtianhao.com/2018/02/02/%E3%80%90%E7%BF%BB%E8%AF%91%E3%80%91%E6%83%B3%E5%B8%AE%E5%8A%A9%E7%94%A8%E6%88%B7%E5%81%9A%E5%86%B3%E5%AE%9A%EF%BC%9F%E4%BD%A0%E7%9A%84APP%E5%8F%AF%E4%BB%A5%E8%BF%99%E6%A0%B7%E8%AE%BE%E8%AE%A1%EF%BC%81/</url>
<content type="html"><![CDATA[<blockquote>
<ul>
<li>原文地址:<a href="https://medium.com/googleplaydev/design-your-app-for-decision-making-e9e5745508e4" target="_blank" rel="external">Design your app for decision-making</a><a id="more"></a></li>
<li>原文作者:<a href="https://medium.com/@_jeniwren?source=post_header_lockup" target="_blank" rel="external">Jeni</a></li>
<li>译文出自:<a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a></li>
<li>本文永久链接:<a href="https://github.com/xitu/gold-miner/blob/master/TODO/design-your-app-for-decision-making.md" target="_blank" rel="external">https://github.com/xitu/gold-miner/blob/master/TODO/design-your-app-for-decision-making.md</a></li>
<li>译者:<a href="https://github.com/pthtc" target="_blank" rel="external">PTHFLY</a></li>
<li>校对者:<a href="https://github.com/ryouaki" target="_blank" rel="external">ryouaki</a></li>
</ul>
</blockquote>
<h1 id="想帮助用户做决定?你的APP可以这样设计!"><a href="#想帮助用户做决定?你的APP可以这样设计!" class="headerlink" title="想帮助用户做决定?你的APP可以这样设计!"></a>想帮助用户做决定?你的APP可以这样设计!</h1><h2 id="简单化,触发器,激励-—-一个优化用户行为的三步走方法-1"><a href="#简单化,触发器,激励-—-一个优化用户行为的三步走方法-1" class="headerlink" title="简单化,触发器,激励 — 一个优化用户行为的三步走方法[1]"></a><em>简单化,触发器,激励 — 一个优化用户行为的三步走方法</em><sup><a href="#note1">[1]</a></sup></h2><p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5beae7b27?w=800&h=421&f=jpeg&s=32383" alt=""></p>
<p>如果你从事移动APP行业,每一天你都有潜在机会影响几百万人的行动。无论是参与使用一个新功能,每天访问你的应用,或是订阅你的增值服务, 你往往很可能在心里有一个希望更多用户会做的关键行为。但是你如何才能增加用户行动的机会呢?</p>
<p>无论你希望的行为是什么, 这篇博文将会把一个优化用户行为结果的三步走方法介绍给你,分别是简化、触发器、积极性(其中的第一部分借鉴了来自于行为经济学、心理学和游戏化的理念)。特别地,在这篇博文中我们将覆盖前两步,也就是简化和触发器。</p>
<p>当考虑鼓励某个特定的用户行为策略的时候,你应该从哪里开始?斯坦福 <a href="http://captology.stanford.edu/" target="_blank" rel="external">Persuasive Tech Lab</a> 的院长,BJ Fogg博士创建了 Fogg 行为模型,来评估三个因素(能力、触发器、积极性)对于给定行为发生可能性的影响:</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5bae13fab?w=484&h=471&f=png&s=36933" alt=""></p>
<p>Fogg 行为模型</p>
<p>模型指出三个影响用户行为的原因,并由此导出三个驱动行为改变的关键步骤</p>
<ul>
<li>步骤一:<strong>简化</strong>所需的行为。通过降低(最好是移除)不利于行为发生的阻碍来鼓励有参与感、有积极性的用户发生行为。</li>
<li>步骤二:<strong>触发</strong>来自于积极用户<sup><a href="#note2">[2]</a></sup>的行为。『触发器』(提示、提示音、行为召唤)的出现甚至可以在积极性水平略低的情况下驱动行为。</li>
<li>步骤三:激发用户的<strong>积极性</strong>。积极性很难被影响,但是如果所需的行为相当『容易』去做,通过吸引人的信息或者加入游戏因素来提升积极性水平可以激励用户按你的想法行动。</li>
</ul>
<p>到此为止还不错。但是你如何执行这些步骤呢?以下我们将会深入探讨前两项步骤。(我会在未来的博文中讨论第三步,激发用户积极性)</p>
<h3 id="步骤一,简化所需行为"><a href="#步骤一,简化所需行为" class="headerlink" title="步骤一,简化所需行为"></a>步骤一,简化所需行为</h3><p>你希望用户去做的行为必须非常容易做到(做起来很少或者没有阻碍)并且容易作出决定(有清晰易懂的好处)。我们做出的每个行为都需要付出代价(比如时间、金钱和认知负担)。这些代价是一种阻碍,每个决定都在代价和收获的好处之间权衡的结果。比如,我们中许多人在年初都会有变得健康的强烈冲动,但是当真需要付出必要锻炼的时候又会导致许多决心的破产。</p>
<p>什么阻碍或者『要求』会降低你的用户进行行动的机会呢?通常用户会付出代价的阻碍包括繁琐的手动输入,冗余的界面,过量的选择,以及因为没有清晰告诉用户要干什么从而引起困惑的信息。这些阻碍可以通过分析用户在应用内的行为数据来定量辨别,也可以通过用户搜索等方式定性识别。一旦你已经识别了用户行动的阻碍,就到了降低或者移除它们的时候了。</p>
<h4 id="降低行动所需时间"><a href="#降低行动所需时间" class="headerlink" title="降低行动所需时间"></a><strong>降低行动所需时间</strong></h4><p>从应用被发现到下载需要有几次点击,更不用说等待下载完成的时间。然而 <a href="https://developer.android.com/topic/instant-apps/index.html" target="_blank" rel="external">Android Instant Apps</a> 是一个通过立即进行本地体验无需下载门槛,让用户快速完成许多任务的选择(例如看一个视频或者支付)。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5bcd4dd2e?w=440&h=449&f=png&s=24523" alt=""></p>
<p>一旦你的用户打开了你的应用,注册过程是下一个繁琐、耗时的雷区。比起每次都要求用户登录,开发者们喜欢的 <a href="https://play.google.com/store/apps/details?id=com.ticketmaster.mobile.android.uk" target="_blank" rel="external">Ticketmaster</a> 和 <a href="https://play.google.com/store/apps/details?id=com.alibaba.aliexpresshd" target="_blank" rel="external">AliExpress</a> 通过整合 <a href="http://get.google.com/smartlock/#for-passwords" target="_blank" rel="external">Google Smart Lock</a> 能够有效省略手动密码这一步骤。它们随后就能看见登录失败的比例大幅下降。</p>
<p>通过进行漏斗分析,开发者能够跟踪核心流程中的用户流失情况,帮助定位所需行为的阻碍。在实行漏斗分析方面,食品配送公司 <a href="https://play.google.com/store/apps/details?id=com.deliveroo.orderapp" target="_blank" rel="external">Deliveroo</a> 识别了一个在首次结账环节的转换流失。他们注意到同样的流失情况并不会在已经将支付和配送信息的存储在应用中的老用户身上发生。意识到他们的注册流程可能是问题的一部分,团队优先考虑部署 <a href="https://developers.google.com/android-pay/" target="_blank" rel="external">Android Pay</a> 来为新用户创造一个简单的结账体验。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5d843d04b?w=446&h=457&f=png&s=40081" alt=""></p>
<h4 id="降低(实际存在或是可察觉的)花费"><a href="#降低(实际存在或是可察觉的)花费" class="headerlink" title="降低(实际存在或是可察觉的)花费"></a><strong>降低(实际存在或是可察觉的)花费</strong></h4><p>降低花费不是意味着你应该全盘降低价格!真实含义是每个预期的购买者有一个不同的『甜蜜点』,根据应用匹配程度、用户位置以及用户综合支付能力反映出他们认为合适的价格。</p>
<p>Tamzin Taylor,Google Play的西欧应用主管,曾经讲出了一些有关于价格优化的最佳关键实践,比如使用 <a href="http://www.economist.com/content/big-mac-index" target="_blank" rel="external">Big Mac Index</a> 进行购买力对比,从而评估每个市场的实际支付能力。</p>
<p><a href="https://www.youtube.com/embed/LQ6MsPmUa38" target="_blank" rel="external"><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac627dd9c00?w=639&h=390&f=png&s=34324" alt="Watch the video"></a></p>
<p>另一个降低成为潜在购买者门槛花费的方法是降低初始消费要求。我们最近为应用订购做的 <a href="https://support.google.com/googleplay/android-developer/answer/140504#intro" target="_blank" rel="external">Introductory Pricing</a> 功能允许你做到这件事。</p>
<p>当我们考虑可察觉的花费的时候,价格显示的方式对价格的感知有重大的影响这件事很有必要被注意带。</p>
<p><strong>1. 锚定效应</strong></p>
<p>开发者和零售商经常通过<a href="https://hbr.org/2013/02/why-good-better-best-prices-are-so-effective" target="_blank" rel="external">堆叠『好』、『更好』、『最好』等词</a>试图『推动』用户去购买某个特定商品。这个方法会因为标志价格与更便宜或更贵的价格点一起放置而起作用。更高价格的『最好』选择扮演了一个参考点,或者说锚点,让用户认为标准价格看起来是个更便宜和超值的选择。</p>
<blockquote>
<p>在一定情境里,我们倾向于中间价格因为他们看起来『公平』。<br>— <a href="https://medium.com/@dkthomp" target="_blank" rel="external">Derek Thompson</a>, The Atlantic</p>
</blockquote>
<p>Dan Ariely 通过一个在他的书《Predictably Irrational.》中一个现在很著名的关于经济学家价格策略的例子引起了我们的注意。杂志提供了三种选择:一个 59 美金的电子版,一个 125 美金的纸质版和一个 125 美金包含纸质和电子两种版本的套餐。Ariely 指出『相对于纯纸质选择,电子+纸质的选择看起来明显超值』,这个说服我们购买第三个选择因为这个『更容易』评估某物的价值当它被放置在有另一个明显不如它的选择旁边。</p>
<p><strong>2. 框架效应</strong></p>
<p>给你两个选择:一个付 60 美金一年,一个付 5 美金每个月,你会选哪个?许多有订阅功能的应用会向潜在购买者高亮显示年付的价格,而不是月付价格。因为这会显得被察觉的价格更低,虽然在一年中他们的花费是一样的。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5bd18a428?w=451&h=446&f=png&s=38131" alt=""></p>
<h4 id="降低认知负担"><a href="#降低认知负担" class="headerlink" title="降低认知负担"></a><strong>降低认知负担</strong></h4><p>你给用户提供越多选择,用户在比较选择和做决定中的心理负担就越沉重。</p>
<p>作为开发者,在用户使用过程的关键节点,除了评估你提供给用户的选择本身,评估你显示选择的方式也值得,因为这将会对做决定的过程有巨大的影响。</p>
<p><strong>限制的价值</strong></p>
<p>比如,在航班应用 <a href="https://play.google.com/store/apps/details?id=net.skyscanner.android.main" target="_blank" rel="external">Skyscanner</a> 中搜索经常获得上千条结果。你可以理性地辩解说顾客们应该衡量每个单独结果的价值。但是在有限时间和认知负担的阻碍下,Skyscanner 决定以一种更好理解的方式聚合结果,从而限制选择。当同样数量的结果被返回,这个页面展示上的简单改变提升了 14% 的转化率。</p>
<p><strong>默认的重要性</strong></p>
<p>总的来说,人们跟随最少阻碍的路径行动。这意味着预先设置的选项是优化用户行为的有力工具,尤其是当这些默认选项对用户有明显好处的时候。</p>
<ul>
<li><p>例如,食谱应用 <a href="https://play.google.com/store/apps/details?id=com.simplefeast.android.app" target="_blank" rel="external">Simple Feast</a> 决定在增值服务的页面强调他们年付订阅。他们用视觉强调的方式展示,并设定为默认用户选择。结果他们发现选择年付订阅的用户增加了。</p>
</li>
<li><p>复选框一个表面上的小改变也有巨大的影响。默认的力量已经被利用并产生了巨大的影响,并在诸如器官捐献领域,很多国家的表格都有『自愿退出』的政策。<a href="http://www.dangoldstein.com/papers/JohnsonGoldstein_Defaults_Transplantation2004.pdf" target="_blank" rel="external">点击查看更高的器官捐献同意比例</a>。为什么?因为人们倾向于继续维持现状。</p>
</li>
</ul>
<h3 id="Step-2-触发积极用户的行为"><a href="#Step-2-触发积极用户的行为" class="headerlink" title="Step 2. 触发积极用户的行为"></a>Step 2. 触发积极用户的行为</h3><p>鼓励所需用户行为的第二步是在主动用户的相关路径中设置相关触发,从而表现出可操作性。BJ Fogg有一个一个值得纪念名言:『在积极用户的使用路径上放置热点触发器』。触发器往往对于用户来说是陌生的,因为它是一个从开发者角度给出的想要影响用户下一步行为的提示、提醒或者行为召唤。一个推送在这个意义上是一个触发器,并且当它是可操作、定制化、时间合适的时候会非常有效。</p>
<p>语言学习软件的 <a href="https://play.google.com/store/apps/dev?id=8335366955203612525" target="_blank" rel="external">Busuu</a> 的产品主管 <a href="https://medium.com/@antoinesakho" target="_blank" rel="external">Antoine Sakho</a> 在他的 <a href="https://medium.com/@antoinesakho/designing-push-notifications-that-dont-suck-af6aaa0ea85" target="_blank" rel="external">Medium 文章</a> 中介绍了他们如何在他们的推送策略中应用 <a href="https://medium.com/@nireyal" target="_blank" rel="external">Nir Eyal</a>的<a href="http://www.nirandfar.com/hooked" target="_blank" rel="external">钩子模型</a> ,从而获得推送打开率300%的增长。他写道:</p>
<blockquote>
<p><em>首先,我们通过个性化推送提示用户</em> <strong><em> (外部触发) </em></strong> <em>从而引发好奇</em> <strong><em>(内部触发)</em></strong><em>. 点击推送, 他们会经历一个测试 **</em>(行为)<em>**</em>。 在测试的最后, 他们会看到一个包含分数的恭喜页面<em> **</em>(奖励)<em>**</em>。 最后,通过训练他们已经学到的词汇,他们强化了长期记忆<em> **</em>(投入)_**.</p>
</blockquote>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac5d339dd56?w=800&h=484&f=gif&s=116216" alt=""></p>
<p>钩子模型,应用于 Busuu 的用户召回活动</p>
<p>尽管推送对于有效召回用户有一定保证,你还是应该避开这些常见陷阱:</p>
<ol>
<li>在不合适的时间推送通知或者推送与用户环境无关的信息,只会产生巨大的反作用。</li>
<li>总是推送相同消息会很快被用户厌烦:跟随Busuu的指引,永远不要把同一条通知推送两遍。</li>
<li>不要归于依赖推送来驱动用户行为。当无需提醒用户也能主动参与应用内容的时候,应用习惯才会最终养成。<a href="https://medium.com/@nireyal" target="_blank" rel="external">Nir Eyal</a> 在他 <a href="https://medium.com/behavior-design/the-psychology-of-notifications-how-to-send-triggers-that-work-25c7be3d84d3#.e4sbkzj7l" target="_blank" rel="external">Medium 文章</a>中总结了这些:</li>
</ol>
<blockquote>
<p><em>能让人养成习惯的产品会在内部触发被感知的时候(比如不确定感或者无聊感)结合外部触发器(例如推送),让用户养成习惯。</em></p>
</blockquote>
<p>最成功的外部刺激是立即反馈。因此你如何构建清晰的时刻。所以你如何在用户应该在确定时刻采取行动的思想下构建这种反馈?根据 <a href="https://en.wikipedia.org/wiki/Prospect_theory" target="_blank" rel="external">Prospect 理论</a>,人们行动会倾向于避免损失,因为同样数量下损失的痛苦会大于获得。这意味着比起有维护的已得的事物,我们更倾向于避免错过一些事情。</p>
<p>限时限量促销是核心工具,许多开发者用它来驱动用户立即购买而不是之后再说。这些手段被我们对失去的厌恶驱动。毕竟,不行动很快导致一种可能性 —— 『错过』交易或者物品。这个点子也可以被用作构建更具说服力的信息。例如,你可以选择聚焦在你用户在不行动可能失去,行动了才会获得的东西。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bac6aac2a7fe?w=412&h=729&f=png&s=126270" alt=""></p>
<p>健康和生活方式 app <a href="https://play.google.com/store/apps/details?id=com.sillens.shapeupclub" target="_blank" rel="external">Lifesum</a> 在加入为新用户准备的限时『新手套装』的第一天就看到了 15% 的增长。 『仅在今天』的信息形成了一种防止错过的紧迫感,驱使用户立即行动。</p>
<p><strong>关键结论总结:</strong></p>
<ul>
<li>在代价和必要资源没有清晰地与最终价值挂钩的时候,用户将不会行动。</li>
<li>如果在可选项之间很难进行评估和选择,用户也不倾向于行动。</li>
<li>如果你在内容中给用户提供相关、可操作的触发,用户更倾向于进行按开发者意愿行动。</li>
</ul>
<p>在 <strong>3月19日周五**</strong>8:30 am (PST)** 加入或者访问 <a href="https://events.google.com/io/schedule/?sid=b187c653-5143-4b2d-addc-103e1f04fbc2#may-19" target="_blank" rel="external">Google I/O talk, “Boost User Retention with Behavioral Insights”</a> ,你可以获得更多的信息<br>。我将会和 <a href="https://play.google.com/store/apps/details?id=co.thefabulous.app" target="_blank" rel="external">The Fabulous</a> 的 CEO Sami Ben Hassine 一起,讨论开发者如何才能应用行为观点来构建更多有吸引力的应用体验。</p>
<hr>
<h4 id="你怎么想"><a href="#你怎么想" class="headerlink" title="你怎么想?"></a>你怎么想?</h4><p>你有关于在优化用户决定方面的问题或者想法吗?在下面评论区继续讨论或者通过井号标签 #AskPlayDev 通知我们,我们会在 <a href="http://twitter.com/googleplaydev" target="_blank" rel="external">@GooglePlayDev</a> (我们会定期分享在上面就如何在Google Play成功的话题分享新闻和小贴士)上回复。</p>
<hr>
<p><em>在我</em> <a href="https://medium.com/googleplaydev/the-right-app-rewards-to-boost-motivation-c1ec86390450" target="_blank" rel="external">第二篇博文</a><em>,我会解释一些行为改变第三步 —— 激发用户积极性的细节。我将探索积极性心理,它与游戏化的关系以及,正确的奖励方法。</em></p>
<p><em>尤其感谢</em> <a href="https://medium.com/@aaronotani" target="_blank" rel="external">Aaron Otani</a> <em>为写这篇博文草稿时提供的反馈。</em></p>
<hr>
<p>译者注:</p>
<ol>
<li><a name="note1"></a> 这篇文章是作者三部曲的第一篇,续集详见:<a href="https://medium.com/googleplaydev/the-right-app-rewards-to-boost-motivation-c1ec86390450" target="_blank" rel="external">传送门</a></li>
<li><a name="note2"></a> 原文为<code>motivated users</code>,此处翻译为积极用户,期待指正</li>
</ol>
<hr>
<blockquote>
<p><a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a> 是一个翻译优质互联网技术文章的社区,文章来源为 <a href="https://juejin.im" target="_blank" rel="external">掘金</a> 上的英文分享文章。内容覆盖 <a href="https://github.com/xitu/gold-miner#android" target="_blank" rel="external">Android</a>、<a href="https://github.com/xitu/gold-miner#ios" target="_blank" rel="external">iOS</a>、<a href="https://github.com/xitu/gold-miner#前端" target="_blank" rel="external">前端</a>、<a href="https://github.com/xitu/gold-miner#后端" target="_blank" rel="external">后端</a>、<a href="https://github.com/xitu/gold-miner#区块链" target="_blank" rel="external">区块链</a>、<a href="https://github.com/xitu/gold-miner#产品" target="_blank" rel="external">产品</a>、<a href="https://github.com/xitu/gold-miner#设计" target="_blank" rel="external">设计</a>、<a href="https://github.com/xitu/gold-miner#人工智能" target="_blank" rel="external">人工智能</a>等领域,想要查看更多优质译文请持续关注 <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a>、<a href="http://weibo.com/juejinfanyi" target="_blank" rel="external">官方微博</a>、<a href="https://zhuanlan.zhihu.com/juejinfanyi" target="_blank" rel="external">知乎专栏</a>。</p>
</blockquote>
]]></content>
</entry>
<entry>
<title><![CDATA[【翻译】断点:像专家一样调试代码]]></title>
<url>http://pengtianhao.com/2018/02/02/%E3%80%90%E7%BF%BB%E8%AF%91%E3%80%91%E6%96%AD%E7%82%B9%EF%BC%9A%E5%83%8F%E4%B8%93%E5%AE%B6%E4%B8%80%E6%A0%B7%E8%B0%83%E8%AF%95%E4%BB%A3%E7%A0%81/</url>
<content type="html"><![CDATA[<blockquote>
<ul>
<li>原文地址:<a href="https://cheesecakelabs.com/blog/breakpoints-debugging-like-pro/" target="_blank" rel="external">Breakpoints: Debugging like a Pro</a><a id="more"></a></li>
<li>原文作者:<a href="https://cheesecakelabs.com/blog/author/alan/" target="_blank" rel="external">Alan Ostanik</a></li>
<li>译文出自:<a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a></li>
<li>本文永久链接:<a href="https://github.com/xitu/gold-miner/blob/master/TODO/breakpoints-debugging-like-pro.md" target="_blank" rel="external">https://github.com/xitu/gold-miner/blob/master/TODO/breakpoints-debugging-like-pro.md</a></li>
<li>译者:<a href="https://github.com/pthtc" target="_blank" rel="external">PTHFLY</a></li>
<li>校对者:<a href="https://github.com/ryouaki" target="_blank" rel="external">ryouaki</a></li>
</ul>
</blockquote>
<h1 id="断点:像专家一样调试代码"><a href="#断点:像专家一样调试代码" class="headerlink" title="断点:像专家一样调试代码"></a>断点:像专家一样调试代码</h1><p><img src="https://user-gold-cdn.xitu.io/2018/2/1/1615037100dcf8e5?w=2000&h=720&f=png&s=12549" alt=""></p>
<p>当我刚开始成为一名iOS开发者的时候,我最大的问题是:当应用崩溃时,我真的不知道 iOS 、 Swift 、Objective-C 都是如何工作的。那时候,我写了很多烂代码,从不担心内存使用、内存访问、 ARC (译者注:Automatic Reference Counting )。那仅仅是因为我不知道那些事情。看在上帝的份上,我是个菜鸟。</p>
<p>就像许多新手一样, <a href="http://www.stackoverflow.com" title="Stack Overflow" target="_blank" rel="external">Stack Overflow</a> 社区教会我许多关于『如何做正确的事情』的方法。我学到了许多帮助提升工作过程的窍门。在这篇文章中,我将分享在这一阶段过程中最重要的一些工具,那就是<strong>断点</strong>!</p>
<p>那么,撸起袖子干起来吧。🙂</p>
<h1 id="断点"><a href="#断点" class="headerlink" title="断点"></a>断点</h1><p>毫无疑问, Xcode 断点是一个强大的工具。其主要目的是调试代码,但是如果我说他们还要更多作用呢? OK,那我们从一些窍门开始吧。</p>
<h2 id="Conditioning-breakpoints-条件断点"><a href="#Conditioning-breakpoints-条件断点" class="headerlink" title="Conditioning breakpoints 条件断点"></a>Conditioning breakpoints 条件断点</h2><p>也许你已经陷入了这样一种困境:你的 <em>TableView</em> 对于所有用户 model 都运行良好,可就是有那么一个引起来一些麻烦。为了调试这个实例,首先你可能会想:『 <em>Ok , 我会在 cell 装载的地方打个断点看看什么情况</em>』。但是对于每个 cell ,甚至是暂时正常的那些,你的断点都会被激活,你不得不不停跳过直到你抵达你想要调试的那个。</p>
<p><a href="https://user-gold-cdn.xitu.io/2018/2/1/161503705af0e010?w=240&h=196&f=gif&s=3600395" target="_blank" rel="external"><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503705af0e010?w=240&h=196&f=gif&s=3600395" alt="The Office TV show gif, saying "please god, no""></a></p>
<p>为了解决这些问题,你可以继续然后给断点设置一个停止的条件,就像我对用户『 Charlinho 』做的那样。<br><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503702f39fd73?w=476&h=146&f=png&s=14899" alt="A conditional breakpoint screenshot"></p>
<h2 id="Symbolic-Breakpoints-标志断点"><a href="#Symbolic-Breakpoints-标志断点" class="headerlink" title="Symbolic Breakpoints 标志断点"></a>Symbolic Breakpoints 标志断点</h2><blockquote>
<p><em>“放轻松,我会用 pod ,那应该会给我们省点工作量。”</em></p>
</blockquote>
<p>谁都无法保证永远不会说这句话。但是使用 pod 或者一个外部库意味着你向你的工程引入了外部代码并且也许你并不知道它是怎么写出来的。比如说你发现在 pod 内部一堆方法里存在一个错误,但是你不知道这个方法在哪里。做个深呼吸,保持冷静。你有<strong><em>Symbolic Breakpoints</em></strong><em>.</em></p>
<p>当事先声明的 <em>标志</em> 被唤醒,这些断点会被激活。 <em>标志</em> 可以是任何非成员函数、实例、类方法,是否在你的类里都可以。因此在函数中加一个断点,无论谁唤醒它,你只要加一个 <em>Symbolic Breakpoint</em> 来观察你想要调试的函数。在我下面的样例中,我观察 <em>UIViewAlertForUnsatisfiableConstraints</em> 方法。每当 Xcode 发现 <em>Autolayout</em> 问题的时候,这个方法都会被唤醒。你可以看在<a href="http://nshint.io/blog/2015/08/17/autolayout-breakpoints/" target="_blank" rel="external">这篇博文</a>看一个更深入的例子。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503703cba303b?w=476&h=191&f=png&s=18623" alt="A Symbolic breakpoint option screenshot"></p>
<h2 id="Customizing-breakpoints-自定义断点"><a href="#Customizing-breakpoints-自定义断点" class="headerlink" title="Customizing breakpoints 自定义断点"></a>Customizing breakpoints 自定义断点</h2><p>像我之前说的,断点是一个强大的工具。你知道吗?你甚至可以在端点上自定义动作。是的,你可以这么做!你可以运行 AppleScript ,捕获 CPU 框架,使用 LLDB ( Low-level Debugger , XCode 内置的调试工具)命令,甚至 shell 命令。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503703ed256a5?w=459&h=176&f=png&s=31832" alt=""></p>
<p>你只需要简单地点击右边的按钮,选择 <em>edit breakpoint</em> 。</p>
<h3 id="好了,你看会想-“酷!但是为什么要这么做?”"><a href="#好了,你看会想-“酷!但是为什么要这么做?”" class="headerlink" title="好了,你看会想: “酷!但是为什么要这么做?”"></a>好了,你看会想: “酷!但是为什么要这么做?”</h3><p>我会给你一个很好的使用案例来帮助你的理解。一个 APP 中最常见的功能是登录,有时候测试它有点无聊。如果你正在同时使用管理员账号和普通账号,你需要不停地输入用户和密码,会让这个过程变得难以忍受。一般的『自动化』登录页面的方法是创建一个 <em>模拟</em> 实例,并把它应用于 <em>if debug</em> 分句。像这样:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TestCredentials</span> </span>{</div><div class="line"> <span class="keyword">static</span> <span class="keyword">let</span> username = <span class="string">"robo1"</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">let</span> password = <span class="string">"xxxxxx"</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">func</span> <span class="title">fillDebugData</span><span class="params">()</span></span> {</div><div class="line"> <span class="keyword">self</span>.userNameTxtField.text = <span class="type">TestCredentials</span>.username</div><div class="line"> <span class="keyword">self</span>.passwordTxtField.text = <span class="type">TestCredentials</span>.password</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="但是你可以用断点来让事情变得简单一点!"><a href="#但是你可以用断点来让事情变得简单一点!" class="headerlink" title="但是你可以用断点来让事情变得简单一点!"></a>但是你可以用断点来让事情变得简单一点!</h3><p>进入登录页面,加一个断点,然后加了两个填写账号密码的 LLDB 表达式。像我下面的例子一样:</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503703dfc124e?w=485&h=258&f=png&s=22672" alt="A Custom breakpoint executing express commands. "></p>
<p>考虑到这一点,你可以加两个不同身份的断点。你只要生效/失效你想要测试的那个,就可以在两个身份间切换了。一旦你在运行中切换用户,也不需要重新构建。</p>
<p>很酷,不是吗?</p>
<h1 id="COMBO-BREAKER"><a href="#COMBO-BREAKER" class="headerlink" title="COMBO BREAKER!"></a>COMBO BREAKER!</h1><p>在我写这篇文章的时候,WWDC 2017 正在举行。他们发布了一些例如新版 Xcode 9 这样的酷炫家伙。如果你想知道在 Xcode 9 中有哪些新的调试工具,我强烈推荐看 <a href="https://developer.apple.com/videos/play/wwdc2017/404/" target="_blank" rel="external">Session 404</a>。</p>
<p><a href="https://user-gold-cdn.xitu.io/2018/2/1/161503707b70c131" target="_blank" rel="external"><img src="https://user-gold-cdn.xitu.io/2018/2/1/161503707b70c131" alt=""></a></p>
<p>这就是全部内容了!现在你知道了一些在我还是新手的时候帮助巨大的最基础的断点窍门。还有哪些我没有提到的酷炫窍门呢?你也有好的主意?请在评论区随意讨论!</p>
<hr>
<blockquote>
<p><a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a> 是一个翻译优质互联网技术文章的社区,文章来源为 <a href="https://juejin.im" target="_blank" rel="external">掘金</a> 上的英文分享文章。内容覆盖 <a href="https://github.com/xitu/gold-miner#android" target="_blank" rel="external">Android</a>、<a href="https://github.com/xitu/gold-miner#ios" target="_blank" rel="external">iOS</a>、<a href="https://github.com/xitu/gold-miner#前端" target="_blank" rel="external">前端</a>、<a href="https://github.com/xitu/gold-miner#后端" target="_blank" rel="external">后端</a>、<a href="https://github.com/xitu/gold-miner#区块链" target="_blank" rel="external">区块链</a>、<a href="https://github.com/xitu/gold-miner#产品" target="_blank" rel="external">产品</a>、<a href="https://github.com/xitu/gold-miner#设计" target="_blank" rel="external">设计</a>、<a href="https://github.com/xitu/gold-miner#人工智能" target="_blank" rel="external">人工智能</a>等领域,想要查看更多优质译文请持续关注 <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a>、<a href="http://weibo.com/juejinfanyi" target="_blank" rel="external">官方微博</a>、<a href="https://zhuanlan.zhihu.com/juejinfanyi" target="_blank" rel="external">知乎专栏</a>。</p>
</blockquote>
]]></content>
</entry>
<entry>
<title><![CDATA[【翻译】17个Xcode小技巧,每个iOS开发者都该知道]]></title>
<url>http://pengtianhao.com/2018/02/02/%E3%80%90%E7%BF%BB%E8%AF%91%E3%80%9117%E4%B8%AAXcode%E5%B0%8F%E6%8A%80%E5%B7%A7%EF%BC%8C%E6%AF%8F%E4%B8%AAiOS%E5%BC%80%E5%8F%91%E8%80%85%E9%83%BD%E8%AF%A5%E7%9F%A5%E9%81%93/</url>
<content type="html"><![CDATA[<blockquote>
<ul>
<li>原文地址:<a href="https://www.detroitlabs.com/blog/2017/04/13/17-xcode-tips-and-tricks-that-every-ios-developer-should-know/" target="_blank" rel="external">17 Xcode Tips and Tricks That Every iOS Developer Should Know</a><a id="more"></a></li>
<li>原文作者:<a href="https://www.detroitlabs.com/blog/author/elyse-turner/" target="_blank" rel="external">Elyse Turner</a></li>
<li>译文出自:<a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a></li>
<li>本文永久链接:<a href="https://github.com/xitu/gold-miner/blob/master/TODO/17-xcode-tips-and-tricks-that-every-ios-developer-should-know.md" target="_blank" rel="external">https://github.com/xitu/gold-miner/blob/master/TODO/17-xcode-tips-and-tricks-that-every-ios-developer-should-know.md</a></li>
<li>译者:<a href="https://github.com/pthtc" target="_blank" rel="external">PTHFLY</a></li>
<li>校对者:<a href="https://github.com/Danny1451" target="_blank" rel="external">Danny1451</a>、<a href="https://github.com/ryouaki" target="_blank" rel="external">ryouaki</a></li>
</ul>
</blockquote>
<h1 id="每个-iOS-开发者都该知道的-17-个-Xcode-小技巧"><a href="#每个-iOS-开发者都该知道的-17-个-Xcode-小技巧" class="headerlink" title="每个 iOS 开发者都该知道的 17 个 Xcode 小技巧"></a>每个 iOS 开发者都该知道的 17 个 Xcode 小技巧</h1><p><img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bbc57b84639e?w=1920&h=1279&f=png&s=55685" alt=""></p>
<p>对于 iOS 开发者,尤其是新手,来说,Xcode 可谓太过复杂,但是不要害怕!我们在这里帮助你。 Xcode 可以帮助你、允许你做的事情非常多。熟悉你的 IDE 是最简单有效增进实力的方法之一。</p>
<p>在对抗越来越臃肿的 Xcode 方面,我们底特律实验室没有新手,并且想与你分享我们的对抗策略。在底特律实验室的开发者投票之后,这是 17 个我们最受欢迎的 Xcode 小技巧。</p>
<p><strong>键位参考:</strong></p>
<ul>
<li><code>⌃</code>: Control</li>
<li><code>⌘</code>: Command</li>
<li><code>⌥</code>: Option</li>
<li><code>⇧</code>: Shift</li>
<li><code>⏎</code>: Return</li>
</ul>
<hr>
<p><strong>1)</strong> 上下移动一整行或者许多行代码:使用 <code>⌘ ⌥ {</code> 上移 或者 <code>⌘ ⌥ }</code> 下移。如果你选择了一些内容, Xcode 会移动所有你选择的代码行;否则,只会移动光标所在的那一行。</p>
<p><strong>2)</strong> 使用 tabs 来保持聚焦。Tab 可以在不同使用情况下被单独配置和优化。Tab可以在<code>Behaviors</code><sup><a href="#note1">[1]</a></sup>中被命名以及使用。</p>
<p><strong>3)</strong> 使用 <code>Behaviors</code> 来根据上下文显示有用的面板。</p>
<ul>
<li><code>Behaviors</code> 在 Xcode 回应某个事项时是重要的偏好设置。当你开始构建的时候,你可以设置一个偏好来打开一个窗口来响应成功、失败、开始调试等等。</li>
<li><strong>有趣的事实:</strong> 在测试失败的时候,你可以将播放音乐作为一个 <code>behavior</code> 。一个这儿的开发者喜欢用『 The Price is Right. 』的音乐当做失败音。</li>
</ul>
<p><strong>4)</strong> 以辅助编辑窗模式打开文件。当使用『快速打开』( <code>⌘ ⇧ O</code> )时,按住 <code>⌥</code> 的同时按 <code>return</code>。</p>
<p><strong>5)</strong> 当光标处于显示『 Copy Qualified Symbol Name 』命令的方法内,使用 <code>⌘ ⇧ ⌃ ⌥ C</code> 会以一个优质、容易粘贴的格式拷贝方法名称。(译者注:例如<code>[UIColor colorWithRed:255/255.0f green:127/255.0f blue:80/255.0f alpha:1]</code>将会被拷贝为<code>+[UIColor colorWithRed:green:blue:alpha:]</code>。)</p>
<p><strong>6)</strong> 当按住 <code>⌥</code> 并点击代码或方法时,有效地使用 Xcode 解析的行内文档可以提供帮助。</p>
<p><strong>7)</strong> 在全局范围一次性更改某个变量名,可以使用 <code>⌘ ⇧ E</code><sup><a href="#note2">[2]</a></sup>。</p>
<p><strong>8)</strong> 你是否使用终端进入一个文件夹并且不确定你的工程使用的是 Xcode 的 workspaces 或者 仅仅是 project ?只需要运行 <code>open -a Xcode</code> 来打开文件夹本身 Xcode 会自动识别。专业提示:把这个加入你的 <code>.bash_profile</code> ,使用一个牛逼的名字(比如 <code>workit</code> )来让你看起来像一个真的骇客。</p>
<p><strong>9)</strong> Xcode 中显示和隐藏的快捷键。</p>
<ul>
<li><code>⌘ ⇧ Y</code> : 显示/隐藏调试区域</li>
<li><code>⌘ ⌥ ⏎</code> : 显示辅助编辑器</li>
<li><code>⌘ ⏎</code> : 隐藏辅助编辑器</li>
</ul>
<p><strong>10)</strong> 使用 <code>⌘ A ^ I</code> 进行自动缩进代码</p>
<p><strong>11)</strong> <a href="http://www.cockos.com/licecap/" target="_blank" rel="external">LICEcap</a> 对于制作在模拟器中的 GIF 动图非常有帮助,用于项目评审非常棒。在 LICEcap 上方,你可以使用 QuickTime 在屏幕上来分享你的硬件(做一个示范或者使用 LICEcap 制作 GIF )。 在你的 iPhone 或者 iPad 插入的情况下,打开 QuickTime Player,点击 File -> New Movie Recording。然后点击记录按钮旁边的向下箭头,选择你的连接设备。这对于远程展示很有用,使用 LICEcap 来制作 GIF 或者为展示制作真机视频。<img src="https://user-gold-cdn.xitu.io/2018/1/31/1614bbc58c67f5ec?w=599&h=338&f=png&s=161701" alt=""></p>
<p><strong>12)</strong> 按下 <code>⌥ ⇧</code> 然后点击项目导航栏中的文件打开一个选择窗口,这时你可以选择在编辑器的哪个位置显示打开的文件。 </p>
<p><strong>13)</strong> 按住 <code>⌥</code> 的同时点击一个项目导航栏中的文件,它会显示在辅助编辑器中。</p>
<p><strong>14)</strong> 把导航面板(显示在 Xcode 界面的左边)想成是『 Command 』面板。那是因为按住 <code>⌘</code> 的同时按一个数字键可以切换到导航栏内相关的『标签』。例如,<code>⌘ 1</code> 打开项目导航;<code>⌘ 7</code> 打开断点导航。相似的,把工具面板看作『 Command+Option 』窗口,<code>⌘ ⌥ 1</code> 也可以打开那个面板的第一个标签 —— 文件检查器。</p>
<p><strong>15)</strong> <code>⌥ ⌘ ↑</code> 和 <code>⌥ ⌘ ↓</code> 在相关文件中进行导航(例如 .m .h 和 .xib 文件)。</p>
<p><strong>16)</strong> 如果你在与 <code>code signing</code> 作战而 Xcode 说你没有一个有效的符合 <code>provisioning profile</code> 的签名身份,它可能会显示给你一个看起来随机、没有什么意义的码。find-identity 会很有帮助。命令 <code>Security find-identity -v</code> 会显示出一件安装的有效身份。</p>
<p><strong>17)</strong> 在你的层层叠叠的文件夹中讯中某个文件夹非常浪费时间。在 Xcode 8 中,你可以使用『 Open Quickly 』对话框或者 <code>⌘ ⇧ O</code> 来省点时间。当它打开了你可以输入你正寻找的文件的文件名的任何部分来找到它。</p>
<p>你是一个 iOS 开发者吗?看看在这里工作是怎样的体验,如果你有兴趣的话,<a href="https://detroitlabs.workable.com/j/F1D69FF0B5" target="_blank" rel="external">点此申请</a>!</p>
<p>译者注:</p>
<ol>
<li><a name="note1"></a> <code>Behaviors</code> 可以在<code>偏好设置</code>中找到</li>
<li><a name="note2"></a> 此处意思是缓存选中的变量名,此时进行 <code>Replace</code> 操作时,替换内容将会直接显示为缓存的内容,而不是空白一片。</li>
</ol>
<hr>
<blockquote>
<p><a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a> 是一个翻译优质互联网技术文章的社区,文章来源为 <a href="https://juejin.im" target="_blank" rel="external">掘金</a> 上的英文分享文章。内容覆盖 <a href="https://github.com/xitu/gold-miner#android" target="_blank" rel="external">Android</a>、<a href="https://github.com/xitu/gold-miner#ios" target="_blank" rel="external">iOS</a>、<a href="https://github.com/xitu/gold-miner#前端" target="_blank" rel="external">前端</a>、<a href="https://github.com/xitu/gold-miner#后端" target="_blank" rel="external">后端</a>、<a href="https://github.com/xitu/gold-miner#区块链" target="_blank" rel="external">区块链</a>、<a href="https://github.com/xitu/gold-miner#产品" target="_blank" rel="external">产品</a>、<a href="https://github.com/xitu/gold-miner#设计" target="_blank" rel="external">设计</a>、<a href="https://github.com/xitu/gold-miner#人工智能" target="_blank" rel="external">人工智能</a>等领域,想要查看更多优质译文请持续关注 <a href="https://github.com/xitu/gold-miner" target="_blank" rel="external">掘金翻译计划</a>、<a href="http://weibo.com/juejinfanyi" target="_blank" rel="external">官方微博</a>、<a href="https://zhuanlan.zhihu.com/juejinfanyi" target="_blank" rel="external">知乎专栏</a>。</p>
</blockquote>
]]></content>
</entry>
<entry>
<title><![CDATA[【附源码】为了帮助程序员谈恋爱,我做了这个APP]]></title>
<url>http://pengtianhao.com/2018/01/21/%E3%80%90%E9%99%84%E6%BA%90%E7%A0%81%E3%80%91%E4%B8%BA%E4%BA%86%E5%B8%AE%E5%8A%A9%E7%A8%8B%E5%BA%8F%E5%91%98%E8%B0%88%E6%81%8B%E7%88%B1%EF%BC%8C%E6%88%91%E5%81%9A%E4%BA%86%E8%BF%99%E4%B8%AAAPP/</url>
<content type="html"><![CDATA[<blockquote>
<p>前段时间,21岁的我第一次开启了名为『恋爱』的副本,开始打名叫『女朋友』的BOSS,深感过程艰难,整个过程仿佛0级的菜鸡进入了布满世界迷雾的地图,只能小心翼翼才能平安通过。然而在打怪升级的过程中,偶然看见有人感叹:要是谈恋爱有进度条该多好!于是基于这个idea,身为iOS开发者的我决定开发一款能够像技术手册一般的app,帮助程序员以及其他恋爱初学者入门,并且更健康的恋爱。当然,女朋友也作为顾问参与了app内容的准备,对此我是感激涕零的。</p>
</blockquote>
<a id="more"></a>
<p>APP的思路是通过任务系统,让用户明白恋爱每个阶段要做的最基本的事情,并且通过做任务的过程来联动其他功能,最终达到能够推送关系深入的效果。</p>
<h1 id="APP信息"><a href="#APP信息" class="headerlink" title="APP信息"></a>APP信息</h1><ul>
<li>名称:恋爱进度条</li>
<li>iOS版下载地址:<a href="http://www.loveprogress.cn" target="_blank" rel="external">恋爱进度条</a></li>
<li>扫码下载:</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/21/16118d52faf3c032?w=280&h=280&f=png&s=4991" alt=""></p>
<ul>
<li>截图:</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2018/1/21/161180358ea35bcf?w=2606&h=1160&f=jpeg&s=964998" alt=""></p>
<h1 id="技术背景"><a href="#技术背景" class="headerlink" title="技术背景"></a>技术背景</h1><p>首先介绍一下技术背景:</p>
<ol>
<li>某渣一本大四计算机专业</li>
<li>还算OK的iOS开发技术,做过几个商业项目,带过技术团队</li>
<li>职业方向转型为产品,拿了某大厂OFFER,有能力搞定原型&UI</li>
</ol>
<h1 id="工具选择"><a href="#工具选择" class="headerlink" title="工具选择"></a>工具选择</h1><ul>
<li>前期准备:金数据、墨刀</li>
<li>开发:Leancloud、Xcode</li>
</ul>
<h1 id="主要功能模块"><a href="#主要功能模块" class="headerlink" title="主要功能模块"></a>主要功能模块</h1><ul>
<li>任务系统:主要包含关系阶段(等级)、任务清单、已完成任务记录、单个任务完成度(即用户向功能表添加的条目)</li>
<li>纪念:实现了计算纪念日已过天数与对应的下一个周年日的倒数天数</li>
<li>行动:实现了记录计划的日期和行动详情</li>
<li>记录:实现了<strong>精简版的朋友圈</strong>,包括内容、时间、tag、六宫格图片顺序存储</li>
<li>喜好:实现了根据是否喜欢和根据场景记录对方喜好,并且可以分类显示的功能</li>
</ul>
<h1 id="开发相关"><a href="#开发相关" class="headerlink" title="开发相关"></a>开发相关</h1><h2 id="后端选择"><a href="#后端选择" class="headerlink" title="后端选择"></a>后端选择</h2><p>虽然Python水平还可以,可以使用Django开发后端,但是为了上线速度,最后还是决定用BAAS服务做后端。之前一直使用Bmob,这回试了一下Leancloud,文档清晰,社区活跃,开发体验非常不错,就是免费额度比较低,商业版收费比较贵。</p>
<h2 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h2><p>开发中唯一的难点是任务不分的设计,由此我的数据库的设计如下<br><img src="https://user-gold-cdn.xitu.io/2018/1/21/1611886c8acd5a77?w=898&h=1144&f=png&s=179105" alt=""></p>
<h2 id="开发"><a href="#开发" class="headerlink" title="开发"></a>开发</h2><p>开发本身比较简单,只说一下使用的第三方库</p>
<ul>
<li>MJRefresh:下拉刷新</li>
<li>WSDaePickerView:日期选择</li>
<li>MBProgressHUD:小菊花</li>
<li>SDWebImage:异步图片加载</li>
<li>ZLPhotoBrowser:图片选择器</li>
</ul>
<h1 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h1><p><a href="https://github.com/pthtc/Loveprogress" target="_blank" rel="external">github(顺便点个赞吧)</a></p>
]]></content>
</entry>
<entry>
<title><![CDATA[逆向解构:摩拜单车月卡系统]]></title>
<url>http://pengtianhao.com/2017/10/06/%E9%80%86%E5%90%91%E8%A7%A3%E6%9E%84%EF%BC%9A%E6%91%A9%E6%8B%9C%E5%8D%95%E8%BD%A6%E6%9C%88%E5%8D%A1%E7%B3%BB%E7%BB%9F/</url>
<content type="html"><![CDATA[<blockquote>
<p>在共享单车市场已成红海、小型创业公司不断倒下的今天,月卡成为摩拜、OFO等玩家拉新、留存的不二选择。在本文探究了摩拜单车的月卡系统运行方式,以下就是作者探究的过程和结论,其中必有疏漏,欢迎指正、讨论。</p>
</blockquote>
<h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><ol>
<li>178开头手机号、185开头手机号,均未注册摩拜单车</li>
<li>测试日期为2017年10月3日</li>
</ol>
<h2 id="探究过程、现象和结论"><a href="#探究过程、现象和结论" class="headerlink" title="探究过程、现象和结论"></a>探究过程、现象和结论</h2><h3 id="一、初次注册"><a href="#一、初次注册" class="headerlink" title="一、初次注册"></a>一、初次注册</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>无</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>注册新用户</li>
<li>跳过实名认证</li>
<li>跳过交纳押金</li>
</ol>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><img src="https://i.loli.net/2017/10/07/59d88085146c1.png" alt="C1E913E1-3DF2-467D-8477-D9E6A0CACAE5.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<p>无</p>
<h3 id="二、H5页面领取月卡"><a href="#二、H5页面领取月卡" class="headerlink" title="二、H5页面领取月卡"></a>二、H5页面领取月卡</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>此时我们先不缴纳押金,寻找是否有免费的月卡可以领取。</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<p><strong>1.</strong> 下图为国信证券推文,从中我们可以看到,小编很明确地告诉我们领取时间为<code>9月1日——9月30日</code>,但是扫描二维码进入页面,使用说明却显示<code>本活动领取月卡的最后期限是10月30日</code>。</p>
<p><img src="https://i.loli.net/2017/10/07/59d882c43eaa8.jpg" alt="CA5045D5B7786D4FEB74130D662712FC.jpg"></p>
<p><strong>2.</strong> 我们可以看到下图是两个不同的活动H5页面,左边为官方活动,右边为飞马旅的联名月卡,这两张月卡推文页所写的时间均为<code>10.1-10.30</code>,且页面模板也与国信证券的有所不同。观察到,两个页面除了颜色和卡名以外完全一致,但是左边月卡作为官方的发放的卡片,卡名却依然为<code>联名月卡</code>。</p>
<p><img src="https://i.loli.net/2017/10/03/59d348670795a.png" alt="0D1711E1-5025-4CCE-9269-0AF6C2FE3406.png"></p>
<p><strong>3.</strong> 作者输入<code>185手机号</code>,成功领取了第一张官方赠送的月卡。但是在尝试领取第二张同模板联名月卡的时候,页面却显示<code>您已领取过月卡 不能重复领取</code>的提示。于是作者又尝试领取国信证券的联名月卡,<code>提示成功</code>。</p>
<p><strong>4.</strong> 使用<code>178手机号</code>领取一张免费月卡</p>
<p><strong>5.</strong> 分析页面URL,发现<br><code>https://m.mobike.com/h5/partner_coupon/zh/index.html?citycode=021&src=weishuba&from=timeline&isappinstalled=0</code><br>中页面内容随着<code>src</code>的改变而改变</p>
<p><strong>6.</strong> 分析H5页面源码,发现资源文件中存储了所有页面的文字</p>
<p><img src="https://i.loli.net/2017/10/06/59d76d6f3d571.jpg" alt="5961507290473_.pic_hd.jpg"></p>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><img src="https://i.loli.net/2017/10/07/59d880c0deba2.png" alt="4A577A72-7528-4121-8995-A9586CF9D1C4.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<ol>
<li><strong><code>摩拜联名月卡领取页自动续期了</code></strong></li>
<li><strong><code>摩拜官方月卡与其他联名月卡使用了统一模板</code></strong></li>
<li><strong><code>所有联名卡均指向某一模板,相同模板下属的不同联名卡不得重复领取</code></strong></li>
<li><strong><code>领取月卡时并不会检测是否已缴纳押金</code></strong></li>
<li><strong><code>月卡计算时间只到30天后的24点,而非精确到秒的标准30天时长</code></strong></li>
</ol>
<h3 id="三、缴纳押金"><a href="#三、缴纳押金" class="headerlink" title="三、缴纳押金"></a>三、缴纳押金</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>按照规则,缴纳押金以后应该会赠送一张月卡,事实上却没有发现增加;只有剩余天数小于30天(事实上30天也可)才被允许退款</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>向185账户缴纳押金,发现剩余时间并没有增加</li>
<li>向178账户缴纳押金,发现剩余时间并没有增加</li>
</ol>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><img src="https://i.loli.net/2017/10/07/59d880f2f3563.png" alt="64DC6E00-49A5-419F-8BFF-B0F45EE77C7F.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<ol>
<li><strong><code>赠送的月卡会立即生效,几张有效期重叠的免费月卡允许同时存在,月卡剩余时间以最晚为准</code></strong></li>
<li><strong><code>首充赠送的月卡与H5页面赠送的联名月卡应属同种</code></strong></li>
</ol>
<h3 id="四、续费"><a href="#四、续费" class="headerlink" title="四、续费"></a>四、续费</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>按照规则,与领取的月卡不同,续费之后时间应该在原有基础上增加,事实上也确实如此</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>向<code>178手机号</code>续费一张<code>一个月月卡</code>,续费之后月卡剩余天数变为<code>60天</code>,在原有基础上增加了30天,与规则符合。</li>
<li>使用<code>185手机号</code>续费,提示失败,与规则符合</li>
</ol>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><strong>178</strong></p>
<p><img src="https://i.loli.net/2017/10/07/59d88115e7247.png" alt="50C10166-DC15-4A23-B5FC-DC5C3D6A5DA4.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<p>无</p>
<h3 id="五、退押金"><a href="#五、退押金" class="headerlink" title="五、退押金"></a>五、退押金</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>按照规则,退款之后,免费月卡将消失,只剩下收费月卡</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>使用<code>178手机号</code>退款,剩余天数归零,与规则符合</li>
<li>使用<code>185手机号</code>退款,剩余天数为31天,比想象中多1天</li>
<li>此时点击退款退款时,在摩拜的iOS端可以看到这段文字:</li>
</ol>
<p><img src="https://i.loli.net/2017/10/03/59d345460bcf3.jpg" alt="退款文字"></p>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><img src="https://i.loli.net/2017/10/07/59d8813ed76e8.png" alt="D3605992-A8C7-41FB-ADBB-CA690107878C.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<ol>
<li><strong><code>摩拜的月卡分为免费月卡和购买的月卡</code></strong></li>
<li><strong><code>在有付费卡的情况下,退款仍然会改变到期日</code></strong></li>
</ol>
<h3 id="六、再次续费"><a href="#六、再次续费" class="headerlink" title="六、再次续费"></a>六、再次续费</h3><ul>
<li><strong>描述</strong></li>
</ul>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>使用<code>178手机号</code>充值3个月,剩余天数变为91天,比想象中多1天</li>
<li>使用<code>185手机号</code>充值,提示失败,与规则符合</li>
</ol>
<ul>
<li><strong>数据</strong></li>
</ul>
<p><img src="https://i.loli.net/2017/10/07/59d88193228be.png" alt="12B14862-4F35-49D4-B571-0D4F96A34F37.png"></p>
<ul>
<li><strong>结论</strong></li>
</ul>
<p>无</p>
<h3 id="七、再次领取H5页面月卡"><a href="#七、再次领取H5页面月卡" class="headerlink" title="七、再次领取H5页面月卡"></a>七、再次领取H5页面月卡</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>按照规则,每张月卡只能领取一次,但是规则又写,如果已领用且仍在有效期内,则不能再次领取,目前的情况是账号因为退款失去了全部免费额度,那么是否可以再次领取呢?</p>
<ul>
<li><strong>过程</strong></li>
</ul>
<ol>
<li>使用<code>185手机号</code>领取H5页面月卡,提示<code>您已领取过月卡 不能重复领取</code></li>
</ol>
<ul>
<li><strong>结论</strong></li>
</ul>
<ol>
<li><strong><code>免费月卡在失效之后,并未删除,而是设置为失效状态</code></strong></li>
</ol>
<h3 id="八、联系官方客服"><a href="#八、联系官方客服" class="headerlink" title="八、联系官方客服"></a>八、联系官方客服</h3><ul>
<li><strong>描述</strong></li>
</ul>
<p>打电话过去,客服的态度非常不耐烦,但还是拿到了想要的信息</p>
<ul>
<li><strong>结论</strong></li>
</ul>
<ol>
<li><strong><code>月卡只按照月卡创建的先后顺序使用,不考虑免费/收费</code></strong></li>
</ol>
<h2 id="疑惑和推测解决方案"><a href="#疑惑和推测解决方案" class="headerlink" title="疑惑和推测解决方案"></a>疑惑和推测解决方案</h2><p>到这里,还有两个问题没有解决</p>
<ol>
<li>为什么3个月月卡购买后为91天(31+30+30),而1个月月卡购买只有30天</li>
<li>一个月免费月卡+一个月购买月卡的账户,退款前为60天,退款后余额为31天而不是30天</li>
</ol>
<ul>
<li><p>针对第一个疑惑,如果<strong><code>摩拜计算日期,仅仅是简单地改变年份和月份而不是真实计算90天以后的日期</code></strong>(这样做只需处理2月29日这一种特殊情况),那么2017年10月3日到2018年1月3日相差92天,2017年10月3日到2017年11月3日相差31天,两个天数均减去1天(即购买瞬间已默认消耗一天),就可以解释91天和30天的现象了</p>
</li>
<li><p>针对第二个疑惑,按前一算法,2017年10月3日到2017年12月3日相差61天,减去当天为60天,60天可以解释;按照客服说法,依据月卡获得顺序使用,那么消耗的首先是免费月卡,购买的月卡未被使用,要得出31天这个数据,必须有两个前提:</p>
</li>
</ul>
<ol>
<li><strong><code>摩拜购买的月卡未被使用前,并不直接写死到期天数,而是以未使用状态的卡片存在</code></strong></li>
<li><strong><code>退押金当日的消费仍然算在上一张卡上,天数计算无需减扣当天使用</code></strong></li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总结一下现在所有的推测:</p>
<ol>
<li><strong><code>摩拜联名月卡领取页自动续期了</code></strong></li>
<li><strong><code>摩拜官方月卡与其他联名月卡使用了统一模板</code></strong></li>
<li><strong><code>所有联名卡均指向某一模板,相同模板下属的不同联名卡不得重复领取</code></strong></li>
<li><strong><code>领取月卡时并不会检测是否已缴纳押金</code></strong></li>
<li><strong><code>月卡计算时间只到30天后的24点,而非精确到秒的标准30天时长</code></strong></li>
<li><strong><code>赠送的月卡会立即生效,几张有效期重叠的免费月卡允许同时存在,月卡剩余时间以最晚为准</code></strong></li>
<li><strong><code>首充赠送的月卡与H5页面赠送的联名月卡应属同种</code></strong></li>
<li><strong><code>月卡分为免费月卡和购买的月卡</code></strong></li>
<li><strong><code>在有付费卡的情况下,退款仍然会改变到期日</code></strong></li>
<li><strong><code>免费月卡在失效之后,并未删除,而是设置为失效状态</code></strong></li>
<li><strong><code>月卡只按照月卡创建的先后顺序使用,不考虑免费/收费</code></strong></li>
<li><strong><code>摩拜在计算日期时,仅仅是简单地改变年份和月份而不是真实计算90天以后的日期</code></strong></li>
<li><strong><code>购买的月卡未被使用前,并不直接写死到期天数,而是以未使用状态的卡片存在</code></strong></li>
<li><strong><code>退押金当日的消费仍然算在上一张卡上,天数计算无需减扣当天使用</code></strong></li>
</ol>
<p>根据以上结论,可以设计一个最简版本月卡,满足当前需求:</p>
<p><img src="https://i.loli.net/2017/10/06/59d7725b2156d.png" alt="25AC0297-A4CC-43D3-8127-FE8ACD8DB95B.png"></p>
]]></content>
</entry>
<entry>
<title><![CDATA[腾讯家的移动医疗:丁香医生、微医、好大夫产品分析]]></title>
<url>http://pengtianhao.com/2017/09/25/%E8%85%BE%E8%AE%AF%E5%AE%B6%E7%9A%84%E7%A7%BB%E5%8A%A8%E5%8C%BB%E7%96%97%EF%BC%9A%E4%B8%81%E9%A6%99%E5%8C%BB%E7%94%9F%E3%80%81%E5%BE%AE%E5%8C%BB%E3%80%81%E5%A5%BD%E5%A4%A7%E5%A4%AB%E4%BA%A7%E5%93%81%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<blockquote>
<p>腾讯作为中国互联网的龙头大哥之一,并没有像阿里一般成立阿里健康,下海贴身肉搏,而是选择通过投资布局互联网医疗行业。现在作者抱着学习的态度,分析了三家由腾讯投资的互联网医疗公司,观点浅薄,欢迎讨论</p>
</blockquote>
<h2 id="总体情况和用户分析"><a href="#总体情况和用户分析" class="headerlink" title="总体情况和用户分析"></a>总体情况和用户分析</h2><h3 id="应用排名"><a href="#应用排名" class="headerlink" title="应用排名"></a>应用排名</h3><p><img src="https://i.loli.net/2017/09/22/59c4d9b264ccf.png" alt="应用排名"></p>
<p>从这里我们可以看出,丁香医生的排名三者垫底,好大夫和微医则处于纠缠状态</p>
<h3 id="用户活跃"><a href="#用户活跃" class="headerlink" title="用户活跃"></a>用户活跃</h3><p><img src="https://i.loli.net/2017/09/22/59c4da5c1378b.png" alt="用户活跃"></p>
<p>微医>好大夫>丁香医生,但是微医活跃度有所下降</p>
<h3 id="人群分布"><a href="#人群分布" class="headerlink" title="人群分布"></a>人群分布</h3><p><img src="https://i.loli.net/2017/09/22/59c4db338704c.png" alt="653BDA29-DAF1-4782-967B-793C661633C5.png"></p>
<ul>
<li><p>丁香医生遍布全国,用户比较平均</p>
</li>
<li><p>好大夫和微医的用户区域比较重合,但微医拥有三者中拥有最多的深色区域,可能与它接入了更多的线下互联网医院、医联体有关</p>
</li>
</ul>
<h3 id="使用场景辨析"><a href="#使用场景辨析" class="headerlink" title="使用场景辨析"></a>使用场景辨析</h3><h4 id="丁香医生"><a href="#丁香医生" class="headerlink" title="丁香医生"></a>丁香医生</h4><p><strong>使用场景:</strong></p>
<ol>
<li><p>身体有恙,使用app进行问诊,得到医生建议之后前往医院进行诊治</p>
</li>
<li><p>小病时查询用药方法和禁忌</p>
</li>
<li><p>查找附近药房医院(极少)</p>
</li>
<li><p>通过检索疾病名,查看可能情况的病例及医生的回答</p>
</li>
</ol>
<p><strong>解决痛点:</strong></p>
<ol>
<li><p>家庭用药快查</p>
</li>
<li><p>轻问诊,方便用户解决小病慢病</p>
</li>
<li><p>阅读靠谱的医疗科普文章的需求</p>
</li>
</ol>
<h4 id="微医"><a href="#微医" class="headerlink" title="微医"></a>微医</h4><p><strong>使用场景:</strong></p>
<ol>
<li><p>不想排队,直接在线挂号,节省时间</p>
</li>
<li><p>生病时与医生交流,依据确诊情况开出的处方</p>
</li>
<li><p>在线购买药品器械</p>
</li>
<li><p>在医言堂与病友、医生交流,并按照疾病获取最新资讯</p>
</li>
<li><p>目前微医还在做医疗百科等项目,希望占据权威医疗知识高地</p>
</li>
</ol>
<p><strong>解决痛点:</strong></p>
<ol>
<li><p>挂号难</p>
</li>
<li><p>小病去医院、药店浪费时间</p>
</li>
<li><p>专家问诊</p>
</li>
</ol>
<h4 id="好大夫"><a href="#好大夫" class="headerlink" title="好大夫"></a>好大夫</h4><p><strong>使用场景:</strong></p>
<ol>
<li><p>生病了想要在线看个病,顺便开个处方/检查,之后通过其他途径购药/检查</p>
</li>
<li><p>急病时想要立刻与医生通话</p>
</li>
<li><p>有慢病,和家庭医生随时联系</p>
</li>
</ol>
<p><strong>解决痛点:</strong></p>
<ol>
<li><p>紧急联系医生需求</p>
</li>
<li><p>可以在线获得处方药许可</p>
</li>
<li><p>慢病管理</p>
</li>
</ol>
<h2 id="功能和页面"><a href="#功能和页面" class="headerlink" title="功能和页面"></a>功能和页面</h2><h3 id="功能详图"><a href="#功能详图" class="headerlink" title="功能详图"></a>功能详图</h3><h4 id="丁香医生-1"><a href="#丁香医生-1" class="headerlink" title="丁香医生"></a>丁香医生</h4><p><img src="https://i.loli.net/2017/09/22/59c4ddb169cd4.png" alt="丁香园功能"></p>
<h4 id="微医-1"><a href="#微医-1" class="headerlink" title="微医"></a>微医</h4><p><img src="https://i.loli.net/2017/09/22/59c4dd87990ef.png" alt="微医功能"></p>
<h4 id="好大夫-1"><a href="#好大夫-1" class="headerlink" title="好大夫"></a>好大夫</h4><p><img src="https://i.loli.net/2017/09/22/59c4dd5fc1b70.png" alt="好大夫功能"></p>
<h3 id="核心功能对比"><a href="#核心功能对比" class="headerlink" title="核心功能对比"></a>核心功能对比</h3><p><img src="https://i.loli.net/2017/09/22/59c4de186cb28.png" alt="1730E13A-BF7B-4E2A-A8E5-3C24CB3D94DE.png"></p>
<p><strong>由此可以看出:</strong></p>
<ul>
<li><p>丁香医生偏<strong>工具属性</strong></p>
</li>
<li><p>微医是<strong>综合医疗平台</strong></p>
</li>
<li><p>好大夫更像是<strong>网上医院</strong></p>
</li>
</ul>
<h3 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h3><h4 id="丁香园"><a href="#丁香园" class="headerlink" title="丁香园"></a>丁香园</h4><p><img src="https://i.loli.net/2017/09/22/59c4df3e67f81.png" alt="B7B59E9E-D8B8-4161-87AC-2D07FFA04B90.png"></p>
<h4 id="微医-2"><a href="#微医-2" class="headerlink" title="微医"></a>微医</h4><p><img src="https://i.loli.net/2017/09/22/59c4df0e51a83.png" alt="6CD1FD9E-AF38-489F-93E9-A3595ECCE0E9.png"></p>
<p><img src="https://i.loli.net/2017/09/22/59c4ded312315.png" alt="32FAA5CE-5E56-4120-9F06-F595CA21A5F4.png"></p>
<h4 id="好大夫-2"><a href="#好大夫-2" class="headerlink" title="好大夫"></a>好大夫</h4><p><img src="https://i.loli.net/2017/09/22/59c4df81d949c.png" alt="F5433C40-B6EA-4E55-9D32-89C5A77454E9.png"></p>
<h4 id="布局总结"><a href="#布局总结" class="headerlink" title="布局总结"></a>布局总结</h4><p><img src="https://i.loli.net/2017/09/22/59c4dfc076240.png" alt="BE045CE1-22D0-45A0-8F6E-5659AD6C9ABA.png"></p>
<h2 id="优劣势"><a href="#优劣势" class="headerlink" title="优劣势"></a>优劣势</h2><h3 id="丁香医生-2"><a href="#丁香医生-2" class="headerlink" title="丁香医生"></a>丁香医生</h3><p><strong>优势</strong></p>
<ol>
<li><p>全平台营销(知乎、微信公众号)带来巨大流量</p>
</li>
<li><p>依托丁香园平台,拥有庞大的医生资源</p>
</li>
<li><p>工具属性让用户能够很方便地使用特定功能</p>
</li>
<li><p>类似分答的功能可以查看已有回答,容易找到与自身相同的病例和解决方法</p>
</li>
<li><p>简洁美观的UI让使用变得非常简单,用户体验较好</p>
</li>
</ol>
<p><strong>劣势</strong></p>
<ol>
<li><p>流量没有转化为下载和活跃</p>
</li>
<li><p>功能太过简单的情况下,功能各不相关,很难让用户在特定场景下想起</p>
</li>
<li><p>核心功能和市场上几乎所有产品重合,没有特色</p>
</li>
<li><p>问诊非实时聊天,用户体验不是很好,经常需要长时间等待</p>
</li>
<li><p>工具箱为鸡肋功能,使用地图应用用户体验更好</p>
</li>
<li><p>没有处方权,用户使用完毕依然需要前往医院</p>
</li>
<li><p>数据零散,用户分析困难</p>
</li>
</ol>
<h3 id="微医-3"><a href="#微医-3" class="headerlink" title="微医"></a>微医</h3><p><strong>优势</strong></p>
<ol>
<li><p>曝光主要来自各大主流媒体(央视报道、各大报纸),且被习大大提及,产品最为落地</p>
</li>
<li><p>咨询—问诊—商城形成闭环,可以提供全流程服务</p>
</li>
<li><p>拳头功能挂号引流强大,用户粘性很强,复用率有保证</p>
</li>
<li><p>大量功能使得用户能够在任何场景想起并且重复使用</p>
</li>
<li><p>问诊采用实时聊天和视频语音,响应迅速,用户习惯从微信迁移,体验高成本低</p>
</li>
</ol>
<p><strong>劣势</strong></p>
<ol>
<li><p>布局复杂,高低频入口无法区分,很难找到想要的功能</p>
</li>
<li><p>品牌调性较差,UI无法给人留下深刻印象</p>
</li>
</ol>
<h3 id="好大夫-3"><a href="#好大夫-3" class="headerlink" title="好大夫"></a>好大夫</h3><p><strong>优势</strong></p>
<ol>
<li><p>以互联网医院为服务核心,用户目标感强,容易在特定场景记起并使用</p>
</li>
<li><p>有特色功能,能够直接联系到医生</p>
</li>
<li><p>慢病管理功能在一定程度上可以提高用户粘性和活跃</p>
</li>
</ol>
<p><strong>劣势</strong></p>
<ol>
<li><p>UI粗糙,用户体验一般</p>
</li>
<li><p>开处方的同时没有提供购买渠道,未形成闭环</p>
</li>
<li><p>聚焦于某几个场景导致其他场景无法覆盖,并且由于地域情况不同导致有些地区甚至无法提供服务</p>
</li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>现今几乎所有的移动医疗产品都接入了问诊功能,并且都基于银川互联网医院拥有开处方能力,然而因为医疗行业的特殊性,『带着镣铐跳舞』,导致某些业务高度重合,竞争极其激烈。</p>
<p>三大产品如今虽然上线时间相差较多,且倾向各有不同,但都有不同的核心打法。丁香医生背靠丁香园近200万医生资源和最为强大的医疗新媒体,拥有良好的口碑和优秀的内容;微医前身为挂号网,有刚需拳头产品,并且与政府关系紧密,基于保险和医联体平台使得其拥有三者中最强的变现能力;好大夫则是三者中历史最悠久的互联网问答产品,积攒了足够的用户,并且有三者中最为明确的定位。 由此可见,腾讯投资的三家公司各有所长,在政策逐渐松绑的互联网医疗领域,未来大有可为。</p>
]]></content>
</entry>
<entry>
<title><![CDATA[在南极旅行是怎样的体验?]]></title>
<url>http://pengtianhao.com/2017/04/06/%E5%9C%A8%E5%8D%97%E6%9E%81%E6%97%85%E8%A1%8C%E6%98%AF%E6%80%8E%E6%A0%B7%E7%9A%84%E4%BD%93%E9%AA%8C/</url>
<content type="html"><![CDATA[<p>话不多说,先上图</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-b3a6c06f60b3d2e2.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p>接下来我会以我的行程为主线,然后穿插一些图和感受,图片直接来自我的iPhoneSE,大家要是有耐心看完的话,非常感谢!</p>
<h2 id="总行程"><a href="#总行程" class="headerlink" title="总行程"></a>总行程</h2><p><strong>上海—布宜诺斯艾利斯—乌斯怀亚—南设得兰群岛—南极大陆—乌斯怀亚—布宜—回国</strong></p>
<p>今年1月31号出发,2月15号回国,半个月时间有十天在船上,行程比较短,和上面专业的旅行家比起来行程更短更轻松,比较适合我这种生活不能自理只会写码的胖子~~</p>
<p>下面直接从乌斯怀亚开始说,如果大家对我那不怎么完整的阿根廷体验感兴趣的话,后面再补。</p>
<h2 id="起点——世界的尽头乌斯怀亚"><a href="#起点——世界的尽头乌斯怀亚" class="headerlink" title="起点——世界的尽头乌斯怀亚"></a>起点——世界的尽头乌斯怀亚</h2><p>乌斯怀亚位于南美大陆最南端,所以有很多所谓的『世界最南边的邮局』『世界最南边的银行』等,事实上这个看起来很美好的小镇以前是被用作犯人的流放之地。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-a7ad00a9b5eeccb5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-edd50b4b3f08482b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>在乌斯怀亚前后待了一天,最让人印象深刻的是在海边餐厅吃到的帝王蟹,毕竟中国沿海并不生产这种张牙舞爪的生物。外壳非常坚硬,吃的时候需要拿剪刀剪开,味道很鲜,但是并没有惊艳的感觉,这么好的食材交给南美人真是可惜。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-522af205bc06af7e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="帝王蟹"></p>
<p>乌斯怀亚白天看起来是个风光不错的小镇,不过一到晚上,主街道变得非常繁忙,作为外国人不是很能分清究竟多少人是游客,多少人是本地人。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-f7c88b0c86ecfb12.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<h2 id="出发——庞洛星辉号"><a href="#出发——庞洛星辉号" class="headerlink" title="出发——庞洛星辉号"></a>出发——庞洛星辉号</h2><p>因为旅程一大半时间都在船上度过,这里不得不介绍一下这艘很有意思的船。按照向导的说法,这是在南极海域活动最好的游轮之一,长这样(在冲锋舟上拍的,雾很大):</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-632c94837f2b517d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="庞洛星辉号"></p>
<p>整条船有7层,这次一共有190名乘客,170名船员,稳定性和那些动辄上千人的大游轮没得比,但是如果没有大浪的情况下舒适度还是不错的。船上的设施大概有: <strong>舱房,自助餐厅,主餐厅,小剧院,酒吧,图书馆,SPA,健身房和游泳池</strong>(当然在南极并没有什么卵用),除了酒吧卖的酒以外所有吃的都是免费的,因为是包船,所以船上大多数地方都有临时的中文提示。下面只说一下舱房、自助餐厅和上网的问题,如果朋友们还有问题可以再问。</p>
<h3 id="舱房"><a href="#舱房" class="headerlink" title="舱房"></a>舱房</h3><p>房间不是很大,但是足够整齐,船员每天打扫两次,更换小冰箱里的免费饮料,房间里的依云水和爱马仕的日用品,再加上24小时的送餐服务,体验不错。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-415d61e40c28ea68.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="舒适的舱房"></p>
<h3 id="自助餐厅"><a href="#自助餐厅" class="headerlink" title="自助餐厅"></a>自助餐厅</h3><p>自助餐厅不多说,正常酒店的配置,每天为了满足中国乘客的口味会有一道不中不洋的special,比如用酸辣酱炒的酸辣土豆丝。值得一提的是,其中一个来自菲律宾的服务生小哥给我留下了深刻印象。仅仅是我问他要了一个橙子,告诉他我想多吃一些维生素,于是直到我们下船,每顿饭他都会亲自给我削几个橙子,非常感动。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-c37ee04f1a166c52.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="永远笑容灿烂的小哥"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-f7538f1c7c7368be.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="小哥削的橙子"></p>
<h3 id="上网"><a href="#上网" class="headerlink" title="上网"></a>上网</h3><p>在船上这几天,深刻体会到网络一家和水电煤一样变成了基础设施,因为是卫星网路,所以网络不仅慢,而且极其昂贵,发一条朋友圈的代价大约是15分钟再加上4.5欧,全天在线绝对是奢望。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-7cce8daf30970c2d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="上网超贵"></p>
<h2 id="最艰难的航行——德雷克海峡"><a href="#最艰难的航行——德雷克海峡" class="headerlink" title="最艰难的航行——德雷克海峡"></a>最艰难的航行——德雷克海峡</h2><p>穿越德雷克海峡大概是2天左右,去程风平浪静,只有一些轻微的摇晃,但是回程路上,这个海峡掀起6~8米的大浪,即使是躺在床上会感觉身体被抛起再落下,非常难受</p>
<p>这边就放一个风平浪静的图,至于惊涛骇浪的时候呢?好吧我没力气拍照了~~</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-dbb8d482763c9519.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="风平浪静的德雷克海峡"></p>
<h2 id="企鹅和长城站——南设得兰群岛"><a href="#企鹅和长城站——南设得兰群岛" class="headerlink" title="企鹅和长城站——南设得兰群岛"></a>企鹅和长城站——南设得兰群岛</h2><p>第一次遇到企鹅就在航行一天半以后,事实上南设得兰群岛距离真正意义上的南极还有不近的距离,但是也足够激动了,这里碰到的企鹅主要是金图企鹅和帽带企鹅,企鹅的密度实在太高导致距离五米的原则完全不可能实现,因此在这里我也是第一次与企鹅零距离接触,准确地说,是被一些好奇的企鹅啄得到处跑~~下面这张图里就同时出现了两种企鹅的身影</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-574dffe943d28469.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="金图企鹅和带帽企鹅"></p>
<p>事实上,南设得兰群岛并没有南极的感觉,因为并没有什么雪。不过企鹅的数量依然让我们吃惊,可以说,要不是企鹅生活在南极,它们根本不能算是保护动物。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-0305dadece0054cf.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="到处都是企鹅"></p>
<p>南设得兰群岛对于中国人有独特的情节,因为第一个南极科考站长城站就在上面,并且我们有幸被允许一探究竟。就上一张全景图,因为全图太大,可以在这里看<a href="https://cl.ly/2o2Q2P1T3W25" target="_blank" rel="external">原图</a></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-cad8700ead339104.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="长城站全景图"></p>
<p>其实还有长城站站长和一些室内的图,不知道能不能上传,就先不放了,不过长城站站wifi可以正常上朋友圈让同来的朋友们非常满意。这里还有一个很有意思的事情,在长城站那里有联通的信号,如果是联通手机的话就可以直接打电话给国内,而且按上海的费率收费,业界良心!</p>
<h2 id="一些日常——航行、登陆、巡航"><a href="#一些日常——航行、登陆、巡航" class="headerlink" title="一些日常——航行、登陆、巡航"></a>一些日常——航行、登陆、巡航</h2><p>说是南极游,其实大多数时间都在船上,只是每天会有两次登陆或者冲锋舟巡航活动。下面就直接上图了</p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-50ffa6c9336a445e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="排队跳上冰面的企鹅"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-d4fa8ffba1cf8aa7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-0a675cb41acf5c6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-ca3cbfdf3b718063.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-3ac9b0b798437821.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="如果天气好的话随便拍一张,不加滤镜,就是一张明信片封面"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-9956a44722ee20cc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="趴在雪上休息的企鹅,他们可能为了积攒能量,换毛需要巨大的能量"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-0b26f43c1879a362.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="碰到过好几种鲸鱼,鲸鱼在船舷边喷水的场面非常震撼"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-708012ada759aca4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="遇到了庞洛公司同一型号的另一艘船,对方围绕我们两周,互相鸣笛致意"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-ea9fa827a0b56146.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="英国售卖纪念品的地方,明信片加邮递服务只要2美元"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-fe231251d031d9cc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="船上舞蹈队的演出"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-451cd0c1943b7bdc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="在雪地上拉琴的小姐姐"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-3975f418c6d604d8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="冰面上的酒会"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-59e26c5ecc7733bc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="船上探险队员的讲座,哈利波特电影里的猫头鹰全部由他训练"></p>
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>这次旅行有巨大冰山,有各种海洋生物,有各种有意思的人,用我在朋友圈里说的一句话作为结束吧</p>
<p><strong>长城站、企鹅、冰川、海豹,满足了我对南极的所有期待</strong></p>
<p><img src="http://upload-images.jianshu.io/upload_images/3078475-d2528693c5c50bdc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
]]></content>
</entry>
<entry>
<title><![CDATA[python抓取教务网成绩]]></title>
<url>http://pengtianhao.com/2017/01/31/python%E6%8A%93%E5%8F%96%E6%95%99%E5%8A%A1%E7%BD%91%E6%88%90%E7%BB%A9/</url>
<content type="html"><![CDATA[<blockquote>
<p>最近想抓取一下教务网成绩,刚好学习了python,就用它来实现</p>
</blockquote>
<a id="more"></a>
<h1 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h1><ol>
<li>基本的JavaScript、html知识,用于分析网站和网络请求</li>
<li>python</li>
<li>chrome 或者其他浏览器的开发者工具</li>
<li>浙江工商大学学生账号密码</li>
</ol>
<h1 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h1><h2 id="发起请求并获取信息"><a href="#发起请求并获取信息" class="headerlink" title="发起请求并获取信息"></a>发起请求并获取信息</h2><p>最后打印的html就是网页的源代码或是接口返回的信息</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">import urllib</div><div class="line">import urllib2</div><div class="line">import cookielib</div><div class="line">import re</div><div class="line">import sys</div><div class="line">import zlib</div><div class="line"></div><div class="line">self<span class="selector-class">.loginUrl</span> = <span class="string">'http://124.160.64.163/jwglxt/xtgl/login_login.html'</span></div><div class="line">self<span class="selector-class">.cookies</span> = cookielib.CookieJar()</div><div class="line">self<span class="selector-class">.postdata</span> = urllib.urlencode({</div><div class="line"> <span class="string">'yhm'</span>:self<span class="selector-class">.username</span>,</div><div class="line"> <span class="string">'mm'</span>:self<span class="selector-class">.mypassword</span>,</div><div class="line"> <span class="string">'yzm'</span>:<span class="string">''</span></div><div class="line">})</div><div class="line">self<span class="selector-class">.opener</span> = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))</div><div class="line">request = urllib2.Request(</div><div class="line"> url = self<span class="selector-class">.loginUrl</span>,</div><div class="line"> data = self.postdata)</div><div class="line">request.add_header(<span class="string">'Accept-encoding'</span>, <span class="string">'gzip'</span>)</div><div class="line">response = self<span class="selector-class">.opener</span><span class="selector-class">.open</span>(request)</div><div class="line"><span class="selector-tag">html</span> = response.read()</div><div class="line">print html</div></pre></td></tr></table></figure>
<h2 id="分析请求"><a href="#分析请求" class="headerlink" title="分析请求"></a>分析请求</h2><h3 id="第一步-——-登录"><a href="#第一步-——-登录" class="headerlink" title="第一步 —— 登录"></a>第一步 —— 登录</h3><p>教务网的入口网址是</p>
<p><code>http://124.160.64.163/jwglxt/xtgl/login_login.html</code></p>
<ul>
<li><p>打开chorme的<code>developer tools</code></p>
</li>
<li><p>在登录页面输入账号密码,点击登录<img src="https://cl.ly/2e3Q1b362I2K" alt=""></p>
</li>
<li><p>此时查看developer tools里面的<code>network</code>选项,<code>hearder</code>里的<code>form data</code>告诉我们登录请求的参数是<code>yhm/mm/yzm</code>,其中yzm传空<img src="https://d17oy1vhnax1f7.cloudfront.net/items/25421L3U051j2W0c2702/A4E301AF-3684-4547-8BB9-94319AD78C6A.png?v=405cebbf" alt=""></p>
</li>
</ul>
<h3 id="第二步-——-通知页面"><a href="#第二步-——-通知页面" class="headerlink" title="第二步 —— 通知页面"></a>第二步 —— 通知页面</h3><p>成功登录以后就要跳转到通知页面,这时候我们需要知道点击『已阅读』按钮以后发生了什么,那就继续观察,发现请求网址是<code>http://124.160.64.163/jwglxt/xtgl/login_loginIndex.html</code>,没有参数,这时候点击『知道了』按钮,跳转到主页。这时候地址栏显示为<code>http://124.160.64.163/jwglxt/xtgl/index_initMenu.html?t=</code>,但是事实上如果通过这个网址发送参数也会失败,再次分析请求,发现实际上点击按钮之后,登录的是<code>http://124.160.64.163/jwglxt/xtgl/login_loginIndex.html</code>,没有参数。通过这个地址,我们就可以得到主页面的内容。</p>
<h3 id="第三步-——-主页面"><a href="#第三步-——-主页面" class="headerlink" title="第三步 —— 主页面"></a>第三步 —— 主页面</h3><p>加载到主页面以后,就开始比较复杂了,这时候我们需要跳转到成绩查询页面,因为新开了一个页面,所以看似是直接跳转新页面的网址<code>http://124.160.64.163/jwglxt/xtgl/init_cxGnPage.html</code>就可以,但是实际操作发现,会报<code>HTTP请求参数gnmkdm不能为空!</code>的错误。因此我们需要分析主页的源代码, 发现了这么一段<code>,于是发现这其实是需要向接口发送特定参数来获得不同的页面。这时候我的请求其实是</code>, 这个操作可以写死。</p>
<h3 id="第四步-——-请求成绩"><a href="#第四步-——-请求成绩" class="headerlink" title="第四步 —— 请求成绩"></a>第四步 —— 请求成绩</h3><p>加载好成绩页面之后,我们就需要告诉服务器我们需要的请求的学年和学期,那么至少有两个参数<code>学期</code>和<code>学年</code>,但是通过分析发现,除了这些还需要的参数包括每一页显示几张,当前多少页已经一些排序相关的参数,这些对我们不重要,只需要依样画葫芦就可以了。这其中有一个叫做<code>nd</code>的参数让我非常头疼,不过好在后来发现直接抄录也没有问题,惊险过关。</p>
<h2 id="获取成绩"><a href="#获取成绩" class="headerlink" title="获取成绩"></a>获取成绩</h2><p>这时候已经分析完毕,只欠代码实现了,但是首先我们需要知道,学校服务器是通过cookie来记录登录状态的,所以我们只需要保存一份cookie就可以保持登录。</p>
<h3 id="登录"><a href="#登录" class="headerlink" title="登录"></a>登录</h3><figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">self<span class="selector-class">.username</span> = username</div><div class="line">self<span class="selector-class">.mypassword</span> = password</div><div class="line">self<span class="selector-class">.loginUrl</span> = <span class="string">'http://124.160.64.163/jwglxt/xtgl/login_login.html'</span></div><div class="line">self<span class="selector-class">.cookies</span> = cookielib.CookieJar()</div><div class="line">self<span class="selector-class">.postdata</span> = urllib.urlencode({</div><div class="line"> <span class="string">'yhm'</span>:self<span class="selector-class">.username</span>,</div><div class="line"> <span class="string">'mm'</span>:self<span class="selector-class">.mypassword</span>,</div><div class="line"> <span class="string">'yzm'</span>:<span class="string">''</span></div><div class="line">})</div><div class="line">self<span class="selector-class">.opener</span> = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))</div><div class="line">request = urllib2.Request(</div><div class="line">url = self<span class="selector-class">.loginUrl</span>,</div><div class="line">data = self.postdata)</div><div class="line">request.add_header(<span class="string">'Accept-encoding'</span>, <span class="string">'gzip'</span>)</div><div class="line">response = self<span class="selector-class">.opener</span><span class="selector-class">.open</span>(request)</div></pre></td></tr></table></figure>
<p>只有在登录这一步的时候需要新建一个cookie,其他步骤直接调用就可以了</p>
<h3 id="通知页面"><a href="#通知页面" class="headerlink" title="通知页面"></a>通知页面</h3><p>从这里开始直接写url和参数</p>
<p><code>http://124.160.64.163/jwglxt/xtgl/login_loginIndex.html</code></p>
<figure class="highlight lasso"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">self</span>.postdata = <span class="literal">None</span></div></pre></td></tr></table></figure>
<h3 id="主页面"><a href="#主页面" class="headerlink" title="主页面"></a>主页面</h3><p><code>http://124.160.64.163/jwglxt/xtgl/init_cxGnPage.html</code></p>
<figure class="highlight elixir"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">mkName = <span class="string">'学生成绩查询'</span></div><div class="line"><span class="keyword">self</span>.postdata = urllib.urlencode({</div><div class="line"> <span class="string">'gnmkdm'</span><span class="symbol">:<span class="string">'N305005'</span></span>,</div><div class="line"> <span class="string">'dyym'</span><span class="symbol">:<span class="string">'/cjcx/cjcx_cxDgXscj.html'</span></span>,</div><div class="line"> <span class="string">'gnmkmc'</span><span class="symbol">:urllib2</span>.<span class="keyword">quote</span>(mkName.encode(<span class="string">"utf-8"</span>)),</div><div class="line"> <span class="string">'sfgnym'</span><span class="symbol">:<span class="string">'null'</span></span></div><div class="line">})</div></pre></td></tr></table></figure>
<h3 id="成绩查询页"><a href="#成绩查询页" class="headerlink" title="成绩查询页"></a>成绩查询页</h3><p><code>http://124.160.64.163/jwglxt/cjcx/cjcx_cxDgXscj.html?doType=query&gnmkdmKey=N305005&sessionUserKey='+self.username</code></p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> semester == <span class="string">'1'</span>:</div><div class="line"> semester = <span class="string">'3'</span></div><div class="line">elif semester == <span class="string">'2'</span>:</div><div class="line"> semester = <span class="string">'12'</span></div><div class="line"><span class="keyword">self</span>.postdata = urllib.urlencode({</div><div class="line"> <span class="string">'xnm'</span><span class="symbol">:year</span>,</div><div class="line"> <span class="string">'xqm'</span><span class="symbol">:semester</span>,</div><div class="line"> <span class="string">'nd'</span><span class="symbol">:<span class="string">'1485587502806'</span></span>,</div><div class="line"> <span class="string">'queryModel.showCount'</span><span class="symbol">:<span class="string">'1000'</span></span>,</div><div class="line"> <span class="string">'queryModel.currentPage'</span><span class="symbol">:<span class="string">'1'</span></span>,</div><div class="line"> <span class="string">'queryModel.sortName'</span><span class="symbol">:<span class="string">''</span></span>,</div><div class="line"> <span class="string">'queryModel.sortOrder'</span><span class="symbol">:<span class="string">'asc'</span></span>,</div><div class="line"> <span class="string">'time'</span><span class="symbol">:<span class="string">'1'</span></span></div><div class="line">})</div></pre></td></tr></table></figure>
<p>由此就拿到了成绩的json串</p>
<h2 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h2><p>拿到json串之后就需要解析并且处理显示想要的结果,非常简单,只是考虑到学生有重修的情况,需要记得相同科目取最大值列入计算。</p>
<figure class="highlight zephir"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">data = json.loads(html)</div><div class="line">countAll = <span class="number">0</span></div><div class="line">countGpaAll = <span class="number">0</span></div><div class="line">countAllDefualt = <span class="number">0</span></div><div class="line">mydic = {}</div><div class="line"></div><div class="line"><span class="keyword">if</span> len(data[<span class="string">'items'</span>]) == <span class="number">0</span>:</div><div class="line"><span class="keyword">print</span> <span class="string">'No data'</span></div><div class="line"><span class="keyword">exit</span>(<span class="number">0</span>)</div><div class="line"></div><div class="line"><span class="keyword">for</span> x in data[<span class="string">'items'</span>]:</div><div class="line"> <span class="keyword">if</span> mydic.has_key(x[<span class="string">'kcmc'</span>]):</div><div class="line"> <span class="keyword">if</span> <span class="keyword">float</span>(mydic[x[<span class="string">'kcmc'</span>]])<<span class="keyword">float</span>(x[<span class="string">'cj'</span>]):</div><div class="line"> countAllDefualt-=<span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounterDefault(mydic[x[<span class="string">'kcmc'</span>]]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> countAllDefualt+=<span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounterDefault(x[<span class="string">'cj'</span>]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> countAll-=<span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounter(mydic[x[<span class="string">'kcmc'</span>]]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> countAll+=<span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounter(x[<span class="string">'cj'</span>]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> mydic[x[<span class="string">'kcmc'</span>]] = x[<span class="string">'cj'</span>]</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> mydic[x[<span class="string">'kcmc'</span>]] = x[<span class="string">'cj'</span>]</div><div class="line"> countAll+= <span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounter(x[<span class="string">'cj'</span>]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> countAllDefualt+= <span class="keyword">float</span>(<span class="keyword">self</span>.gpaCounterDefault(x[<span class="string">'cj'</span>]))*<span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"> countGpaAll += <span class="keyword">float</span>(x[<span class="string">'xf'</span>])</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">for</span> z in mydic:</div><div class="line"> <span class="keyword">print</span> <span class="string">' '</span>+z+<span class="string">' '</span>+mydic[z]</div><div class="line"></div><div class="line"><span class="keyword">print</span> <span class="string">'\n学分(基于wes算法): '</span>+ str(countAll/countGpaAll)</div><div class="line"><span class="keyword">print</span> <span class="string">'\n学分(基于标准算法): '</span>+ str(countAllDefualt/countGpaAll)</div></pre></td></tr></table></figure>
<p>由此就实现了从教务网查询成绩并且打印学分</p>
<h1 id="命令行参数"><a href="#命令行参数" class="headerlink" title="命令行参数"></a>命令行参数</h1><p>现在这个程序还差灵活性,最好是通过命令行参数来录入账号密码和学期学年,并且学期学年可以缺省,这时候就需要<code>argparse</code>这个库来帮助了,使用也非常简单。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">parser = argparse.ArgumentParser(description='')</div><div class="line">parser.add_argument('-u', dest='username', type=str, <span class="keyword">help</span>=<span class="string">'Your username'</span>,<span class="keyword">required</span> = <span class="literal">True</span>)</div><div class="line">parser.add_argument(<span class="string">'-p'</span>, dest=<span class="string">'password'</span>, <span class="keyword">type</span>=<span class="keyword">str</span>, <span class="keyword">help</span>=<span class="string">'Your password'</span>,<span class="keyword">required</span> = <span class="literal">True</span>)</div><div class="line">parser.add_argument(<span class="string">'-y'</span>, dest=<span class="string">'year'</span>, <span class="keyword">type</span>=<span class="keyword">str</span>, <span class="keyword">help</span>=<span class="string">'Your school year,like [2016]'</span>,<span class="keyword">required</span> = <span class="literal">False</span>,<span class="keyword">default</span>=<span class="string">''</span>)</div><div class="line">parser.add_argument(<span class="string">'-s'</span>, dest=<span class="string">'semester'</span>, <span class="keyword">type</span> =<span class="keyword">str</span>, <span class="keyword">help</span>=<span class="string">'Your semester [1]or[2]'</span>,<span class="keyword">required</span> = <span class="literal">False</span>,<span class="keyword">default</span>=<span class="string">''</span>)</div><div class="line"></div><div class="line">myUserName = <span class="keyword">None</span></div><div class="line">myPassword = <span class="keyword">None</span></div><div class="line">myYear = <span class="keyword">None</span></div><div class="line">mySemester = <span class="keyword">None</span></div><div class="line"></div><div class="line">try:</div><div class="line"> options = parser.parse_args()</div><div class="line"> myUserName = options.username</div><div class="line"> myPassword = options.password</div><div class="line"> myYear = options.year</div><div class="line"> mySemester = options.semester</div><div class="line"><span class="keyword">except</span>:</div><div class="line"> print(parser.parse_args([<span class="string">'-h'</span>]))</div><div class="line"> <span class="keyword">exit</span>(<span class="number">0</span>)</div></pre></td></tr></table></figure>
<h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><p><img src="https://d3uepj124s5rcx.cloudfront.net/items/2I0Z3A2c1g1K3I2E3e2O/IMG_1688.JPG?v=d3d0dfd8" alt=""></p>
<h1 id="完整代码(含bug修复"><a href="#完整代码(含bug修复" class="headerlink" title="完整代码(含bug修复)"></a>完整代码(含bug修复)</h1><p><a href="https://github.com/pthtc/ZJGSU-GPACounter" target="_blank" rel="external">ZJGSU-GPACounter</a></p>
]]></content>
</entry>
<entry>
<title><![CDATA[快速实现基于Python的微信聊天机器人]]></title>
<url>http://pengtianhao.com/2017/01/23/%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E5%9F%BA%E4%BA%8EPython%E7%9A%84%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA/</url>
<content type="html"><![CDATA[<blockquote>
<p>最近听说一个很好玩的图灵机器人api,正好可以用它做一个微信聊天机器人,下面是实现</p>
</blockquote>
<a id="more"></a>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># test.py</span></div><div class="line"></div><div class="line"><span class="keyword">import</span> requests</div><div class="line"><span class="keyword">import</span> itchat <span class="comment">#这是一个用于微信回复的库</span></div><div class="line"></div><div class="line">KEY = <span class="string">'8edce3ce905a4c1dbb965e6b35c3834d'</span> <span class="comment">#这个key可以直接拿来用</span></div><div class="line"></div><div class="line"><span class="comment"># 向api发送请求</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_response</span><span class="params">(msg)</span>:</span></div><div class="line"> apiUrl = <span class="string">'http://www.tuling123.com/openapi/api'</span></div><div class="line"> data = {</div><div class="line"> <span class="string">'key'</span> : KEY,</div><div class="line"> <span class="string">'info'</span> : msg,</div><div class="line"> <span class="string">'userid'</span> : <span class="string">'pth-robot'</span>,</div><div class="line"> }</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> r = requests.post(apiUrl, data=data).json()</div><div class="line"> <span class="keyword">return</span> r.get(<span class="string">'text'</span>)</div><div class="line"> <span class="keyword">except</span>:</div><div class="line"> <span class="keyword">return</span></div><div class="line"></div><div class="line"><span class="comment"># 注册方法</span></div><div class="line">\@itchat.msg_register(itchat.content.TEXT)</div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">tuling_reply</span><span class="params">(msg)</span>:</span></div><div class="line"> <span class="comment"># 为了保证在图灵Key出现问题的时候仍旧可以回复,这里设置一个默认回复</span></div><div class="line"> defaultReply = <span class="string">'I received: '</span> + msg[<span class="string">'Text'</span>]</div><div class="line"> <span class="comment"># 如果图灵Key出现问题,那么reply将会是None</span></div><div class="line"> reply = get_response(msg[<span class="string">'Text'</span>])</div><div class="line"> <span class="comment"># a or b的意思是,如果a有内容,那么返回a,否则返回b</span></div><div class="line"> <span class="keyword">return</span> reply <span class="keyword">or</span> defaultReply</div><div class="line"></div><div class="line"><span class="comment"># 为了让修改程序不用多次扫码,使用热启动</span></div><div class="line">itchat.auto_login(hotReload=<span class="keyword">True</span>)</div><div class="line">itchat.run()</div></pre></td></tr></table></figure>
<p>如果要让这个机器人永远运行,就需要上传到服务器,用<code>screen</code>指令新开一个窗口,运行<code>python3 test.py</code>,这时候就会在同一个目录下生成一个<code>QR.jpg</code>文件,但是因为一般我们是用ssh连接服务器,没有图像,所以需要用<code>scp</code>指令,下载到本地之后,用手机扫码,这样工作就完成了</p>
<p>效果如下:</p>
<p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/0s1B152M3H45092S0v3q/IMG_0591.PNG?v=380fe5c0" alt=""></p>
]]></content>
</entry>
<entry>
<title><![CDATA[计算机网络笔记]]></title>
<url>http://pengtianhao.com/2016/11/07/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<blockquote>
<p>计算机网络的课堂笔记</p>
</blockquote>
<a id="more"></a>
<h2 id="协议图"><a href="#协议图" class="headerlink" title="协议图"></a>协议图</h2><p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/463z3S3E1A3o04323l0K/1366100130_5972.jpg?v=34777df0" alt=""></p>
<p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/1T0H2M0I1d2F0i36063O/0_1325744597WM32.gif?v=e4566c46" alt=""></p>
<h2 id="公式"><a href="#公式" class="headerlink" title="公式"></a>公式</h2><h3 id="最小帧长计算"><a href="#最小帧长计算" class="headerlink" title="最小帧长计算"></a>最小帧长计算</h3><p><code>最小帧长 = 2*总传播时间*传播速率 = 2*(网络跨距/传播速率+ 处理延迟)*传输速率</code></p>
<h3 id="香农定理"><a href="#香农定理" class="headerlink" title="香农定理"></a>香农定理</h3><ul>
<li>信噪比 <code>db = 10*lg(S/N)</code></li>
<li>最大传输速率 <code>C = Wlog2(1+S/N)</code> W——信道带宽</li>
</ul>
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><ul>
<li><p><strong>网络</strong> 由节点和链路组成 <strong>点和线都很重要</strong></p>
</li>
<li><p><strong>计算机网络</strong> 一个互相连接、自治的计算机集合</p>
</li>
<li><p><strong>互联网(internet)</strong> 网络的网络,使用任意协议</p>
</li>
<li><p><strong>英特网(Internet)</strong> 一种互联网,使用TCP/IP协议,世界上最大的计算机网络</p>
</li>
<li><p><strong>以太网</strong> 采用CSMA/CD协议的局域网</p>
</li>
<li><p><strong>上网</strong> 通过ISP获得IP地址</p>
</li>
<li><p><strong>协议</strong> 为网络数据交换而建立的规则</p>
</li>
<li><p><strong>交换机</strong> 为接入交换机的任意两个网络节点提供独享的电信号通路,其实就是<strong>分配信号的东西</strong></p>
</li>
<li><p><strong>路由</strong> 分组从源到目的地时,决定端到端路径的网络范围的进程,<strong>家里的那种其实是交换机和路由器的集合</strong></p>
</li>
<li><p><strong>路由器</strong> 节点交换机</p>
</li>
<li><p><strong>WAN</strong> 广域网</p>
</li>
<li><p><strong>LAN</strong> 局域网</p>
</li>
<li><p><strong>ISP</strong> 因特网服务供应商,有申请IP地址的能力,比如<strong>电信、移动、联通</strong></p>
</li>
<li><p><strong>IP</strong> 网络协议</p>
</li>
<li><p><strong>IXP</strong> 互联网交换点,为了低级ISP之间转发方便设置</p>
</li>
<li><p><strong>TCP</strong> 传输控制协议,保证信息传达完整,比较慢</p>
</li>
<li><p><strong>UDP</strong> 用户数据报协议,不保证信息转达完整,比较快</p>
</li>
<li><p><strong>ICP</strong> 网络内容服务商</p>
</li>
<li><p><strong>时延(延迟)</strong> 数据从网络一端到另一端所需的时间,转发中要排队造成<br><code>总时延 = 发送时延(主机发送数据帧的时间)+传播时延(电磁波在信道中传播一定距离的时间)+处理时延+排队时延</code></p>
</li>
</ul>
<h3 id="英特网的组成"><a href="#英特网的组成" class="headerlink" title="英特网的组成"></a>英特网的组成</h3><h4 id="互联网边缘部分"><a href="#互联网边缘部分" class="headerlink" title="互联网边缘部分"></a>互联网边缘部分</h4><p>进行由各种终端组成(PC/某个ISP/互联网摄像头等)</p>
<ul>
<li><p><strong>客户-服务器通信(C/S)</strong> 客户(Client)和服务器(Server)都指进程</p>
</li>
<li><p><strong>对等通信(P2P)</strong> </p>
</li>
</ul>
<h5 id="服务器软件"><a href="#服务器软件" class="headerlink" title="服务器软件"></a>服务器软件</h5><p>一种同时处理多个远程和本地客户的请求的程序</p>
<ul>
<li><strong>一直运行</strong></li>
<li><strong>被动等待</strong></li>
</ul>
<h4 id="互联网核心部分"><a href="#互联网核心部分" class="headerlink" title="互联网核心部分"></a>互联网核心部分</h4><p>由网络和路由器等组成,起特殊作用的是<strong>路由器</strong>,用于转发收到的分组</p>
<h5 id="路由器"><a href="#路由器" class="headerlink" title="路由器"></a>路由器</h5><p><strong>路由器的输入输出端口间没有直接连线</strong></p>
<p>路由器处理分组的过程:</p>
<ul>
<li><p>收到的分组放入缓存</p>
</li>
<li><p>查找转发表</p>
</li>
<li><p>把分组送到合适的端口转发出去</p>
</li>
</ul>
<h5 id="各种交换"><a href="#各种交换" class="headerlink" title="各种交换"></a>各种交换</h5><ul>
<li><p><strong>交换</strong> 建立连接——通信——释放连接</p>
</li>
<li><p><strong>分组</strong> 将数据打包分块,也就是<strong>包</strong>,分组的头部叫做<strong>包头</strong></p>
</li>
</ul>
<hr>
<ul>
<li><p><strong>分组交换</strong> 单个分组传送到节点,储存下来后查找转发表,转发下一个节点</p>
</li>
<li><p><strong>报文交换</strong> 整个报文先传送到相邻节点,全部储存下来后查找转发表,转发下一个节点</p>
</li>
<li><p><strong>电路交换</strong> 持续地连续从源点直达终点,一直占用资源</p>
</li>
</ul>
<h3 id="协议"><a href="#协议" class="headerlink" title="协议"></a>协议</h3><h4 id="协议的组成"><a href="#协议的组成" class="headerlink" title="协议的组成"></a>协议的组成</h4><ul>
<li><strong>语法</strong> 格式结构</li>
<li><strong>语义</strong> 定义发出的信息和应该做出的响应</li>
<li><strong>同步</strong> 事件实现顺序</li>
</ul>
<h4 id="五级体系结构"><a href="#五级体系结构" class="headerlink" title="五级体系结构"></a>五级体系结构</h4><ul>
<li><p><strong>物理层</strong> 网线等,与链路层组成<code>局域网</code></p>
</li>
<li><p><strong>链路层</strong> 交换机、路由器等发挥作用的地方</p>
</li>
<li><p><strong>网络层</strong> IP</p>
</li>
<li><p><strong>运输层</strong> TCP/UDP</p>
</li>
<li><p><strong>应用层</strong> DNS/FTP/HTTP/URL/SMTP/POP3/IMAP等,交换的数据单元叫<strong>报文</strong></p>
</li>
</ul>
<h4 id="TCP-IP"><a href="#TCP-IP" class="headerlink" title="TCP/IP"></a>TCP/IP</h4><p><strong>体系结构</strong> 将五级结构的物理层和链路层合并</p>
<p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/0W3I081S0r1m3Y3C0c2h/020009362228293.png" alt=""></p>
<p><strong>沙漏型</strong> 应用层和网络接口层都有很多协议,IP层很小,所以各种协议都向下汇聚到IP协议中</p>
<p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/0V0k0k3M440i3Y0O0F2K/d6ca7bcb0a46f21fd656b015f7246b600c33ae12.jpg" alt=""></p>
<h2 id="物理层"><a href="#物理层" class="headerlink" title="物理层"></a>物理层</h2><p>物理层考虑的是</p>
<ul>
<li><strong>如何在链接各种计算机的传输媒体上传输数据bit流</strong></li>
<li><strong>如何使数据链层无法感受到传输媒体和通信手段的差异</strong></li>
</ul>
<p>通过<strong>串行传输</strong>传输数据(成本考虑)</p>
<h3 id="数据通信"><a href="#数据通信" class="headerlink" title="数据通信"></a>数据通信</h3><h4 id="组成成分"><a href="#组成成分" class="headerlink" title="组成成分"></a>组成成分</h4><ul>
<li><strong>源系统:</strong> 源点、发送器</li>
<li><strong>传输系统</strong></li>
<li><strong>目的系统:</strong> 接收器、终点</li>
</ul>
<h4 id="一些概念"><a href="#一些概念" class="headerlink" title="一些概念"></a>一些概念</h4><ul>
<li><strong>消息</strong> 数据的本来面目(语音、图像)</li>
<li><strong>数据</strong> 有意义的符号序列(01010?)</li>
<li><strong>信号</strong> 电磁表现(1.<strong>模拟信号:</strong> 取值连续 2. <strong>数字信号:</strong>取值离散)</li>
<li><strong>信道</strong> 单向数据通路,通信电路包含两条信道(接收、发送)</li>
<li><strong>单向通信</strong> 没有方向的通信,不期望回复(广播、电视)</li>
<li><strong>双向交替通信</strong> 字面意思,同一时间一方只能接收或者发送,需要两条信道</li>
<li><strong>双向同时通信</strong> 字面意思,两条信道</li>
<li><strong>调幅AM</strong> 振幅</li>
<li><strong>调频FM</strong> 频率</li>
<li><strong>调相</strong> 初始相位</li>
<li><strong>码间串扰</strong> 码元传输速率太高导致接收到的信号失去了清晰的界限</li>
<li><strong>信噪比(S/N)</strong> 信号平均功率和噪声平均功率之比,单位是分贝<br> <code>信噪比(db) = 10*lg(S/N)</code> </li>
<li><strong>极限传输速率(香浓定力)</strong> <code>C = Wlog2(1+S/N)</code> W-信道带宽</li>
</ul>
<h4 id="传输媒体"><a href="#传输媒体" class="headerlink" title="传输媒体"></a>传输媒体</h4><h5 id="引导型"><a href="#引导型" class="headerlink" title="引导型"></a>引导型</h5><ul>
<li><strong>双绞线</strong></li>
<li><strong>同轴电缆</strong></li>
<li><strong>光缆</strong></li>
</ul>
<h5 id="非引导型"><a href="#非引导型" class="headerlink" title="非引导型"></a>非引导型</h5><p>各种无线电波通信</p>
<h4 id="信道复用技术"><a href="#信道复用技术" class="headerlink" title="信道复用技术"></a>信道复用技术</h4><h5 id="频分复用、时分复用、统计时分复用"><a href="#频分复用、时分复用、统计时分复用" class="headerlink" title="频分复用、时分复用、统计时分复用"></a>频分复用、时分复用、统计时分复用</h5><ul>
<li><strong>频分复用FDM</strong> 所有用户占用不同的频带(频率区域)</li>
<li><strong>时分复用TDM</strong> 每个用户在TDM帧中占用固定序号的时隙</li>
<li><strong>统计时分复用STDM</strong> 低速信号充满缓存之后发送,保证利用率</li>
<li><strong>波分复用</strong> 就是光的频分复用</li>
<li><strong>码分复用</strong> </li>
<li><strong>复用器、分用器</strong> 见图</li>
</ul>
<p><img src="https://d17oy1vhnax1f7.cloudfront.net/items/0X2G1j3I1g2d030b3r1b/003shoMnzy6NmKBEc6X2f&690.png?v=3553ba49" alt=""> </p>
<h2 id="数据链路层"><a href="#数据链路层" class="headerlink" title="数据链路层"></a>数据链路层</h2><ul>
<li><p><strong>点对点信道</strong> </p>
</li>
<li><p><strong>广播信道</strong> </p>
</li>
<li><p><strong>链路</strong> 一个节点到相邻点的一段物理线路(有线或无线)</p>
</li>
<li><p><strong>数据链路</strong> 通过通信协议控制数据的软硬件</p>
</li>
</ul>
<h3 id="点对点信道的数据链路层"><a href="#点对点信道的数据链路层" class="headerlink" title="点对点信道的数据链路层"></a>点对点信道的数据链路层</h3><ol>
<li>节点A把网络层给的IP数据报添加首尾封装成帧</li>
<li>A把帧发送给B</li>
<li>B确认无差错,就从帧中提出IP数据报给网络层</li>
</ol>
<h3 id="封装成帧"><a href="#封装成帧" class="headerlink" title="封装成帧"></a>封装成帧</h3><p>在一段数据前后添加首部和尾部,用于<strong>帧界定</strong>(如果首尾不全,那么就是垃圾数据,应该丢弃)</p>
<p><strong>每个帧长度要小于最大传送单元MTU</strong></p>
<p>特殊:ASCII要用SOH(00000001)和EOT(00000100)界定</p>
<h3 id="透明传输"><a href="#透明传输" class="headerlink" title="透明传输"></a>透明传输</h3><ul>
<li><p><strong>定义:</strong> 保证<strong>无论哪种数据都能完好无损地传输(即使和界定符长得一样)</strong></p>
</li>
<li><p><strong>手段:</strong> 通过在字节前加转义字符(ESC) </p>
</li>