-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
384 lines (322 loc) · 181 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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[test_draft_publish_without_layout_variable]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F22%2Ftest-draft-publish-without-layout-variable%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F22%2Ftest-draft-publish-without-layout-variable%2F</url>
-->
<url>/2016%2F12%2F22%2Ftest-draft-publish-without-layout-variable%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[test_draft_publish]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F22%2Ftest-draft-publish-1%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F22%2Ftest-draft-publish-1%2F</url>
-->
<url>/2016%2F12%2F22%2Ftest-draft-publish-1%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[在python里通过myql访问远程服务器数据库配置memo]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F21%2F%E5%9C%A8python%E9%87%8C%E9%80%9A%E8%BF%87myql%E8%AE%BF%E9%97%AE%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AEmemo%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F21%2F%E5%9C%A8python%E9%87%8C%E9%80%9A%E8%BF%87myql%E8%AE%BF%E9%97%AE%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AEmemo%2F</url>
-->
<url>/2016%2F12%2F21%2F%E5%9C%A8python%E9%87%8C%E9%80%9A%E8%BF%87myql%E8%AE%BF%E9%97%AE%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AEmemo%2F</url>
<content type="text"><![CDATA[mac安装mysqlmac:brew install mysql 12345678910111213==> CaveatsWe've installed your MySQL database without a root password. To secure it run: mysql_secure_installationTo connect run: mysql -urootTo have launchd start mysql now and restart at login: brew services start mysqlOr, if you don't want/need a background service you can just run: mysql.server start==> Summary🍺 /usr/local/Cellar/mysql/5.7.13: 13,344 files, 445.0M ubuntu:sudo apt-get-install mysql-server mysql-clientsudo apt-get-install libmysqlclient-dev# 为了支持python,有依赖 #reference-How to get the MySQL C API libraries on OS Xmysql-server与mysql-client有什么区别? 在 Mac 下用 Homebrew 安装 MySQL python支持mac:pip install mysqlclient/easy_install mysql-pythonubuntu:pip install mysqlclient #reference-MySQL database connector for Python#reference-有哪些比较好的在 Python 中访问 MySQL 的类库? 使用import mysqldb 可能遇见的问题连接远程服务器返回ERROR 2003 (HY000): Can't connect to MySQL server原因就是Mysql数据库的默认配置文件my.cnf(linux下)中的bind-address默认为127.0.0.1,即只允许本机访问,需要取消该限制。 ubuntu下使用locate my.cnf获取文件地址,如果返回locate: can not stat ()/var/lib/mlocate/mlocate.db’:,执行updatedb解决。 [locate: can not stat ()/var/lib/mlocate/mlocate.db’: No such file or directory](http://blog.csdn.net/w13770269691/article/details/6987384)12345root@localhost:~# locate my.cnf/etc/alternatives/my.cnf/etc/mysql/my.cnf/etc/mysql/my.cnf.fallback/var/lib/dpkg/alternatives/my.cnf my.cnf一般在/etc/mysql下面,网络绝大部分资源都显示直接修改该文件。root@localhost:~# cat /etc/alternatives/my.cnf123456789101112131415161718192021## The MySQL database server configuration file.## You can copy this to one of:# - "/etc/mysql/my.cnf" to set global options,# - "~/.my.cnf" to set user-specific options.# # One can use all long options that the program supports.# Run program with --help to get a list of available options and with# --print-defaults to see which it would actually understand and use.## For explanations see# http://dev.mysql.com/doc/mysql/en/server-system-variables.html## * IMPORTANT: Additional settings that can override those from this file!# The files must end with '.cnf', otherwise they'll be ignored.#!includedir /etc/mysql/conf.d/!includedir /etc/mysql/mysql.conf.d/ 根据Why /etc/mysql/my.cnf is EMPTY,配置文件(.cnf文件,一般是mysqld.cnf)locate在/etc/mysql/conf.d/和/etc/mysql/mysql.conf.d/中,我的mysqld.cnf地址是:/etc/mysql/mysql.conf.d/mysqld.cnf 这才开始修改我的配置(mysqld.cnf)修改前的配置文件为:1234# # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 127.0.0.1 修改后:1234# # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. # bind-address = 127.0.0.1 修改后并未立即生效,需要重启生效(可以找办法restart该服务,我是直接reboot) #reference-远程连接Mysql数据库问题(ERROR 2003 (HY000)#reference-MySQL数据库无法远程连接的解决办法MySQL远程连接ERROR 2003 (HY000):Can’t connect to MySQL server on’XXXXX’的问题mac/ubuntu环境安装 连接远程服务器返回ERROR 1130 (HY000): is not allowed to connect to this MySQL server服务器连接上mysql后, 授权myuser使用mypassword从任何主机连接到mysql服务器GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'192.168.1.3' IDENTIFIED BY 'mypassword' WITH GRANT OPTION; mysql远程连接解决办法无法远程登入MySQL Server解决 ERROR 1045 (28000): Access denied for user尝试从本地连接服务器:!mysql -h'104.128.92.224' -u<%username> -p<%password>反馈mysql: [Warning] Using a password on the command line interface can be insecure.所以执行mysql -h'104.128.92.224' -uroot但返回ERROR 1045 (28000): Access denied for user,属于粗心,正确的执行方式为:`mysql -h'104.128.92.224' -u<%username> -p再输入密码即可。 遇到问题—mysql账户密码以及权限的问题完整过程解决 ERROR 1045 (28000)Ubuntu服务器常用配置-mysql数据库的安装 log问题mysql错误日志分析IP address could not be resolved: Temporary failure in name resolution mysql连接到远程服务器的思考mysql-client连接到mysql-server,通过什么协议发起请求?是否安全?看起来不是https 的Python and Connecting to MySQL over SSHConnect on remote MySQL database through Pythonpython模块paramiko与ssh #Standard Mysql DocumentationMySQLdb User’s GuidePython Database API Specification v2.0]]></content>
</entry>
<entry>
<title><![CDATA[数据科学笔记]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F19%2F%E6%95%B0%E6%8D%AE%E7%A7%91%E5%AD%A6%E7%AC%94%E8%AE%B0%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F19%2F%E6%95%B0%E6%8D%AE%E7%A7%91%E5%AD%A6%E7%AC%94%E8%AE%B0%2F</url>
-->
<url>/2016%2F12%2F19%2F%E6%95%B0%E6%8D%AE%E7%A7%91%E5%AD%A6%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[10 Minutes to pandasQuickstart tutorialSTANDARDarray — Efficient arrays of numeric values¶Python 中常用的数据类型 #了解scikit-learnGeneral examplesAn introduction to machine learning with scikit-learn]]></content>
</entry>
<entry>
<title><![CDATA[docker初探]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F18%2Fdocker%E5%88%9D%E6%8E%A2%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F18%2Fdocker%E5%88%9D%E6%8E%A2%2F</url>
-->
<url>/2016%2F12%2F18%2Fdocker%E5%88%9D%E6%8E%A2%2F</url>
<content type="text"><![CDATA[主机的概念集群的概念利用daocloud管理独立主机/kvm架构vps&docker的加速器dao命令集群和主机的管理及注意事项使用脚本命令手动添加主机Systemd 入门教程:命令篇Failed to start Docker Application Container Engine. Docker使用官方mysql镜像]]></content>
</entry>
<entry>
<title><![CDATA[test_draft_layout]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F07%2Ftest-draft-layout%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F07%2Ftest-draft-layout%2F</url>
-->
<url>/2016%2F12%2F07%2Ftest-draft-layout%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[photoe_test]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F06%2Fphotoe-test%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F06%2Fphotoe-test%2F</url>
-->
<url>/2016%2F12%2F06%2Fphotoe-test%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[draft_test]]></title>
<!--modified by Nora
<url>%2F2016%2F12%2F05%2Fdraft-test%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F12%2F05%2Fdraft-test%2F</url>
-->
<url>/2016%2F12%2F05%2Fdraft-test%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[Hexo Blog Setup以及常见配置]]></title>
<!--modified by Nora
<url>%2F2016%2F11%2F24%2FHexo-Blog-Setup%E4%BB%A5%E5%8F%8A%E5%B8%B8%E8%A7%81%E9%85%8D%E7%BD%AE%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F11%2F24%2FHexo-Blog-Setup%E4%BB%A5%E5%8F%8A%E5%B8%B8%E8%A7%81%E9%85%8D%E7%BD%AE%2F</url>
-->
<url>/2016%2F11%2F24%2FHexo-Blog-Setup%E4%BB%A5%E5%8F%8A%E5%B8%B8%E8%A7%81%E9%85%8D%E7%BD%AE%2F</url>
<content type="text"><![CDATA[img { -moz-box-shadow: 3px 3px 5px 6px #ccc; -webkit-box-shadow: 3px 3px 5px 6px #ccc; box-shadow: 3px 3px 5px 6px #ccc; } 使用Hexo进行了Blog的Setup后,我们希望选择一个心仪的theme(即不同页面类型保持一致的风格),配置需要的功能选项/属性,甚至进行一些个性化的定制。如何预览和使用心仪的theme,Google后有足够的信息,这里就不详细介绍了。Hexo默认theme是landscape。网络上也有不少对默认主题的自定义配置方案,例如,hexo的私人订制Simple is the best,毕竟不是搞设计和前端开发的。个人选择了Next作为theme,一方面偏好它的极简主义,另一方面,整体设计的留白和交互都很不错。 theme本身很优秀,但我们还是有一些个性化的需求,需要了解Hexo的更详细配置(对应next下的主题级别的配置),或,简单地修改脚本。例如: post granularity的SEO post granularity的tracking & conversation tracking 隐藏部分post,不按timeline呈现,按特定方式访问 设置404 page 新增简历 page 修改搜索的UI(未完成) … 大部分信息网络页都有,利用这篇文章简单总结,并在最后探索Hexo & Next theme的架构。 简介Hexo(site) 配置Hexo级别的配置 关于站点/项目级别的内容、样式属性。如网站标题、网站描述、网站的SEO关键词、项目Github地址等。一般是内容属性。类似meta类。 theme 配置theme级别的配置已经对基础设置,如,如何切换theme,更新profie、social link等,做了比较详细的介绍。主要是样式属性。 就像html+css把内容、样式分离开来一样,theme负责Hexo的默认展现(样式)。除了配置外的内容信息一般存放于./source目录 进阶theme级别的配置有部分用户路径是没有考虑,如404页面,搜索等。 设置404页面我们希望404并非纯粹的Cannot GET <%path>或者其它一成不变的错误信息。 我们希望有效地利用这部分流量,甚至做一些有意义的事情。例如:构建搜索的闭环;做一些公益; 新建页面执行$ hexo new page 404进入source/404编辑index.md 宝贝回家-腾讯公益 123456789101112131415161718---title: 404permalink: /404comments: false---<!DOCTYPE HTML><html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8;"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="robots" content="all" /> <meta name="robots" content="index,follow"/> </head> <body> <script type="text/javascript" src="http://www.qq.com/404/search_children.js" charset="utf-8" homePageUrl="/ " homePageName="回到我的主页"> </script> </body></html> PS: 新建page和新建post是不一样的,新建page利用hexo new page %pagename,创建后会有%pagename文件夹,文件夹有index.md,md内容为该page调用内容。 新建post利用hexo new %postname创建后有%postname.md为名的md文件,存放于source/_post。page和post属于hexo的两种layout,其它layout差异请看这儿 参考hexo变量 其他除非DNS或者其他网络异常,无法请求到custom_domain的情况,否则404页面都会正常显示网络异常导致的不能访问 设置about同理 设置RSSRSS是一个模版粒度的设置,换了模版就有必要重新设置(如果有需要)#reference-显示feed链接 设置分享插件(duoshuo)设置站内搜索利用Next主题的local search插件的时候,会出现一个特别的情况:当你在yoursite.com/<%page>做search的时候,点击搜索结果会出现异常:会navigate到yoursite.com/<%page1>/<%search_result_relativePath>出现这个问题的主要原因是站点配置/path_your_blog/source/_cofig.yml的url值配置问题,改为absolutePath可以杜绝这个情况。即,yoursite.comhttp://yoursite.com/其中最后的斜杠/不能遗漏,会导致转义出现问题 新问题:修改generator-search-dbsearch.ejs1234<!--modified by Nora<url><%- encodeURIComponent(config.root + post.path) %></url>--><url><%- config.url + config.root + encodeURIComponent( post.path) %></url> 增加绝对路径生成search.xml 再更新:确认了html 属性href作相对引用的逻辑后,修改为:因为href=’/path’会直接访问当前主域名+path(取决于浏览器) 1<url><%- config.root + encodeURIComponent( post.path) %></url> 因为使用相对路径404页面也更新为:1<script type="text/javascript" src="http://www.qq.com/404/search_children.js" charset="utf-8" homePageUrl="/ " homePageName="回到我的主页"> 其它md的相对路径也按照该逻辑处理。减少绝对路径使用,因为有多个发布地点。为了减少所有的绝对饮用,url设置为url: check_hexo_config_url 观察是否有问题。 github的包含关系。例如_post已经在一个仓库了,希望建一个新的仓库包涵旧仓库 encode& decode根据http协议,URL中的部分字符会进行转义(encode),例如中文字符,会遇见的一个问题是把/转为%2F进行页面访问。检查yoursite.com/search.xml可以发现npm install hexo-generator-searchdb --save会根据文章简历索引,并编码后访问#reference:#reference: HTML URL 编码 github做了子项目pagescoding也需要做noragithub/prd_deployment映射到nora_coding/prd_deployment,同时pages业务。两个项目都已经忽略了主域名,不做映射回出问题(coding找不到该页面)这里利用hook设置 hook需要自由服务器,结合docker的自动化常识,daoke.cluod名字是pages blog 文章配置参考hexo变量增加updated: 1482378550000 显示updatedSEO优化设置keywords页面级别配置 友链sitetracking出现在搜索引擎Faviconhttp://ww2.sinaimg.cn/mw690/6fa34428jw8e6sgfwn3suj20c80afmxk.jpg 反向代理 Github 访问速度太慢,有没有通过反向代理或者CDN加速的方式提高访问速度?特别是如果更新DNS的情况下 经过调研,最简单快速的方法是利用coding.net(墙内版Github)的pages服务设置hook自动更新 //godaddy设置www别名到pages.coding.me绑定coding pages到www.busihacker.com 定期更新问题&hook问题&父子域名、(发布到coding.me/noragithub.io)可能有的问题 https协议的支持 seo支持 自定义监测支持提示有编码错误 不支持所有页面进行local search(当对配置中 search- field从post修改为all,会提醒post is not defined ) updatetime function 动动手指,NexT主题与Hexo更搭哦(基础篇)hexo + gitpages 搭建博客Hexo 主题制作指南 定制化(进阶)Hexo的布局(layout)默认布局-postlayout(布局)概念类似一种页面类型,(默认情况下,但是如何新建一个type?新的渲染格式?)Hexo有三种不同的layout,不同layout保存的路径并不一样。 Layout Path post ./source/_post page ./source draft ./source/_draft 不同layout的唯一区别是保存到不同的路径(to some extent) layout支持自定义,存放目录和post一样,当scaffold无自定义类型时,调用默认layout hexo publish 可用于发表草稿(draft–>post,参考1)(仅用于发表草稿,不代表支持转移目录,是否代表支持修改scaffold里的layout变量?) hexo new page 可以利用layout组织template template Page Fallback Path Fallback Path index Home page post Posts index _post page Pages index source source/%pagename archive Archives index category Category archives archive tag Tag archives archive Fallback位于类似于scaffolds,只是定义了变量,不同template的渲染文件位于 ./themes/next/layout/\* 下,利用swig组成,swig是一个js模版引擎http://imweb.io/topic/565b2e23bb6a753a136242b5 /source/_其它(page?)reference-写作 hexo new draft ..其中..d的layout是post,并非draft,有点坑啊。。。在scaffolds中国年增加变量layout: draft解决该问题,然而历史的的md都会被认为是默认的layout:post,需手动添加layout: draft补充在post.swig和post-collapse的 标签增加 {% if post.layout === "draft" %} 撰写中 {% endif %} 用于提醒。 通过hexo new darft "test_draft_publish"然后hexo publish "test_draft_publish",知识修改了文件的名称,从test-draft-publish.md修改为test-draft-publish-1.md,layout并未修改为post(期望是layout变量修改为post,并把md文件转移到_post文件夹),这个问题的主要原因是scaffold/draft.md增加了layout变量,需要想办法修改下。 scaffold自定义模版scaffolds/%layoutscaffolds决定不同layout的变量,在initial一个article时出现的变量,不同的layout变量使用不同的template和不同的pathtemplate上述内容的展现方式切换布局 如果要显示草稿,页面级别设置是没用的,只能全站级别希望修改为页面级别,而且修改样式提醒为草稿以及首页显示后提醒草稿样式 建站结构 为了进行个性化定制,先定义一些主要的数据类型:页面(类型),如首页,归档页,标签页等:page页面:默认(default)post 我们页了解下Hexo框架下,页面(html)的生成逻辑。另外也应该了解同一个选项,hexo的配置的优先级:page>theme>hexo从html角度进行个性化定制 我们来看看hexo 的名录以及各目录下配置或文件的作用tree -L 3 -I node_modules\|public 12345678910111213141516171819202122232425262728293031323334353637383940414243444546.├── _config.yml//站点配置├── db.json├── debug.log├── package.json├── scaffolds│ ├── draft.md//草稿│ ├── page.md//页面│ └── post.md//博客├── source│ ├── 404 //404页面│ │ └── index.md│ ├── _posts //博客目录│ │ ├── Hexo-Blog-Setup以?\217\212常?\201?\205\215置.md│ │ ├── Markdown\ Web\ \ ?\226?\221?\231?产?\223\201?\203?\224.md│ │ ├── README.md│ │ ├── ..│ │ ├── ..│ │ │ ├── about //关于页面│ │ └── index.md│ └── tags //tags页面│ └── index.md└── themes//模版信息 ├── landscape //landscape模版脚本 │ ├── Gruntfile.js │ ├── LICENSE │ ├── README.md │ ├── _config.yml │ ├── languages │ ├── layout │ ├── package.json │ ├── scripts │ └── source └── next //next模版脚本 ├── README.en.md ├── README.md ├── _config.yml//模版配置 ├── bower.json ├── gulpfile.coffee ├── languages ├── layout ├── package.json ├── scripts ├── source └── test//测试 我们进到next模版目录详细研究tree -I test 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280.├── README.en.md├── README.md├── _config.yml├── bower.json├── gulpfile.coffee├── languages//语言包│ ├── zh-Hans.yml│ ├── default.yml│ ├── en.yml│ ├── zh-hk.yml│ ├── ..│ └── ..│ ├── layout//layout控制│ ├── _layout.swig//layout javascript 模版引擎│ ├── _macro//所有变量│ │ ├── post-collapse.swig│ │ ├── post.swig│ │ ├── reward.swig│ │ ├── sidebar.swig│ │ └── wechat-subscriber.swig│ ├── _partials│ │ ├── comments.swig│ │ ├── duoshuo-hot-articles.swig│ │ ├── footer.swig│ │ ├── head│ │ │ └── external-fonts.swig│ │ ├── head.swig│ │ ├── header.swig│ │ ├── pagination.swig│ │ ├── search│ │ │ ├── localsearch.swig│ │ │ ├── swiftype.swig│ │ │ └── tinysou.swig│ │ ├── search.swig│ │ └── share│ │ ├── add-this.swig│ │ ├── baidushare.swig│ │ ├── duoshuo_share.swig│ │ └── jiathis.swig│ ├── _scripts│ │ ├── baidu-push.swig│ │ ├── boostrap.swig│ │ ├── commons.swig│ │ ├── pages│ │ │ └── post-details.swig│ │ ├── schemes│ │ │ ├── mist.swig│ │ │ ├── muse.swig│ │ │ └── pisces.swig│ │ ├── third-party│ │ │ ├── analytics│ │ │ │ ├── baidu-analytics.swig│ │ │ │ ├── busuanzi-counter.swig│ │ │ │ ├── cnzz-analytics.swig│ │ │ │ ├── facebook-sdk.swig│ │ │ │ ├── google-analytics.swig│ │ │ │ └── tencent-analytics.swig│ │ │ ├── analytics.swig│ │ │ ├── comments│ │ │ │ ├── disqus.swig│ │ │ │ └── duoshuo.swig│ │ │ ├── comments.swig│ │ │ ├── lean-analytics.swig│ │ │ ├── localsearch.swig│ │ │ ├── mathjax.swig│ │ │ └── tinysou.swig│ │ └── vendors.swig│ ├── archive.swig│ ├── category.swig│ ├── index.swig│ ├── page.swig│ ├── post.swig│ └── tag.swig├── package.json├── scripts│ ├── merge-configs.js│ └── tags│ ├── center-quote.js│ ├── full-image.js│ └── group-pictures.js└── source ├── 404.html ├── css │ ├── _common │ │ ├── components │ │ │ ├── back-to-top.styl │ │ │ ├── buttons.styl │ │ │ ├── comments.styl │ │ │ ├── components.styl │ │ │ ├── footer │ │ │ │ └── footer.styl │ │ │ ├── header │ │ │ │ ├── header.styl │ │ │ │ ├── headerband.styl │ │ │ │ ├── menu.styl │ │ │ │ ├── site-meta.styl │ │ │ │ └── site-nav.styl │ │ │ ├── highlight │ │ │ │ ├── highlight.styl │ │ │ │ └── theme.styl │ │ │ ├── pages │ │ │ │ ├── archive.styl │ │ │ │ ├── categories.styl │ │ │ │ ├── pages.styl │ │ │ │ └── post-detail.styl │ │ │ ├── pagination.styl │ │ │ ├── post │ │ │ │ ├── post-collapse.styl │ │ │ │ ├── post-eof.styl │ │ │ │ ├── post-expand.styl │ │ │ │ ├── post-gallery.styl │ │ │ │ ├── post-meta.styl │ │ │ │ ├── post-more-link.styl │ │ │ │ ├── post-nav.styl │ │ │ │ ├── post-reward.styl │ │ │ │ ├── post-tags.styl │ │ │ │ ├── post-title.styl │ │ │ │ ├── post-type.styl │ │ │ │ └── post.styl │ │ │ ├── sidebar │ │ │ │ ├── sidebar-author-links.styl │ │ │ │ ├── sidebar-author.styl │ │ │ │ ├── sidebar-blogroll.styl │ │ │ │ ├── sidebar-feed-link.styl │ │ │ │ ├── sidebar-nav.styl │ │ │ │ ├── sidebar-toc.styl │ │ │ │ ├── sidebar-toggle.styl │ │ │ │ ├── sidebar.styl │ │ │ │ └── site-state.styl │ │ │ ├── tag-cloud.styl │ │ │ ├── tags │ │ │ │ ├── blockquote-center.styl │ │ │ │ ├── full-image.styl │ │ │ │ ├── group-pictures.styl │ │ │ │ └── tags.styl │ │ │ └── third-party │ │ │ ├── baidushare.styl │ │ │ ├── busuanzi-counter.styl │ │ │ ├── duoshuo.styl │ │ │ ├── jiathis.styl │ │ │ ├── localsearch.styl │ │ │ └── third-party.styl │ │ ├── outline │ │ │ └── outline.styl │ │ └── scaffolding │ │ ├── base.styl │ │ ├── helpers.styl │ │ ├── normalize.styl │ │ ├── scaffolding.styl │ │ └── tables.styl │ ├── _custom │ │ └── custom.styl │ ├── _mixins │ │ ├── Mist.styl │ │ ├── Muse.styl │ │ ├── Pisces.styl │ │ ├── base.styl │ │ └── custom.styl │ ├── _schemes │ │ ├── Mist │ │ │ ├── _base.styl │ │ │ ├── _header.styl │ │ │ ├── _logo.styl │ │ │ ├── _menu.styl │ │ │ ├── _posts-expanded.styl │ │ │ ├── _search.styl │ │ │ ├── index.styl │ │ │ ├── outline │ │ │ │ └── outline.styl │ │ │ └── sidebar │ │ │ └── sidebar-blogroll.styl │ │ ├── Muse │ │ │ ├── _layout.styl │ │ │ ├── _logo.styl │ │ │ ├── _menu.styl │ │ │ ├── _search.styl │ │ │ ├── index.styl │ │ │ └── sidebar │ │ │ └── sidebar-blogroll.styl │ │ └── Pisces │ │ ├── _brand.styl │ │ ├── _full-image.styl │ │ ├── _layout.styl │ │ ├── _menu.styl │ │ ├── _posts.styl │ │ ├── _sidebar.styl │ │ └── index.styl │ ├── _variables │ │ ├── Mist.styl │ │ ├── Muse.styl │ │ ├── Pisces.styl │ │ ├── base.styl │ │ └── custom.styl │ └── main.styl ├── fonts ├── images │ ├── avatar.gif │ ├── cc-by-nc-nd.svg │ ├── cc-by-nc-sa.svg │ ├── cc-by-nc.svg │ ├── cc-by-nd.svg │ ├── cc-by-sa.svg │ ├── cc-by.svg │ ├── cc-zero.svg │ ├── loading.gif │ ├── placeholder.gif │ ├── quote-l.svg │ ├── quote-r.svg │ └── searchicon.png ├── js │ └── src │ ├── affix.js │ ├── bootstrap.js │ ├── hook-duoshuo.js │ ├── motion.js │ ├── post-details.js │ ├── schemes │ │ └── pisces.js │ ├── scrollspy.js │ └── utils.js └── vendors ├── fancybox │ └── source │ ├── blank.gif │ ├── fancybox_loading.gif │ ├── [email protected] │ ├── fancybox_overlay.png │ ├── fancybox_sprite.png │ ├── [email protected] │ ├── helpers │ │ ├── fancybox_buttons.png │ │ ├── jquery.fancybox-buttons.css │ │ ├── jquery.fancybox-buttons.js │ │ ├── jquery.fancybox-media.js │ │ ├── jquery.fancybox-thumbs.css │ │ └── jquery.fancybox-thumbs.js │ ├── jquery.fancybox.css │ ├── jquery.fancybox.js │ └── jquery.fancybox.pack.js ├── fastclick │ ├── LICENSE │ ├── README.md │ ├── bower.json │ └── lib │ ├── fastclick.js │ └── fastclick.min.js ├── font-awesome │ ├── HELP-US-OUT.txt │ ├── bower.json │ ├── css │ │ ├── font-awesome.css │ │ ├── font-awesome.css.map │ │ └── font-awesome.min.css │ └── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── jquery │ └── index.js ├── jquery_lazyload │ ├── CONTRIBUTING.md │ ├── README.md │ ├── bower.json │ ├── jquery.lazyload.js │ └── jquery.scrollstop.js ├── ua-parser-js │ └── dist │ ├── ua-parser.min.js │ └── ua-parser.pack.js └── velocity ├── bower.json ├── velocity.js ├── velocity.min.js ├── velocity.ui.js └── velocity.ui.min.js 更新语言包的映射表。更新_marco/post.swig,用于增加updated time元素,在post增加updated变量swig使用指南从源码级别优化hexo next主题 为了增加page的toc可以在page.swig里添加逻辑语句进行渲染 1234567891011121314151617181920#默认设置{% block sidebar %} {{ sidebar_template.render(false) }}{% endblock %}#}{#<!--page增加sidebar控制--><!--sidebar不代表toc,toc是sidebar的一部分--><!--sidebar_template.render(false)显示非toc部分--><!--sidebar_template.render(true)显示toc部分--><!--toc和非toc是二选一的关系-->#}{% block sidebar %} {% if theme.sidebar.display == 'always' %} {{ sidebar_template.render(true) }} {% else %} {{ sidebar_template.render(false) }} {% endif %}{% endblock %} page是所有页面变量?在%page_name/index.md里增加变量toc: true变量,在page.swig增加判断逻辑对该变量校验1234567891011121314151617181920212223{#<!--page增加sidebar控制--><!--sidebar不代表toc,toc是sidebar的一部分--><!--sidebar_template.render(false)显示非toc部分--><!--sidebar_template.render(true)显示toc部分--><!--toc和非toc是二选一的关系-->{% block sidebar %} {% if theme.sidebar.display == 'always' %} {{ sidebar_template.render(true) }} {% else %} {{ sidebar_template.render(false) }} {% endif %}{% endblock %}#}{% block sidebar %} {% if page.toc %} {{ sidebar_template.render(true) }} {% else %} {{ sidebar_template.render(false) }} {% endif %}{% endblock %} 在about增加update时间在about自我简介+resume链接增加is_hidden: true属性首页不显示123456789101112{% block content %} <section id="posts" class="posts-expand"> {% for post in page.posts %} {#test#} {% if post.is_hidden != 'true' %} {{ post_template.render(post, true) }} {% endif %} {% endfor %} </section> {% include '_partials/pagination.swig' %}{% endblock %} 自己创建&管理主题regeneratehexo clean删除db.json 支持流程图npm install hexo-diagram –savenpm install phantomjs [email protected]: Package renamed to phantomjs-prebuilt. Please update 'phantomjs' package references to 'phantomjs-prebuilt'npm install phantomjs-prebuilt -g通过sublime插件搜索node_module下所有包涵’phantomjs’语句,替换为’phantomjs-prebuilt’ /Users/NoraChan/Desktop/Blog/node_modules/.bin/esvalidate:/Users/NoraChan/Desktop/Blog/node_modules/esprima/bin/esvalidate.js:/Users/NoraChan/Desktop/Blog/node_modules/phantom/phantom.coffee:/Users/NoraChan/Desktop/Blog/node_modules/phantom/phantom.js: npm install phantomjs –save–save和-g的区别是?(phantomjs-prebuilt) 决定按照insecure方式处理修改回这些文件/Users/NoraChan/Desktop/Blog/node_modules/.bin/esvalidate:/Users/NoraChan/Desktop/Blog/node_modules/esprima/bin/esvalidate.js:/Users/NoraChan/Desktop/Blog/node_modules/phantom/phantom.coffee:/Users/NoraChan/Desktop/Blog/node_modules/phantom/phantom.js: hexo-diagram 渲染问题 流程图支持的markdown语法 ###fancy box 效果问题&增加post级别fancy设置 设置点击不跳转设置阴影效果1234567<style type="text/css">img { -moz-box-shadow: 3px 3px 5px 6px #ccc; -webkit-box-shadow: 3px 3px 5px 6px #ccc; box-shadow: 3px 3px 5px 6px #ccc;}</style> css3的box-shadow属性实现图层阴影效果CSS Box Shadow]]></content>
</entry>
<entry>
<title><![CDATA[STAR of Projects]]></title>
<!--modified by Nora
<url>%2F2016%2F11%2F15%2FSTAR-of-Projects%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F11%2F15%2FSTAR-of-Projects%2F</url>
-->
<url>/2016%2F11%2F15%2FSTAR-of-Projects%2F</url>
<content type="text"><![CDATA[Summary偏好数据技术&后端技术技术栈积累,希望充当一个能做产品需求分析、设计&项目管理的Program Manager。本文档的主要是对每个经手项目做详细的S(Situation)T(Task)A(Action)R(Result)分析 舜飞科技-DSP产品经理先后向CEO/产品负责人(创始人之一)汇报。偏后端项目管理,偏Scrum Master。数据驱动衡量需求价值并协调前端、后端和测试资源保证项目顺利上线。协助流量变现,移动端广告投放占比增长到50%以上。 构建产品部文档和项目管理制度; 负责渠道/媒介对接,衡量渠道价值,安排优先级; 重点客户/项目跟进(技术/英文/复杂等);2016年,舜飞的战略方向有两个,一个是大的品牌广告主(展示类广告是广告市场大头,效果类广告欠积累),一个是海外的资源拓展(国内增量市场)。其中,品牌广告主主要来自4A公司。这类需求有几个特点: 需求不确定/变更频繁 对技术需求没有概念且复杂、个性化 预算大,时间长 对概念( 一般由第三方技术对接)的要求高。 其中,“高通”是舜飞2016年最大的品牌广告主,同时高通的成功投放也是舜飞“品牌广告主”战略至关重要的一步。 “计划”一期设计&测试BES_AMS服务对接Situation主流的人群标签以及第三方数据主要是基于访客的浏览数据进行建模,对于toB类广告主,这类人群标签显然不够精准。BES是百度最大的ADX。AMS(Audience Matching Service)服务是百度关键词服务,通过API返回固定数量关键词对应的搜索人群(cookie),用于RTB过程中的(re)targeting。另一方面,这个功能对于品牌广告主市场而言有强烈的吸引性,属于稀缺资源。渠道协议& 文档已经到位,客户有强烈需求。 Task按时上线,保证服务正常。 Action甄别背景信息&设计产品、跟进进度&撰写文档、保证测试 探索&明确产品需求和商务紧急性 显性需求: 对接 隐性需求: 跨渠道支持关键词人群(二期,与技术成本相关) 运营理解并容易使用 需要考虑的问题: 广告主/DSP无法验证 可能无法投出量 可能无效果 和渠道沟通,确认 确认后端数据交换流程、协议 接口测试(联调) 确认前端界面开发方案 测试 对比日志中请求数据与出价数据保证出价比例 控制商务期望 确认数据口径,撰写使用文档,简单明了阐述清楚原理和使用方法,降低使用成本。包括使用规则和cookie周期等可能会遇到的问题。 遭遇两个方案: 实时请求方案(基于BiddingX、BES、三方ADX共同成功mapping的cookies)理想模型: 12BiddingX->BES: wordsBES->BiddingX: tag_id 1234All ADXs->BiddingX: Bid RequestBiddingX->BES: AMS RequestBES->BiddingX:tag_id(s)BiddingX->All ADXs: Bid Response 离线方案实际模型(考虑服务器资源和120ms内的返回竞价回复的实际情况): 12BiddingX->BES: wordsBES->BiddingX: tag_id 曝光cookie mapping+cookie mapping 托管123Other ADXs->BiddingX: Bid RequestBiddingX->BiddingX:self-queryBiddingX->Other ADXs: Bid Response Result按时上线,服务SAP、IBM广告主。 The_Media_Trust对接Situation进行海外投放过程中,像Imobi/Google/Mopub/Pubmatic渠道,有严格的创意&政策要求。The Media Trust是保证创意、落地页无恶意代码以及政策违反的第三方服务机构,通过定时轮询,保证投放免受政策惩罚。由于代理商广告创意包含恶意代码,BiddingX被Google惩罚,禁止投放。台湾、香港以及东南亚、国外广告主受严重影响,需要紧急上线该服务保证创意质量。 Task 通过对接第三方服务、和Google沟通等恢复海外资源投放 确认Google投放要求,避免类似情况出现 Action 和Google Tech Team&Business Team沟通确认specific issue(创意/落地页) 确认清楚被惩罚的具体原因(严重沟通漏斗) 结合实际考虑Google的对接建议 对接保证创意安全 确认最保守的轮询策略(Google有最后通牒:永久禁投) 合适的提醒方案(不打扰目前创意送审流程同时出问题可以及时发现、修改和沟通:送审轮询得到恶意代码后提醒) 测试 review出问题创意是否及时阻止 case by case 跟进所有The Media Trust警告邮件 确认被默认停止投放的广告具体原因 ResultGoogle恢复正常投放,顺利保证The Media Trust服务。 Peer_39/ADbug_brandsafety(品牌安全)对接Situation品牌类广告主对于品牌美誉度有要求,要求广告位所在的页面不影响品牌调性。brandsafety指的正是这一类ad verification服务。Peer 39/ADbug是国内外该服务提供商中最出名的。机房竞价前请求(国内/海外)服务器page级别的属性数据,判断是否危害广告主品牌安全。用于保护品牌类广告主广告调性。主要用于高通广告主第三波投放。 Task 对接brandsafety服务 调研brandsafety原理、关键指标和边界并分享 Action 控制商务期望(最重要) 探索&明确产品需求和商务紧急性 需求&里程碑确认 技术&产品文档确认 确认前端界面开发方案和后端出价、获取数据逻辑 保证使用 测试(合理预计过滤比例) 数据报告(验证有效性) 使用方案 基于行业内brandsafety的adserving调研并产出PPT Result顺利对接brandsafety服务,服务高通广告主并形成文档、ppt,完成分享 Sizmek_ad_serving对接SituationSizmek为4A提供广告创意& 监测服务,包括动态创意和brandsafety (品牌安全-post bid)/viewability(可视率)监测。通过对接,保证创意、落地页和竞价服务(DSP端、渠道端)相互兼容。 主要用于高通广告主投放。 Task 对接第三方动态创意服务(通过HTML投放js) 保证video in banner正常投放(在banner广告位做视频投放) Action 控制商务期望(最重要) 探索&明确产品需求 确认“动态创意”包含什么服务(通过HTML投放js) 确认video in banner投放具体细节 特殊广告形式下的成本(CDN) 非expandable 确认具体尺寸& 分别尺寸可竟得的impression数据 具体创意尺寸& 格式、压缩方式、加载时长等(第几帧开始第几帧结束) 广告主方/渠道方是否有特殊需求 渠道/广告位粒度的协议确认 测试 保证统计、监测服务正常 分终端、分ADX的adserving测试 Result正常投放,并在投放过程中解决渠道对类Mraid协议、创意兼容问题(设备级别) 高通DSP投放Situation高通(甲方)的需求来自奥美(乙方)和sizmek(丙方),舜飞在商务投放中处于丁方的地位,需要向上把握需求。且商务决策中每一方的需求点都不一样,执行的细节有很大需要把握的空间和需求的正确性。 需求来源于邮件,而且同一个需求在不同的时间点由不同的人提出,继续确认哪些需求是同一个需求,那些不是,确认范围和时间点。 主要用于高通广告主第二波投放。 高通是2016年最重要的品牌类广告主,品牌类广告主的需求变化多且个性化。解决业务层面为当前产品/技术架构带来的挑战。 Task/Result 保证campaign按时上线 满足商务“平分轮显”需求 Action 控制商务期望(最重要) 明确cookie保存周期最多半年(频次控制by cookie&by time) 明确商务需求,包括媒介需求& 产品功能需求,确认需求边界于关键时间点 确认“平分轮显”具体要求(by day) 跨渠道跨创意的频次控制(by adx&by creative&by cookie) 无法预测的累积时间段的频次控制(by cookie&by time) 创意轮流显示且平分显示次数(by creative&by cookie) 保证优酷/腾讯/爱奇艺PDB 市场对接完成(OTV) 从商务需求转化为技术需求,拆分子任务和关键验证点(创意按by cookie/by 频次 随机) 测试 设置合适的campaign(按活动/按创意,对比频次数据,预期数据无差异) 保证统计discrepancy 广告主方/渠道方是否有特殊需求 渠道/广告位粒度的协议确认 确认RTB/PDB市场完成对接 考虑可扩展性 跨渠道跨创意的频次控制(by adx&by creative&by cookie) campaign、产品、创意包粒度的“平分轮显”需求 全局轮显& 用户轮显 3+reach最多 移动RTB/全渠道PMP/PDB市场对接Situation/Result14年后,互联网进入移动时代。同时广告市场也根据商务需求和技术发展进入了RTB/PMP/PDB的精细化运营的时代,RTB/PMP/PDB市场业务有区别,需要根据协议和商务业务调整竞价服务。从产品层面考虑兼容性,兼容跨渠道差异以及现有产品功能。该项目由很多小项目组成,根据不同项目case by case 解决问题,兼产品经理和项目经理,根据协议进行产品需求设计,协调资源,推动进度,保证上线。例如: 腾讯渠道RTB和PMP/PDB通过两套(相似)协议和分离环境实现两部分流量的分发。流程有不一样的地方,为了高度的拓展性,牺牲了一定的的便利性:如何关联广告主,如何放量。为了可理解性,希望一定的开发资源。 资质的扩展、adx的合并与分离等(新旧数据) 通过算法保证PDB按比例返量并考虑极端情况(提前和媒体&广告主沟通) PDB竞价暂停情况 SEM品牌推广SEM做广告投放 Situation原MKT SEM离职,老板希望重新整理BiddingX品牌的SEM&DSP投放,通过数据化运营优化后吸引客户注册 Task负责BiddingX品牌的SEM& DSP营销,调整关键词和campaign,优化后吸引客户注册 Action 整合Site Tracking和PDMP重定向目标人群 利用数据可视化展现工作成果 Result 日均消费提高400%(700–>3000) 点击率提升100%(0.3%–>0.7%) 展示量提升100% 有赞商城DSP推广Situation电商APP推广,为了开拓电商客户,有赞是2015年重点客户之一。通过使用包括Banner创意和视频创意在内,尝试引导网页流量转化为APP激活,用于探索PC往APP转化路径。 Task 通过投放优化,满足客户CPA需求 尝试引导网页流量转化为APP激活 Action 通过业务分析,制定投放策略 受众分析 选择媒体 不断建立测试campaign& 调整优化创意、落地页、定向条件 整合第三方DMP数据到DSP进行目标人群重定向 获取激活数据用于优化 对接第三方监测平台,通过S2S scheme获取激活数据 在PC端缺乏数据回传机制后,改方向投放 使用项目管理平台(tower.im)协调优化师与开发资源 Result优化后,Android端正常投放并达到客户CPA要求。 移动SSP_iOS_SDK第一版SituationDSP发展到一个阶段,意识到媒体是广告市场的竞争力所在,需要拓展自有的核心视频& 信息流资源。商务团队已经有几个目标客户拓展中,有其紧急性,但与此同时,SSP开发团队项目由于需求持续变更,无法迭代。老板需要两周内完成完成第一版,后续迭代。 Task/Result保证移动SSP iOS SDK第一版两周内上线(老板deadline) Action 通过SSP PM不进行次要需求的开发(只保留关键指标:展示量& 收入 的统计和测试) 保证BD团队和开发团队沟通,确保上线日期 唯品会-数据产品经理向BI(一级部门)/数据产品(二级部门)高级经理汇报。数据分析产品化。涉及数据产品的主要流程,从PRD文档、到产品的UI设计,以及跨系统的产品整合,甚至部分ETL,黑白盒数据测试和项目管理工作。 负责面向管理层的APP数据产品, 产品化常规的运营分析流程,支持决策; 接手旧产品,并根据管理层反馈做快速迭代; 参与创新APP项目,如, 我是妈咪 ( 母婴闪购电商)和 hey!购物 (基于库存的个性化推荐APP); 总裁看板Situation利用Oracle BIEE实现销售、流量业务的数据报表化,管理层通过每日邮件方式了解业务运营。原“总裁看板”产品经理离职,作为“总裁看板”第二任产品经理接手该项目。也是作为“数据产品经理”后,第一个接手负责的产品/项目。由于ETL开发人员紧张,也参与部分ETL开发工作(熟悉业务&具备独立分析能力)。 Task 保证产品数据正确& 稳定运行,及时处理脏数据& fix bug 结合新业务进行产品迭代,通过数据向管理层传递最新业务的现状 对管理层提出的运营问题快速反馈(具备分析师经验)& 迅速响应新需求 Action 了解产品开发/上线流程 了解产品指标体系&统计口径,对比常规分析报告与差异,预判可能出现的问题 Result顺利接手旧系统,并对管理层需求快速反馈。(5天内) 针对数据中的问题提供分析报告 根据业务变化进行ETL工作 协调资源保证产品按时上线 创新APP项目Situation/Result唯品会执行多APP战略,要求基于目前的业务系统孵化新业务APP。BI部门也需要参与到业务运营,承担KPI,真正做到数据驱动业务(业绩)。包括“我是妈咪”(母婴特卖垂直领域电商APP)和“hey!购物”(基于库存的个性化推荐APP)。由于对接品牌部门和市场部门业务分析,对推广业务熟悉。 负责整合旧系统的推广、渠道、账户业务逻辑到新app。 负责H5推广活动页产品实现,通过微博、微信渠道吸引新用户 功能测试和数据埋点 Task 对接优惠系统、渠道推广系统到“我是妈咪”(母婴特卖垂直领域电商APP) “我是妈咪”(母婴特卖垂直领域电商APP)部分购物车相关、用户中心(profile)相关设计和系统对接 对微信HTML5推广需求出设计方案&数据追踪方案 接手& 跟进 “hey!购物”(基于库存的个性化推荐APP)的 数据埋点&负责用户中心(profile)相关功能测试用例 Action 了解旧系统业务逻辑,出产品设计&跟进项目进度 了解需求细节 移动司南PRD Situation/Result 面向对象是CXO level的管理层,数据需求主要是对经营profile的了解。 当时部门要求pitch老板资源的,因此,对高保真要求高于详细文档要求。当时目标是成为独立事业部,开发和设计资源都很少,能够用口头沟通代替部分文字表达。 这是第一份偏设计的PRD,而且要求(尽量)高保真,有问题,但不影响展示(主要根据手机屏幕大小做了取舍)全面负责面向管理层的移动端分析产品,包括产品设计,数据探索和数据可视化。 Task基于PC端产品,根据移动端特性取舍,做移动端数据产品设计 Action 阅读iOS&Android设计指南 数据探索设计是否合理(指标范围,实际数据展现,分析角度,计算精度) 尽快提交设计获取反馈进行迭代 Android中文Material DesigniOS8人机界面指南Human Interface Guidelines 唯品司南Situation/Result作为功能性产品经理,参与到品牌、档期、品类维度的分析产品中去为品牌销售和区域销售提供数据决策的可视化产品。对常规化的分析进行ETL后,利用high-chart,e-chart框架展现,包括档期分析,退拒分析等。获得管理层高度认可。一期参与到后期数据验证,负责黑白盒数据测试&验证,确保数据正确性。二期作为功能性产品经理,参与到品类分析、品牌分析的产品规划。并对数据可视化提供UE建议。 Task 了解产品指标体系&统计口径,对数据进行黑盒测试,找出常规分析报告差异 作为功能性产品经理,参与到品类分析、品牌分析的产品规划。并对数据可视化提供UE建议。 Action 数据探索&分析 和商务运营沟通 了解数据口径 分析产品,具体看什么指标,怎么看?具体如何同比,如何环比。 唯品经纬Situation/Result“唯品经纬”属于创新项目,希望通过利用订单地址数据,挖掘客户有价值的人口统计学信息& 进行职业信息分析,并根据这些信息找到营销切入点,实现的业务可视化,为市场进行地面推广提供支持。对唯品经纬数据部分进行黑盒测试。由于ETL离职,作为ETL开发参与到白盒测试,保证数据正确性。 Task 了解产品指标体系&统计口径,对数据进行黑盒测试,找出常规分析报告差异 作为ETL,协助指标体系口径一致 Action 确认项目边界 白盒子口径检验 设计数据测试方案 确认项目便捷 唯品会-数据分析师先后向BI(一级部门)/运营分析(二级部门)经理、主管汇报。作为运营分析组一员,每周提供运营分析以及日常快捷反馈到总裁会,并根据反馈进行运营层面的分析建议。负责市场运营方面日常分析: 广告投放&渠道:为衡量渠道价值进行资源分配而构造归因模型; 促销组合:不同促销形式的效果&组合分析,并形成报表系统; 财务:市场费用的构成分析; 微信特卖Situation/Result100%对微信商城的BI分析负责,形成虚拟团队向品牌副总裁汇报。利用微信公众号,在微信端进行销售。 Task100%support微信商城销售 每周提供微信数据周报以及数据分析 对微信常态分析产品化 建议微信端代金券全场使用,促进跨品牌销售; Action 了解底层仓库表&申请权限 提供业务报表 和大数据部门沟通,形成微信报表产品 归因模型Situation/Result独立分析项目。确认定岗BI部门后,参与到BI对市场部的跨渠道归因模型项目中,探索跨投放与销售的因果关系。利用归因(助攻模型)模型衡量渠道贡献价值,尝试利用图论实现可视化,提供业务决策支持。 TaskAction其它电子商务代运营Situation大二参与到电商创业团队,当时天猫(淘宝商城)刚成立,全网都有流量、运营资源倾斜,从高利润品类切入(化妆品),做到50w/m销售业绩 Task确认推广目标,参与淘店运营 Action 微博运营活动策划,目标设定,拉去资源,淘店引流 校园活动策划与执行 Result50w/m销售业绩 PS:工作中,探索的部分比较多,受限于资源和信息不对称,自己发起的商业化产品较少但尝试发起并运营内部的项目管理产品 - wiki & jira & Gitbook 由于舜飞是starup,以及作为agent的特殊性,这边的工作比较少考虑完整的测试用例覆盖,工作中缺乏完整的联调环境和测试环境,广告投放正式环境和联调环境本质还是有差距的。这是由于: 自身无法控制对方环境的完整性 广告市场的极度长尾化与信息不对称的资源割据 更多通过线上测试以及快速回归(出价和统计都比较难覆盖) 舜飞有明确的规章制度把产品技术和商务区隔开,我无法得知整个投放涉及的预算,但后续第二第三波投放都主要由我对接,投放没有出现大问题,基本按进度上线]]></content>
</entry>
<entry>
<title><![CDATA[《深入理解计算机操作系统》-chapter 7 链接 Memo]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F27%2F%E3%80%8A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E3%80%8B-chapter-7-%E9%93%BE%E6%8E%A5-Memo%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F27%2F%E3%80%8A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E3%80%8B-chapter-7-%E9%93%BE%E6%8E%A5-Memo%2F</url>
-->
<url>/2016%2F10%2F27%2F%E3%80%8A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E3%80%8B-chapter-7-%E9%93%BE%E6%8E%A5-Memo%2F</url>
<content type="text"><![CDATA[链接可在编译(compile-time)、加载(load-time)或运行(run-time)时执行。 顺序:语言预处理器–>编译器–>汇编器–>链接器 ###静态链接输入:可重定位的目标文件(object file),命令行参数输出:可执行的目标文件 输入的可重定位目标文件由各种不同的代码和数据节(section)组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。 .pyx–>.c–>.o/.so]]></content>
</entry>
<entry>
<title><![CDATA[【译】快速开始Jupyter Notebook]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F12%2F%E3%80%90%E8%AF%91%E3%80%91%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8BJupyter%20Notebook%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F12%2F%E3%80%90%E8%AF%91%E3%80%91%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8BJupyter%20Notebook%2F</url>
-->
<url>/2016%2F10%2F12%2F%E3%80%90%E8%AF%91%E3%80%91%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8BJupyter%20Notebook%2F</url>
<content type="text"><![CDATA[#reference-Jupyter Notebook If you would like to learn more about the specific elements within the Notebook Editor, you can go through the User Interface Tour by selecting Help in the menubar then selecting User Interface Tour. (http://jupyter-notebook.readthedocs.io/en/latest/ui_components.html)]]></content>
</entry>
<entry>
<title><![CDATA[计算机网络观测]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F10%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%A7%82%E6%B5%8B%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F10%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%A7%82%E6%B5%8B%2F</url>
-->
<url>/2016%2F10%2F10%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%A7%82%E6%B5%8B%2F</url>
<content type="text"><![CDATA[Summary104.128.92.224和119.29.198.230是两台独立服务器(VPS),其中,104.128.92.224位于Los Angeles,119.29.198.230位于广州。通过对它们的网络行为进行监控,对比他们收取到回复的差异,确认GFW的特殊价值并探索计算机应用层协议的原理与细节。(本文以LA服务器和广州服务器分别指代)本文主要参考翻墙路由器的原理与实现来探索计算机网络(主要是应用层),并对探索过程中遇到的问题做简单记录,遇到问题涉及计算机操作系统、编译原理的部分知识。 #reference-104.128.92.224的IP地址#reference-119.29.198.230的IP地址 #reference-【转】翻墙路由器的原理与实现 #reference- 操作系统的差异,Ubuntu #reference-netfilterqueue文档 工具NetfilterQueue我们主要利用NetfilterQueue进行计算机网络的观测,这是Libnetfilter_queue的python实现,Libnetfilter_queue是Netfilter project的一部分。Linux下,NetfilterQueue允许用户访问符合iptables规则的IP包。iptables也是Netfilter project的一部分,理解成一张用于保存IP包转发规则的table就好,主要用于防火墙。利用这套规则,计算机可以接受、丢弃,改变(转发)和标记IP包。 ###升级apt-get更新资源并升级: \$ sudo apt-get update && sudo apt-get upgrad http://blog.csdn.net/ai_net/article/details/7710324 安装NetfilterQueue有依赖,不细心的童鞋会走弯路(例如我T_T) sudo apt-get install build-essential python-dev libnetfilter-queue-dev # 系统依赖sudo pip install NetfilterQueue # NetfilterQueue安装 安装过程中可能遇到的问题未安装gcc,无法编译。显示: 12unable to execute 'gcc': No such file or directoryerror: Setup script exited with error: command 'gcc' failed with exit status 1 安装gcc \$ sudo apt-get install gcc pippip是python下包管理工具。主要用于安装和管理python包,和easy_install类似。 Install& Update& Upgrade不正确地安装会有问题。 \$ sudo apt-get install python-pip #reference-How to install pip on Ubuntu#reference-Doc_installing#reference-easy_install进行模块/包管理#reference-How to install software or upgrade from an old unsupported release dpkt \$ sudo pip install dpkt Bind-digdig 命令全称Domain Information Groper,系统自带,观测过程中需要用到的主要命令。#reference-Internet System Consortium-BIND#reference-How do I install dig#reference-使用 Ubuntu 安裝 Bind9: Domain Name Service (DNS) 计算机网络观测DNS劫持观测广州服务器: $ dig @8.8.8.8 twitter.com ; <<>> DiG 9.8.3-P1 <<>> @8.8.8.8 twitter.com; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45740;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION:;twitter.com. IN A ;; ANSWER SECTION:twitter.com. 60 IN A 243.185.187.39 ;; Query time: 112 msec;; SERVER: 8.8.8.8#53(8.8.8.8);; WHEN: Sat Oct 15 13:07:17 2016;; MSG SIZE rcvd: 45 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @8.8.8.8 twitter; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 55863;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 512;; QUESTION SECTION:;twitter. IN A ;; AUTHORITY SECTION:. 81741 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2016110901 1800 900 604800 86400 ;; Query time: 25 msec;; SERVER: 8.8.8.8#53(8.8.8.8);; WHEN: Wed Nov 09 21:01:40 EST 2016;; MSG SIZE rcvd: 111显然,返回的IP地址错误。 安装apt-get install …http://askubuntu.com/questions/91815/how-to-install-software-or-upgrade-from-an-old-unsupported-release sudo 权限下完成 (#4。 有依赖)https://pypi.python.org/pypi/NetfilterQueue \$ sudo apt-get install build-essential python-dev libnetfilter-queue-dev &&sudo apt-get install python-pip sudo pip install NetfilterQueuesudo pip install dpkt #开始观测 mkdir computer_network_observecd computer_network_observe 遇到的问题 操作系统&编译原理 执行from netfilterqueue import NetfilterQueue遇到编译错误1ImportError: /usr/local/lib/python2.6/dist-packages/netfilterqueue.so: undefined symbol: nfq_set_verdict2 网络上并没有现成的信息解决该问题,简单记录下解决该问题的详细思路。#reference-详细错误信息: ImportError: /usr/local/lib/python2.6/dist-packages/netfilterqueue.so: undefined symbol: nfq_set_verdict2 #16 netfilterqueue importing makes compile error #reference-nm 目标文件格式分析#reference-ldd ldd命令#reference-Linux Shell脚本Ldd命令原理使用方法#reference-浅析ldd命令 定义问题尝试理解错误信息,问题的是netfilterqueue.so有未定义的符号nfq_set_verdict2,而.so文件是编译的中间过程文件。so stands for shared object。 链接可在编译(compile-time)、加载(load-time)或运行(run-time)时执行。顺序:标准的编译过程是基于脚本时利用cpp撰写的:#reference-C语言的编译链接过程详解#reference-从源代码到可执行文件全过程当脚本来自python:可以利用python库,通过”.pyx”文件生成c,最后编译为机器码(二进制)PythonCython应用手记 ##静态链接输入:可重定位的目标文件(object file),命令行参数输出:可执行的目标文件 输入的可重定位目标文件由各种不同的代码和数据节(section)组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。我们查看NetfilterQueue的文件和步骤发现,”.c”,”.pxd”,”.pyx”主要用于编译NetfilterQueue,因此问题应该主要来自对系统的依赖文件。对系统的依赖主要是这些: NetfilterQueue is a C extention module that links against libnetfilter_queue. Before installing, ensure you have: A C compiler Python development files Libnetfilter_queue development files and associated dependencies On Debian or Ubuntu, install these files with: apt-get install build-essential python-dev libnetfilter-queue-dev 由于.c是由.pyx生成的,.pxd定义了函数模版查看.pyx 文件中可以发现nfq_set_verdict2从未被定义,即,它主要来自其它头文件(.h)的include,.pxd 发现该变量来自cdef extern from "libnetfilter_queue/libnetfilter_queue.h": 怀疑是由于是开源项目,内核版本的升级过程也将一些类库升级,却不能向前兼容。于是,升级内核libnetfilter_queue版本。参考libnefilter-queue起步走,下载最新的对应项目 libmnl-1.0.4 libnetfilter_queue-1.0.2 libnfnetlink-1.0.1 下载相关项目: \$ wget http://netfilter.org/projects/libmnl/files/libmnl-1.0.4.tar.bz2\$ wget http://netfilter.org/projects/libnetfilter_queue/files/libnetfilter_queue-1.0.2.tar.bz2\$ wget http://netfilter.org/projects/libnfnetlink/files/libnfnetlink-1.0.1.tar.bz2 按顺序执行安装 libmnl-1.0.4 \$ tar -jxvf libmnl-1.0.4.tar.bz2\$ cd libmnl-1.0.4/\$ ./configure\$ make\$ sudo make install libnetfilter_queue-1.0.2 \$ tar -jxvf libnetfilter_queue-1.0.2.tar.bz2\$ cd libnetfilter_queue-1.0.2/\$ ./configure\$ make\$ sudo make install libnfnetlink-1.0.1 \$ tar -jxvf libnfnetlink-1.0.1.tar.bz2\$ cd libnfnetlink-1.0.1/\$ ./configure\$ make\$ sudo make install 安装完依旧是不生效的。那么计算机是如何找到这个运行时的文件夹就很关键了。参考libnetfilter_queue setup(安装向导)和编译中接触到 pkg config在 /etc/ld.so.conf 文件中添加库的搜索路径/usr/local,并执行/sbin/ldconfig 命令。 #reference-linux各文件夹的作用执行成功。 搬瓦工机器有问题pip update后,安装netfilterqueue成功,发现和root权限有根本性的差异。打算重新sudo pip install netfilterqueue 先运行计算机观测。(从安装pip开始) 问题相信信息: #reference-NetfilterQueue tiped “are you root ” ,which I was already the “root”证据 sudo which python/usr/bin/pythonwhich python/root/canopy/bin/python 结果两个都问了,are you root. 这就很尴尬了。 import osif os.geteuid() != 0: print “This program must be run as root. Aborting.” sys.exit(1) 证明我已经是root。 cd /temp尝试重新安装所有依赖,并改目标目录。失败。 Linux 下用户权限设置#reference-permission denied errorhttps://www.google.com.hk/search?newwindow=1&c2coff=1&safe=strict&hl=zh-CN&q=+visudo+permission+de&oq=+visudo+permission+de&gs_l=serp.3..0j0i30k1l3j0i8i30k1l3j0i5i30k1l2.290009.295367.0.296570.17.13.0.0.0.0.558.2178.2-3j1j1j1.6.0....0...1c.1.64.serp..11.6.2174...0i13k1j0i7i30k1j0i7i10i30k1j35i39k1.3Yf3P28PMq0#reference-linux下添加用户并赋予root权限 脚本12import sqlprint 'f' #思考其实我目前遇到的问题都是范运维问题,能否通过docker容器解决呢?如果解决是否有机会产品经理学习计算机知识的市场呢?其实还蛮有趣的。]]></content>
</entry>
<entry>
<title><![CDATA[购买域名+服务器+部署服务器+实现ssl协议]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F09%2F%E8%B4%AD%E4%B9%B0%E5%9F%9F%E5%90%8D%2B%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E5%AE%9E%E7%8E%B0ssl%E5%8D%8F%E8%AE%AE%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F09%2F%E8%B4%AD%E4%B9%B0%E5%9F%9F%E5%90%8D%2B%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E5%AE%9E%E7%8E%B0ssl%E5%8D%8F%E8%AE%AE%2F</url>
-->
<url>/2016%2F10%2F09%2F%E8%B4%AD%E4%B9%B0%E5%9F%9F%E5%90%8D%2B%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E5%99%A8%2B%E5%AE%9E%E7%8E%B0ssl%E5%8D%8F%E8%AE%AE%2F</url>
<content type="text"><![CDATA[纪录部署busihacker.com的一些问题 两条a记录主要是为了负载均衡吧?常用域名记录解释:A记录、MX记录、CNAME记录、TXT记录、AAAA记录、NS记录如何建立个人网站?新手教程:建立网站的全套流程与详细解释2014年网站SEO常见作弊方法详细解析Godaddy注册商域名修改DNS地址管理DNS个人godaddy域名备案解决方案godaddy 对域名的所有权以及备案问题。免费的是最贵的免费域名如何抢注一个刚刚过期的域名 SSL协议与加密SSL/TLS协议运行机制的概述HTTPS 升级指南PGP加密原理GPG入门教程关于pgpRSA算法原理RSA算法原理http协议与ssl协议[TCP三次握手&Render Tree页面渲染=>从输入URL到页面显示的过程?]不同format的certificate和private/public key 使用startssl认证证书站点启用https替换http完整步骤(申请StartSSL免费证书,nginx配置)怎样申请StartSSL免费ssl证书SSL Certificate ReviewsStartSSL申请全过程 让网站拥有免费SSL证书现在就启用 HTTPS,免费的!how to obtain and install an SSL/TLS certificate for freeopenssl、x509、crt、cer、key、csr、ssl、tls 这些都是什么鬼?crt csr 谷歌搜索伪造 证书 谷歌搜索tornado使用nginx作为反向代理公钥 私钥 原理 谷歌搜索rsa pop关系 谷歌搜索使用Tornado搭建HTTPS网站How to create HTTPS tornado serverSSL和SSH的区别SSL SSH 谷歌搜索generating-a-gpg-keygenerating-an-ssh-key ipython notebookWhen using a password, it is a good idea to also use SSL, so that your password is not sent unencrypted by your browser. You can start the notebook to communicate via a secure protocol mode using a self-signed certificate with the command: $ ipython notebook –certfile=mycert.pem A self-signed certificate can be generated with openssl. For example, the following command will create a certificate valid for 365 days with both the key and certificate data written to the same file: $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem 远程服务下一步:部署到域名进行访问busihacker.com认证ssl/tls证书https://<your-domain>:9999]]></content>
</entry>
<entry>
<title><![CDATA[前端学习笔记]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F09%2F%E5%89%8D%E7%AB%AF%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F09%2F%E5%89%8D%E7%AB%AF%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%2F</url>
-->
<url>/2016%2F10%2F09%2F%E5%89%8D%E7%AB%AF%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[前段学习的一些问题和摘要 Chrome ConsoleJavascriptCSSDOMHTMLReact Natie微信小程序小程序 native #jquery]]></content>
</entry>
<entry>
<title><![CDATA[区块链(block chain)调研]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F09%2F%E5%8C%BA%E5%9D%97%E9%93%BE%EF%BC%88block%20chain%EF%BC%89%E8%B0%83%E7%A0%94%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F09%2F%E5%8C%BA%E5%9D%97%E9%93%BE%EF%BC%88block%20chain%EF%BC%89%E8%B0%83%E7%A0%94%2F</url>
-->
<url>/2016%2F10%2F09%2F%E5%8C%BA%E5%9D%97%E9%93%BE%EF%BC%88block%20chain%EF%BC%89%E8%B0%83%E7%A0%94%2F</url>
<content type="text"><![CDATA[区块链和大数据、云计算结合貌似可以有作为,开个头。 %人工智能、大数据和云计算分别指的是什么 Web2Web Bootstrap 笑来兄在微信公号发布的,torren不依附于任何服务器,很难将其关闭,或许这就是未来信息存储和传播的主要方式,永久保存不消失,创造永恒的信息世界 挖矿石如何产生比特币的比特币挖矿算法是怎样的?比特币如何挖矿区块链(Blockchain)知乎 主题精华比特币 挖矿]]></content>
</entry>
<entry>
<title><![CDATA[git的一些坑]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F09%2Fgit%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F09%2Fgit%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91%2F</url>
-->
<url>/2016%2F10%2F09%2Fgit%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91%2F</url>
<content type="text"><![CDATA[weibo的git使用笔记。 Git远程操作详解Git 使用规范流程git rebase 阮一峰 谷歌搜索Error:cannot pull with rebase:you have unstated changesyou have unstated changesgit workflow 常用命令多人协作分支管理策略管理修改 探索.git目录 git diff(谷歌)diff 原理Diff 算法的原理是什么, 怎样学习和理解?diff程序的算法读懂diff(阮一峰)Interpreting git diff -cc[was:merge conflict]git diff –cc 怎么读sublime diff20 个强大的 Sublime Text 插件 #git push –force 的使用场景git checkout -b devgit add -allgit checkout master initialization后,从未git commit ,checkout后git branch –list 也看不到自己的分支。如何在git里撤销几乎任何操作 #文件的命名不要带有/windows下不接受这种命名导致git pull 后git rm,永远无法和remote repository保持一致。这个时候可能一定会遇到push force问题。(由于是没用的文件,我直接舍弃了该文件。) ##git push –force 的使用场景push错误后,git 怎样删除远程仓库的某次错误提交?push后撤销 ## Why are my commits linked to the wrong user? ##常规步骤起步 - 初次运行 Git 前的配置Set Up GitGenerating a new SSH key and adding it to the ssh-agentMERGE STRATEGIES准备合并dev分支,请注意–no-ff参数,表示禁用Fast forward:‘’’$ git merge –no-ff -m “merge with no-ff” devMerge made by the ‘recursive’ strategy. readme.txt | 1 + 1 file changed, 1 insertion(+)‘’’分支管理策略]]></content>
</entry>
<entry>
<title><![CDATA[文字识别初探]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F08%2F%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%E5%88%9D%E6%8E%A2%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F08%2F%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%E5%88%9D%E6%8E%A2%2F</url>
-->
<url>/2016%2F10%2F08%2F%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%E5%88%9D%E6%8E%A2%2F</url>
<content type="text"><![CDATA[随着中国互联网对广告的审核越来越严格,日常投放运营过程中,渠道对资质文件和创意的要求越来越高,如营业执照、身份证等。我们需要花费大量的人力成本对这些文件的属性进行文本描述,如营业主题,姓名,生日等。有没有一种方案对文件进行快速的描述,减少人力成本? 另一方面,这个需求是具有可扩展性的。图形学方向上的人工智能,是建立在图像识别基础上。计算广告日后必然和图形学和人工智能强关联,正好探索这个方向的可行性,并把该躺的坑给躺一遍。 作为一个产品,会先对一些开源的框架以及开放的 API做基本的可行性调研。随手Google之后,决定本地使用Tesseract观察目前OCR(Optical Character Recognition)引擎效果。 #reference-浅谈OCR之Tesseract#reference-Tesseract:安装与命令行使用 安装通过homebrew安装到本地/usr/local/Cellar #reference-homebrew brew install tesseract 直接识别尝试识别如下图片: 执行tesseract <%path_to_pic> stdout后得到。 可以看到英文和数字的识别正确率很高,中文识别这么不靠谱?显然不是,不同语言的文字识别机制可能不一样。Google后发现需要指定语言后识别。由于我们的目标是中文识别,必须解决语言包问题。当然可以自行选择自己愿意安装的语言包,但最方便的是支持所有available的语言包。重新执行brew install tesseract --all-languages 安装所有语言包。 #reference-Chinese OCR#reference-利用Tesseract图片文字识别初探#reference-ocr markdown 现在我们执行tesseract --list-langs,可以看到 $ tesseract –list-langsList of available languages (107):…chi_simchi_tra… 简繁体的中文识别目前都已经支持。由于是大陆环境,我们将主要是用简体中文语言包进行识别。 中文识别再次通过语言包尝试识别,执行tesseract <%path_to_pic> stdout -l chi_sim 显然,准确率大大提高。虽然如此,可用性还是不足以支撑生产环境的需求。 分块后中文识别会不会因为单词识别文本太多,导致识别不够准确呢?毕竟即使是人,当看到一大块文本的时候也感到头晕目眩。我们把原文本一分为三后,再次识别。 显然准确率有所提高。虽然也还是无法使用:( 如何分块那么我们应该如何对一张图片中的文本作分块呢?简单来说,就是利用opencv,看出来白色的色块 然后定位做文字识别。 #后续探索 后续可以进行探索的方向: OCR 中文识别用哪种软件识别率比较高? image++api 如何更合适地对图片的文本区域分块 根据图片直接判断语言 原文发布于:文字识别初探]]></content>
</entry>
<entry>
<title><![CDATA[【转】翻墙路由器的原理与实现]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F06%2F%E3%80%90%E8%BD%AC%E3%80%91%E7%BF%BB%E5%A2%99%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F06%2F%E3%80%90%E8%BD%AC%E3%80%91%E7%BF%BB%E5%A2%99%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0%2F</url>
-->
<url>/2016%2F10%2F06%2F%E3%80%90%E8%BD%AC%E3%80%91%E7%BF%BB%E5%A2%99%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0%2F</url>
<content type="text"><![CDATA[转自:http://drops.wooyun.org/papers/10177 0×00 开篇GFW具有重大的社会意义。无论是正面的社会意义,还是负面的意义。无论你是讨厌,还是憎恨。它都在那里。在可以预见的将来,墙还会继续存在。我们要学会如何与其共存。我是一个死搞技术的,就是打算搞技术到死的那种人。当我读到西厢计划的博客上的这么一段话时,我被深深的触动了。不是为了什么政治目的,不是为了什么远大理想,仅仅做为一个死搞技术的人显摆自己的价值,我也必须做些什么。博客上的原话是这么写的: 作为个搞技术的人,我们要干点疯狂的事。如果我们不动手,我们就要被比我们差的远的坏技术人员欺负。这太丢人了。眼前就是,GFW这个东西,之前是我们不抱团,让它猖狂了。现在咱们得凑一起,想出来一个办法让它郁闷一下,不能老被欺负吧。要不,等到未来,后代会嘲笑我们这些没用的家伙,就象我们说别人“你怎么不反抗?” 我把翻墙看成一场我们与GFW之间的博弈,是一个不断对抗升级的动态过程。目前整体的博弈态势来讲是GFW占了绝对的上风。我们花费了大量的金钱(买VPS买VPN),花费大量时间(学习各种翻墙技术),而GFW只需要简单发几个包,配几个路由规则就可以让你的心血都白费。 GFW并不需要检查所有的上下行流量中是不是有不和谐的内容,很多时候只需要检查连接的前几个包就可以判断出是否要阻断这个连接。为了规避这种检查,我们就需要把所有的流量都通过第三方代理,还要忍受不稳定,速度慢等各种各样的问题。花费的是大量的研究的时间,切换线路的时间,找出是什么导致不能用的时间,当然还有服务器的租用费用和带宽费用。我的感觉是,这就像太极里的四两拨千斤。GFW只需要付出很小的成本,就迫使了我们去付出很大的反封锁成本,而且这种成本好像是越来越高了。 这场博弈的不公平之处在于,GFW拥有国家的资源和专业的团队。而我们做为个体,愿意花费在翻墙上的时间与金钱是非常有限的。在竞争激烈的北上广深,每天辛苦忙碌的白领们。翻墙无非是为了方便自己的工作而已。不可能在每天上下班从拥挤的地铁中挤出来之后再去花费已经少得可怜的业余时间去学习自己不是翻墙根本不需要知道的名词到底是什么意思。于是乎,我们得过且过。不用Google也不会死,对不对。SSH加浏览器设置,搞一搞也就差不多能用就行啦。但是得过且过也越来越不好过了。从最开始的HTTP代理,到后来的SOCKS代理,到最近的OpenVPN,一个个阵亡。普通人可以使用的方式越来越少。博弈的天平远远不是平衡的,而是一边倒。 GFW用技术的手段达到了四两拨千斤的作用。难道技术上就没有办法用四两拨千斤的方法重新扭转这一边倒的局面吗? 办法肯定是有的。我能想到的趋势是两个。第一个趋势是用更复杂的技术,但是提供更简单的使用方式。简单的HTTP代理,SOCKS明文代理早已阵亡。接下来的斗争需要更复杂的工具。无论是ShadowSocks还是GoAgent都在向这个方向发展。技术越复杂,意味着普通人要学习要配置的成本就越高。每个人按照文档,在自己的PC上配置ABC的方式已经不能满足下一阶段的斗争需要了。我们需要提升手里的武器,站在一个更高的平台上。 传统的配置方式的共同特点是终端配置。你需要在你的PC浏览器上,各种应用软件里,手机上,平板电脑上做各种各样的配置。这样的终端配置的方式在过去是很方便的。别人提供一个代理,你在浏览器里一设置就好用了。但是在连OpenVPN都被封了的今天,这种终端配置的方式就大大限制了我们的选择。缺点是多方面的: 翻墙的方式受到终端支持的限制。特别是手机和平板电脑,不ROOT不越狱的话,选择就非常有限了。 终端种类繁多,挂一漏万。提供翻墙的工具的人不可能有精力来测试支持所有种类的终端。 如果家里有多个笔记本,还有手机等便携设备使用起来就很不方便。躺在床上要刷Twitter的时候,才发现手机的里的OpenVPN帐号已经被封了,新的那个只配置在了电脑里。 最主流的终端是Windows的PC机。但是在Windows上控制底层网络的运作非常不方便。给翻墙工具的作者设置了一个更高的门槛。 终端一般处于家庭路由器的后面。大多数直穿的穿墙方式都很难在这种网络环境下工作正常。 把翻墙工具做到路由器上就可以达到实现更复杂的翻墙技术,同时提供极其简单的使用体验。但是路由器的缺陷也是非常明显的。传统的路由器刷OpenWRT等可以定制的第三方系统有如下缺点: 便携不方便,路由器大部分没有电池,也不方便放在包里 相比在电脑上装一个软件试试好不好使,额外购买专门用来翻墙的路由器未免试用成本也太高了。如果没有人愿意尝试,更加不会有人来使用。 路由器安装软件不方便。笔者花了大量时间研究OpenWRT的USB刷机方式。虽然技术上有所突破,但是仍然感觉不适合普通人操作。 硬件受限。路由器的CPU都很慢。内存非常小。如果不是用C来编写应用,速度会非常慢。极大地抬高了开发成本。流行的翻墙工具GoAgent和shadowsocks的最初版本都是Python的。 有没有既可以获得路由器的好处,又克服了其缺点的解决方案呢?答案是肯定的。手机做为路由器就可以。目前fqrouter已经推出了Android版本,把手机变成了翻墙路由器。一方面,完成了平台的跃升,从终端翻墙变为了路由器翻墙。另外一方面,因为手机的便携,无需额外设备,安装软件简单,而且硬件强大完胜了常规意义上的路由器。使用手机做为路由器之后: 翻墙方式不再受到终端的限制。只要能接入路由器,就可以翻墙。 提供翻墙工具的人不需要测试所有的终端是不是支持。 多种终端可以同时共享一个路由器。无需重复配置。 路由器基于的Linux操作系统给翻墙工具的作者提供了极大的便利,新的工具可以更容易地被实现出来。 提供了一定的直穿的可能性。 iPhone, Windows Phone等设备不需要越狱,也可以通过翻墙路由器享受到shadowsocks等更高级的翻墙工具。 运行在Android上的翻墙工具fqrouter已经在Google Play上架了:https://play.google.com/store/apps/details?id=fq.router2 这是趋势一,平台的提升。第二个趋势是去中心化。我相信未来的趋势肯定不是什么境外敌对势力出于不可告人的目的给我们提供翻墙方式。未来的趋势是各自为战的,公开贩卖的各种翻墙服务会被封杀殆尽。我们要确保的底线是,做为个人,在拥有一台国外服务器,然后有一定技术能力的情况下,能够稳定无忧的翻墙。 在我们能够保证独善其身的前提下,才有可能怎么去达则兼善天下。才有可能以各自为圆心,把服务以P2P的方式扩散给亲朋好友使用。即便是能够有这样的互助网络建立起来,也肯定是一种去中心化的,开源的实现。只有遍地开花,才能避免被连根拔起。 前面谈到路由器刷第三方固件对于个人来说不是理想的翻墙路由器的实现方式。但是固定部署的路由器却是理想的P2P节点。P2P的一个简化版本是APN,也就是把代理放在国内,然后iPhone等可以简单地使用HTTP未加密方式使用代理。这种部署方式就比较适合刷在固定部署的路由器上。个人可以在自己家里的路由器上部署了代理,然后无论走到哪里都可以通过家里的路由器代理上网。使用路由器固定部署P2P节点的好处是P2P网络可以有更多的稳定接入点。这些刷了OpenWRT等第三方系统安装了P2P节点程序的路由器不会是普通人玩得转的。其意义更多是有技术实力的志愿者,提供自己的家庭路由器,以换得其他方面的方便。 实现一个P2P的网络的难点有三个: 代理服务器的容量有限。传统的代理服务器是无法负载很多人同时用1080P看youtube的,因为带宽不够。不要说免费的P2P网络,就是很多付费的代理服务,也无法满足容量要求。 中心服务器被封IP。TOR做为著名的P2P网络,其主要问题就是要接入其网络需要连接一个中心服务器。这些服务器的IP数量是有限的。GFW会尽一切力量找到这些IP,然后封IP。 P2P意味着索取与奉献。人人都想这索取,为什么会有人奉献?如果没有一个等价交换做为社区的基础,这个社区是无法长久的。 目前仍然没有理想的P2P翻墙方式出现。但是这是fqrouter的努力方向。 中心化的翻墙方式,特别是商业贩卖的翻墙服务注定难逃被捕杀殆尽的命运。具有光明未来的翻墙方式必然是去中心化的,松散的,自组织的P2P的。 0×01 全面学习GFWGFW会是一个长期的存在。要学会与之共存,必须先了解GFW是什么。做为局外人,学习GFW有六个角度。渐进的来看分别是: 首先我们学习到的是WHAT和WHEN。比如说,你经常听到人的议论是“昨天”,“github”被封了。其中的昨天就是WHEN,github就是WHAT。这是学习GFW的最天然,最朴素的角度。在这个方面做得非常极致的是一个叫做greatfire的网站。这个网站长期监控成千上万个网站和关键词。通过长期监控,不但可以掌握WHAT被封锁了,还可以知道WHEN被封的,WHEN被解封的。 接下来的角度是WHO。比如说,“方校长”这个人名就经常和GFW同时出现。但是如果仅仅是掌握一个两个人名,然后像某位同志那样天天在twitter上骂一遍那样,除了把这个人名骂成名人之外,没有什么特别的积极意义。我更看好这篇文章通过分析论文挖掘防火长城(GFW)的技术人员%E7%9A%84%E6%8A%80%E6%9C%AF%E4%BA%BA%E5%91%98&gws_rd=ssl)的思路。通过网络上的公开信息,掌握GFW的哪些方面与哪些人有关系,这些合作者之间又有什么联系。除了大家猜测的将来可以鞭尸之外,对现在也是有积极的意义的。比如关注这些人的研究动态和思想发展,可以猜测GFW的下一步发展方向。比如阅读过去发表的论文,可以了解GFW的技术演进历史,可以从历史中找到一些技术或者管理体制上的缺陷。 再接下来就是WHY了。github被封之后就常听人说,github这样的技术网站你封它干啥?是什么原因促成了一个网站的被封与解封的?我们做为局外人,真正的原因当然是无从得知的。但是我们可以猜测。基于猜测,可以把不同网站被封,与网络上的舆情时间做关联和分类。我们知道,方校长对于网路舆情监控是有很深入研究的。有一篇论文(Whiskey, Weed, and Wukan on the World Wide Web: On Measuring Censors’ Resources and Motivations)专门讨论监管者的动机的。观测触发被封的事件与实际被封之间的时间关系,也可以推测出一些有趣的现象。比如有人报告,OpenVPN触发的封端口和封IP这样的事情一般都发生在中国的白天。也就是说,GFW背后不光是机器,有一些组件是血肉构成的。 剩下的两个角度就是对如何翻墙穿墙最有价值的两个角度了:HOW和WHERE。HOW是非常好理解的,就是在服务器和客户端两边抓包,看看一个正常的网络通信,GFW做为中间人,分别给两端在什么时候发了什么包或者过滤掉了什么包。而这些GFW做的动作,无论是过滤还是发伪包又是如何干扰客户端与服务器之间的正常通信的。WHERE是在知道了HOW之后的进一步发展,不但要了解客户端与服务器这两端的情况,更要了解GFW是挂在两端中间的哪一级路由器上做干扰的。在了解到GFW的关联路由器的IP的基础上,可以根据不同的干扰行为,不同的运营商归属做分组,进一步了解GFW的整体部署情况。 整体上来说,对GFW的研究都是从WHAT和WHEN开始,让偏人文的就去研究WHO和WHY,像我们这样偏工程的就会去研究HOW和WHERE。以上就是全面了解GFW的主体脉络。接下来,我们就要以HOW和WHERE这两个角度去看一看GFW的原理。 0×02 GFW的原理要与GFW对抗不能仅仅停留在什么不能访问了,什么可以访问之类的表面现象上。知道youtube不能访问了,对于翻墙来说并无帮助。但是知道GFW是如何让我们不能访问youtube的,则对下一步的翻墙方案的选择和实施具有重大意义。所以在讨论如何翻之前,先要深入原理了解GFW是如何封的。 总的来说,GFW是一个分布式的入侵检测系统,并不是一个严格意义上的防火墙。不是说每个出入国境的IP包都需要先经过GFW的首可。作为一个入侵检测系统,GFW把你每一次访问facebook都看做一次入侵,然后在检测到入侵之后采取应对措施,也就是常见的连接重置。整个过程一般话来说就是: 检测有两种方式。一种是人工检测,一种是机器检测。你去国新办网站举报,就是参与了人工检测。在人工检测到不和谐的网站之后,就会采取一些应对方式来防止国内的网民访问该网站。对于这类的封锁,规避检测就不是技术问题了,只能从GFW采取的应对方式上采取反制措施。另外一类检测是机器检测,其检测过程又可以再进一步细分: 重建重建是指GFW从网络上监听过往的IP包,然后分析其中的TCP协议,最后重建出一个完整的字节流。分析是在这个重建的字节流上分析具体的应用协议,比如HTTP协议。然后在应用协议中查找是不是有不和谐的内容,然后决定采用何种应对方式。 所以,GFW机器检测的第一步就是重建出一个字节流。那么GFW是如何拿到原始的IP包的呢?真正的GFW部署方式,外人根本无从得知。据猜测,GFW是部署在国家的出口路由器的旁路上,用“分光”的方式把IP包复制一份到另外一根光纤上,从而拿到所有进出国境的IP包。下图引在@GFW技术评论: 但是Google在北京有自己的机房。所以聪明的网友就使用Google的北京机房提供的GAE服务,用Goagent软件达到高速翻墙的目的。但是有网友证实(https://twitter.com/chengr28/status/260970749190365184),即便是北京的机房也会被骨干网丢包。事实上Google在北京的谷翔机房有一个独立的AS(BGP的概念)。这个AS与谷歌总部有一条IPV6的直连线路,所以通过这个机房可以用IPV6不受墙的限制出去。但是这个AS无论是连接国内还是国外都是要经过GFW的。所以机房在北京也不能保证国内访问不被墙。GFW通过配置骨干网的BGP路由规则,是可以让国内的机房也经过它的。另外一个例子是当我们访问被封的网站触发连接重置的时候,往往收到两个RST包,但是TTL不同。还有一个例子是对于被封的IP,访问的IP包还没有到达国际出口就已经被丢弃。所以GFW应该在其他地方也部署有设备,据推测是在省级骨干路由的位置。 对于GFW到底在哪这个话题,最近又有国外友人表达了兴趣(https://github.com/mothran/mongol)。笔者在前人的基础上写了一个更完备的探测工具https://github.com/fqrouter/qiang。其原理是基于一个IP协议的特性叫TTL。TTL是Time to Live的简写。IP包在没经过一次路由的时候,路由器都会把IP包的TTL减去1。如果TTL到零了,路由器就不会再把IP包发给下一级路由。然后我们知道GFW会在监听到不和谐的IP包之后发回RST包来重置TCP连接。那么通过设置不同的TTL就可以知道从你的电脑,到GFW之间经过了几个路由器。比如说TTL设置成9不触发RST,但是10就触发RST,那么到GFW就是经过了10个路由器。另外一个IP协议的特性是当TTL耗尽的时候,路由器应该发回一个TTL EXCEEDED的ICMP包,并把自己的IP地址设置成SRC(来源)。结合这两点,就可以探测出IP包是到了IP地址为 什么(多少)的路由器之后才被GFW检测到。有了IP地址之后,再结合IP地址地理位置的数据库就可以知道其地理位置。据说,得出的位置大概是这样的: 但是这里检测出来的IP到底是GFW的还是骨干路由器的?更有可能的是骨干路由器的IP。GFW做为一个设备用“分光”的方式挂在主干路由器旁边做入侵检测。无论如何,GFW通过某种神奇的方式,可以拿到你和国外服务器之间来往的所有的IP包,这点是肯定的。更严谨的理论研究有:Internet Censorship in China: Where Does the Filtering Occur GFW在拥有了这些IP包之后,要做一个艰难的决定,那就是到底要不要让你和服务器之间的通信继续下去。GFW不能太过于激进,毕竟全国性的不能访问国外的网站是违反GFW自身存在价值的。GFW就需要在理解了IP包背后代表的含义之后,再来决定是不是可以安全的阻断你和国外服务器之间的连接。这种理解就要建立了前面说的“重建”这一步的基础上。大概用图表达一下重建是在怎么一回事: 重建需要做的事情就是把IP包1中的GET /inde和IP包2中的x.html H和IP包3中的TTP/1.1拼到一起变成GET /index.html HTTP/1.1。拼出来的数据可能是纯文本的,也可能是二进制加密的协议内容。具体是什么是你和服务器之间约定好的。GFW做为窃听者需要猜测才知道你们俩之间的交谈内容。对于HTTP协议就非常容易猜测了,因为HTTP的协议是标准化的,而且是未加密的。所以GFW可以在重建之后很容易的知道,你使用了HTTP协议,访问的是什么网站。 重建这样的字节流有一个难点是如何处理巨大的流量?这个问题在这篇博客中已经讲得很明白了。其原理与网站的负载均衡器一样。对于给定的来源和目标,使用一个HASH算法取得一个节点值,然后把所有符合这个来源和目标的流量都往这个节点发。所以在一个节点上就可以重建一个TCP会话的单向字节流。 最后为了讨论完整,再提两点。虽然GFW的重建发生在旁路上是基于分光来实现的,但并不代表整个GFW的所有设备都在旁路。后面会提到有一些GFW应对形式必须是把一些GFW的设备部署在了主干路由上,比如对Google的HTTPS的间歇性丢包,也就是GFW是要参与部分IP的路由工作的。另外一点是,重建是单向的TCP流,也就是GFW根本不在乎双向的对话内容,它只根据监听到的一个方向的内容然后做判断。但是监听本身是双向的,也就是无论是从国内发到国外,还是从国外发到国内,都会被重建然后加以分析。所以一个TCP连接对于GFW来说会被重建成两个字节流。具体的证据会在后面谈如何直穿GFW中详细讲解。 分析分析是GFW在重建出字节流之后要做的第二步。对于重建来说,GFW主要处理IP协议,以及上一层的TCP和UDP协议就可以了。但是对于分析来说,GFW就需要理解各种各样的应用层的稀奇古怪的协议了。甚至,我们也可以自己发明新的协议。 总的来说,GFW做协议分析有两个相似,但是不同的目的。第一个目的是防止不和谐内容的传播,比如说使用Google搜索了“不该”搜索的关键字。第二个目的是防止使用翻墙工具绕过GFW的审查。 下面列举一些已知的GFW能够处理的协议。 防止不和谐内容传播对于GFW具体是怎么达到目的一,也就是防止不和谐内容传播的就牵涉到对HTTP协议和DNS协议等几个协议的明文审查。大体的做法是这样的。 像HTTP这样的协议会有非常明显的特征供检测,所以第一步就没什么好说的了。当GFW发现了包是HTTP的包之后就会按照HTTP的协议规则拆包。这个拆包过程是GFW按照它对于协议的理解来做的。比如说,从HTTP的GET请求中取得请求的URL。然后GFW拿到这个请求的URL去与关键字做匹配,比如查找Twitter是否在请求的URL中。为什么有拆包这个过程?首先,拆包之后可以更精确的打击,防止误杀。另外可能预先做拆包,比全文匹配更节省资源。其次,xiaoxia和liruqi同学的jjproxy的核心就是基于GFW的一个HTTP拆包的漏洞,当然这个bug已经被修复了。其原理就是GFW在拆解HTTP包的时候没有处理有多出来的\r\n这样的情况,但是你访问的google.com却可以正确处理额外的\r\n的情况。从这个例子中可以证明,GFW还是先去理解协议,然后才做关键字匹配的。关键字匹配应该就是使用了一些高效的正则表达式算法,没有什么可以讨论的。 HTTP代理和SOCKS代理,这两种明文的代理都可以被GFW识别。之前笔者认为GFW可以在识别到HTTP代理和SOCKS代理之后,再拆解其内部的HTTP协议的正文。也就是做两次拆包。但是分析发现,HTTP代理的关键字列表和HTTP的关键字列表是不一样的,所以笔者现在认为HTTP代理协议和SOCKS代理协议是当作单独的协议来处理的,并不是拆出载荷的HTTP请求再进行分析的。 目前已知的GFW会做的协议分析如下: DNS 查询GFW可以分析53端口的UDP协议的DNS查询。如果查询的域名匹配关键字则会被DNS劫持。可以肯定的是,这个匹配过程使用的是类似正则的机制,而不仅仅是一个黑名单,因为子域名实在太多了。证据是:2012年11月9日下午3点半开始,防火长城对Google的泛域名 .google.com 进行了大面积的污染,所有以 .google.com 结尾的域名均遭到污染而解析错误不能正常访问,其中甚至包括不存在的域名。(来源:http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E5%8A%AB%E6%8C%81) 目前为止53端口之外的查询也没有被劫持。但是TCP的DNS查询已经可以被TCP RST切断了,表明了GFW具有这样的能力,只是不屑于大规模部署。而且TCP查询的关键字比UDP劫持的域名要少的多。目前只有dl.dropbox.com会触发TCP RST。相关的研究论文有: Hold-On: Protecting Against On-Path DNS Poisoning The Great DNS Wall of China HTTP 请求GFW可以识别出HTTP协议,并且检查GET的URL与HOST。如果匹配了关键字则会触发TCP RST阻断。前面提到了jjproxy使用的构造特殊的HTTP GET请求欺骗GFW的做法已经失效,现在GFW只要看到\r\n就直接TCP RST阻断了(来源https://plus.google.com/u/0/108661470402896863593/posts/6U6Q492M3yY)。相关的研究论文有: The Great Firewall RevealedIgnoring the Great Firewall of ChinaHTTP URL/深度关键字检测ConceptDoppler: A Weather Tracker for Internet Censorship HTTP 响应GFW除了会分析上行的HTTP GET请求,对于HTTP返回的内容也会做全文关键字检查。这种检查与对请求的关键字检查不是由同一设备完成的,而且对GFW的资源消耗也更大。相关的研究论文有: Empirical Study of a National-Scale Distributed Intrusion Detection System: Backbone-Level Filtering of HTML Responses in China HTTP代理协议TODO SOCKS4/5代理协议TODO SMTP 协议因为有很多翻墙软件都是以邮件索取下载地址的方式发布的,所以GFW有针对性的封锁了SMTP协议,阻止这样的邮件往来。 封锁有三种表现方式(http://fqrouter.tumblr.com/post/43400982633/gfw-smtp),简单概要的说就是看邮件是不是发往上了黑名单的邮件地址的(比如[email protected]就是一个上了黑名单的邮件地址),如果发现了就立马用TCP RST包切断连接。 电驴(ed2k)协议GFW还会过滤电驴(ed2k)协议中的查询内容。因为ed2k还有一个混淆模式,会加密往来的数据包,GFW会切断所有使用混淆模式的ed2k连接,迫使客户端使用明文与服务器通讯(http://fqrouter.tumblr.com/post/43490772120/gfw-ed2k)。然后如果客户端发起了搜索请求,查找的关键字中包含敏感词的话就会被用TCP RST包切断连接。 对翻墙流量的分析识别GFW的第二个目的是封杀翻墙软件。为了达到这个目的GFW采取的手段更加暴力。原因简单,对于HTTP协议的封杀如果做不好会影响互联网的正常运作,GFW与互联网是共生的关系,它不会做威胁自己存在的事情。但是对于TOR这样的几乎纯粹是为翻墙而存在的协议,只要检测出来就是格杀勿论的了。GFW具体是如何封杀各种翻墙协议的,我也不是很清楚,事态仍然在不断更新中。但是举两个例子来证明GFW的高超技术。 TOR第一个例子是GFW对TOR的自动封杀,体现了GFW尽最大努力去理解协议本身。根据这篇博客(https://blog.torproject.org/blog/knock-knock-knockin-bridges-doors)。使用中国的IP去连接一个美国的TOR网桥,会被GFW发现。然后GFW回头(15分钟之后)会亲自假装成客户端,用TOR的协议去连接那个网桥。如果确认是TOR的网桥,则会封当时的那个端口。换了端口之后,可以用一段时间,然后又会被封。这表现出了GFW对于协议的高超检测能力,可以从国际出口的流量中敏锐地发现你连接的TOR网桥。据TOR的同志说是因为TOR协议中的握手过程具有太明显的特征了。另外一点就表现了GFW的不辞辛劳,居然会自己伪装成客户端过去连连看。 ShadowSocks第二个例子表现了GFW根本不在乎加密的流量中的具体内容是不是有敏感词。只要疑似翻墙,特别是提供商业服务给多个翻墙,就会被封杀。根据这个帖子(http://www.v2ex.com/t/55531),使用的ShadowSocks协议。预先部署密钥,没有明显的握手过程仍然被封。据说是GFW已经升级为能够机器识别出哪些加密的流量是疑似翻墙服务的。 关于GFW是如何识别与封锁翻墙服务器的,最近写了一篇文章提出我的猜想,大家可以去看看:http://fqrouter.tumblr.com/post/45969604783/gfw。 OpenVPN & SSL最近发现GFW对OpenVPN和SSL证书已经可以做到准实时的封IP(端口)。原理应该是离线做的深包分析,然后提取出可疑的IP列表,经过人工确认之后封IP。因为OpenVPN有显著的协议的特征,而且基本不用于商业场景所以很容易确认是翻墙服务。但是SSL也就是HTTPS用的加密协议也能基于“证书”做过滤不得不令人感到敬畏了。Shadowsocks的作者Clowwindy为此专门撰文“为什么不应该用SSL翻墙“:https://gist.github.com/clowwindy/5947691。 总结起来就是,GFW已经基本上完成了目的一的所有工作。明文的协议从HTTP到SMTP都可以分析然后关键字检测,甚至电驴这样不是那么大众的协议GFW都去搞了。从原理上来说也没有什么好研究的,就是明文,拆包,关键字。GFW显然近期的工作重心在分析网络流量上,从中识别出哪些是翻墙的流量。这方面的研究还比较少,而且一个显著的特征是自己用没关系,大规模部署就容易出问题。我目前没有在GFW是如何封翻墙工具上有太多研究,只能是道听途说了。 应对GFW的应对措施是三步中最明显的,因为它最直接。GFW的重建过程和协议分析的过程需要耐心的试探才能大概推测出GFW是怎么实现的。但是GFW的应对手段我们每天都可以见到,比如连接重置。GFW的应对目前可以感受到的只有一个目的就是阻断。但是从广义上来说,应对方式应该不限于阻断。比如说记录下日志,然后做统计分析,秋后算账什么的也可以算是一种应对。就阻断方式而言,其实并不多,那么我们一个个来列举吧。 封IP一般常见于人工检测之后的应对。还没有听说有什么方式可以直接使得GFW的机器检测直接封IP。一般常见的现象是GFW机器检测,然后用TCP RST重置来应对。过了一段时间才会被封IP,而且没有明显的时间规律。所以我的推测是,全局性的封IP应该是一种需要人工介入的。注意我强调了全局性的封IP,与之相对的是部分封IP,比如只对你访问那个IP封个3分钟,但是别人还是可以访问这样的。这是一种完全不同的封锁方式,虽然现象差不多,都是ping也ping不通。要观摩的话ping twitter.com就可以了,都封了好久了。 其实现方式是把无效的路由黑洞加入到主干路由器的路由表中,然后让这些主干网上的路由器去帮GFW把到指定IP的包给丢弃掉。路由器的路由表是动态更新的,使用的协议是BGP协议。GFW只需要维护一个被封的IP列表,然后用BGP协议广播出去就好了。然后国内主干网上的路由器都好像变成了GFW的一份子那样,成为了帮凶。 如果我们使用traceroute去检查这种被全局封锁的IP就可以发现,IP包还没有到GFW所在的国际出口就已经被电信或者联通的路由器给丢弃了。这就是BGP广播的作用了。 DNS劫持这也是一种常见的人工检测之后的应对。人工发现一个不和谐网站,然后就把这个网站的域名给加到劫持列表中。其原理是基于DNS与IP协议的弱点,DNS与IP这两个协议都不验证服务器的权威性,而且DNS客户端会盲目地相信第一个收到的答案。所以你去查询facebook.com的话,GFW只要在正确的答案被返回之前抢答了,然后伪装成你查询的DNS服务器向你发错误的答案就可以了。 TCP RST阻断TCP协议规定,只要看到RST包,连接立马被中断。从浏览器里来看就是连接已经被重置。我想对于这个错误大家都不陌生。据我个人观感,这种封锁方式是GFW目前的主要应对手段。大部分的RST是条件触发的,比如URL中包含某些关键字。目前享受这种待遇的网站就多得去了,著名的有facebook。还有一些网站,会被无条件RST。也就是针对特定的IP和端口,无论包的内容就会触发RST。比较著名的例子是https的wikipedia。GFW在TCP层的应对是利用了IPv4协议的弱点,也就是只要你在网络上,就假装成任何人发包。所以GFW可以很轻易地让你相信RST确实是Google发的,而让Google相信RST是你发的。 封端口GFW除了自身主体是挂在骨干路由器旁路上的入侵检测设备,利用分光技术从这个骨干路由器抓包下来做入侵检测 (所谓 IDS),除此之外这个路由器还会被用来封端口 (所谓 IPS)。GFW在检测到入侵之后可以不仅仅可以用TCP RST阻断当前这个连接,而且利用骨干路由器还可以对指定的IP或者端口进行从封端口到封IP,设置选择性丢包的各种封禁措施。可以理解为骨干路由器上具有了类似“iptables”的能力(网络层和传输层的实时拆包,匹配规则的能力)。这个iptables的能力在CISCO路由器上叫做ACL Based Forwarding (ABF)。而且规则的部署是全国同步的,一台路由器封了你的端口,全国的挂了GFW的骨干路由器都会封。一般这种封端口都是针对翻墙服务器的,如果检测到服务器是用SSH或者VPN等方式提供翻墙服务。GFW会在全国的出口骨干路由上部署这样的一条ACL规则,来封你这个服务器+端口的下行数据包。也就是如果包是从国外发向国内的,而且src(源ip)是被封的服务器ip,sport(源端口)是被封的端口,那么这个包就会被过滤掉。这样部署的规则的特点是,上行的数据包是可以被服务器收到的,而下行的数据包会被过滤掉。 如果被封端口之后服务器采取更换端口的应对措施,很快会再次被封。而且多次尝试之后会被封IP。初步推断是,封端口不是GFW的自动应对行为,而是采取黑名单加人工过滤地方式实现的。一个推断的理由就是网友报道,封端口都是发生在白天工作时间。 在进入了封端口阶段之后,还会有继发性的临时性封其他端口的现象,但是这些继发性的封锁具有明显的超时时间,触发了之后(触发条件不是非常明确)会立即被封锁,然后过了一段时间就自动解封。目前对于这一波封SSH/OPENVPN采用的以封端口为明显特征的封锁方式研究尚不深入。可以参考我最近写的一篇文章:http://fqrouter.tumblr.com/post/45969604783/gfw HTTPS间歇性丢包对于Google的HTTPS服务,GFW不愿意让其完全不能访问。所以采取的办法是对于Google的某些IP的443端口采取间歇性丢包的措施。最明显的ip段是国内解析google域名常见的74.125.128.*。其原理应该类似于封端口,是在骨干路由器上做的丢包动作。但是触发条件并不只是看IP和端口,加上了时间间隔这样一个条件。 0×03 翻墙原理前面从原理上讲解了GFW的运作原理。翻墙的原理与之相对应,分为两大类。第一类是大家普遍的使用的绕道的方式。IP包经由第三方中转已加密的形式通过GFW的检查。这样的一种做法更像“翻”墙,是从墙外绕过去的。第二类是找出GFW检测过程的中一些BUG,利用这些BUG让GFW无法知道准确的会话内容从而放行。这种做法更像“穿”墙。曾经引起一时轰动的“西厢计划”第一季就是基于这种方式的实现。 基于绕道法的翻墙方式无论是VPN还是SOCKS代理,原理都是类似的。都是以国外有一个代理服务器为前提,然后你与代理服务器通信,代理服务器再与目标服务器通信。 绕道法对于IP封锁来说,因为最终的IP包是由代理服务器在墙外发出的,所以国内骨干路由封IP并不会产生影响。对于TCP重置来说,因为TCP重置是以入侵检测为前提的,客户端与代理之间的加密通信规避了入侵检测,使得TCP重置不会被触发。 但是对于反DNS污染来说,VPN和SOCKS代理却有不同。基于VPN的翻墙方法,得到正确的DNS解析的结果需要设置一个国外的没有被污染的DNS服务器。然后发UDP请求去解析域名的时候,VPN会用绕道的方式让UDP请求不被劫持地通过GFW。 但是SOCKS代理和HTTP代理这些更上层的代理协议则可以选择不同的方式。因为代理与应用之间有更紧密的关系,应用程序比如浏览器可以把要访问的服务器的域名直接告诉本地的代理。然后SOCKS代理可以选择不在本地做解析,直接把请求发给墙外的代理服务器。在代理服务器去与目标服务器做连接的时候再在代理服务器上做DNS解析,从而避开了GFW的DNS劫持。 VPN与SOCKS代理的另外一个主要区别是应用程序是如何使用上代理去访问国外的服务器的。先来看不加代理的时候,应用程序是如何访问网络的。 应用程序把IP包交给操作系统,操作系统会去决定把包用机器上的哪块网卡发出去。VPN的客户端对于操作系统来说就是一个虚拟出来的网卡。应用程序完全不用知道VPN客户端的存在,操作系统甚至也不需要区分VPN客户端与普通网卡的区别。 VPN客户端在启动之后会把操作系统的缺省路由改成自己。这样所有的IP包都会经由这块虚拟的网卡发出去。这样VPN就能够再打包成加密的流量发出去(当然线路还是之前的电信线路),发回去的加密流量再解密拆包交还给操作系统。 SOCKS代理等应用层的代理则不同。其流量走不走代理的线路并不是由操作系统使用路由表选择网卡来决定的,而是在应用程序里自己做的。也就是说,对于操作系统来说,使用SOCKS代理的TCP连接和不使用SOCKS代理的TCP连接并没有任何的不同。应用程序自己去选择是直接与目标服务器建立连接,还是与SOCKS代理服务器建立TCP连接,然后由SOCKS代理服务器去建立第二个TCP连接,两个TCP连接的数据由代理服务器中转。 关于VPN/SOCKS代理,可以参见我博客上的文章:http://fqrouter.tumblr.com/post/51474945203/socks-vpn 绕道法的翻墙原理就是这些了,相对来说非常简单。其针对的都是GFW的分析那一步,通过加密使得GFW无法分析出流量的原文从而让GFW放行。但是GFW最近的升级表明,GFW虽然无法解密这些加密的流量,但是GFW可以结合流量与其他协议特征探测出这些流量是不是“翻墙”的,然后就直接暴力的切断。绕道法的下一步发展就是要从原理弄明白,GFW是如何分析出翻墙流量的,从而要么降低自身的流量特征避免上短名单被协议分析,或者通过混淆协议把自己伪装成其他的无害流量。 0×04 穿墙原理##实验环境准备穿墙比翻墙要复杂得多,但也有意思得多。本章节以实验为主。实验的设备是家庭用的路由器,我用的是“水星4530R”。需要有公网IP。刷的操作系统是“OpenWRT Attitude Adjustment 12.09 rc-1”版本。使用的包有: NetfilterQueue(https://github.com/fqrouter/fqrouter 中有) bind-dig shadow dpkt (不是OpenWRT的包,是python的 http://dpkt.googlecode.com/files/dpkt-1.7.tar.gz ) 本文并不打算详细讲解实验环境的设置。对于有OpenWRT编译和刷机经验的朋友可能可以按照我的叙述重建出实验环境来。整个实验的关键在于 公网上的ip地址 Linux python python访问netfilter queue的库 如果你有一台公网上的Linux机器,安装了Python和Python的NetfilterQueue,也可以进行同样的实验。 如果你使用的是路由器,需要验证你有公网ip。这个可以访问ifconfig.me来证实。其次要保证路由器是OpenWRT的并且有足够的空间安装python-mini。到这里基本上都和普通的OpenWRT刷机没有什么两样。 重点在于: 安装Python的NetfilterQueueOpenWRT提供了NetfilterQueue的C的库。但是使用C来做实验太笨重了。所以我选择了Python。但是Python的NetfilterQueue的库没有在OpenWRT中。下载https://github.com/fqrouter/fqrouter 解压后可以得到一个名字叫fqrouter的目录。然后给feeds.con添加一行src-link fqrouter /opt/fqrouter/package。把/opt/fqrouter替换为你解压的目录。然后scripts/feeds update -a,再执行scripts/feeds install python-netfilterqueue就添加好了。然后在make menuconfig中选择Languages=>Python=>python-netfilterqueue。 有了这个库就赋予了我们使用Python任意抓包,修改包和发包的能力。在OpenWRT上,除了python没有第二种脚本语言可以如此简单地获得这些能力。 安装Python的dpkt能够抓取和发送IP包之后,第二个头疼的问题是如何解析和构造任意的IP包。Python有一个库叫dpkt可以帮我们很好地完成这项任务。这是我们选择Python做实验的第二个重要理由。 在路由器上直接下载http://dpkt.googlecode.com/files/dpkt-1.7.tar.gz ,然后解压缩,拷贝其中的dpkt目录到/usr/lib/python2.7/site-packages下。 DNS劫持观测我们要做的第一个实验是用python代码观测到DNS劫持的全过程。 应用层观测dig是DNS的客户端,可以很方便地构造出我们想要的DNS请求。dig @8.8.8.8 twitter.com。可以得到相应如下 ; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5494;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION:;twitter.com. IN A ;; ANSWER SECTION:twitter.com. 4666 IN A 59.24.3.173 ;; Query time: 110 msec;; SERVER: 8.8.8.8#53(8.8.8.8);; WHEN: Sun Jan 13 13:22:10 2013;; MSG SIZE rcvd: 45 可以很清楚地看到我们得到的错误答案59.24.3.173。 抓包观测使用iptables我们可以让特定的IP包经过应用层的代码,从而使得我们用python观测DNS查询过程提供了可能。代码如下,保存文件名dns_hijacking_obversation.py: 12345678910111213141516171819from netfilterqueue import NetfilterQueueimport subprocessimport signaldef observe_dns_hijacking(nfqueue_element): print('packet past through me') nfqueue_element.accept() nfqueue = NetfilterQueue()nfqueue.bind(0, observe_dns_hijacking) def clean_up(*args): subprocess.call('iptables -D OUTPUT -p udp --dst 8.8.8.8 -j QUEUE', shell=True) subprocess.call('iptables -D INPUT -p udp --src 8.8.8.8 -j QUEUE', shell=True) signal.signal(signal.SIGINT, clean_up) try: subprocess.call('iptables -I INPUT -p udp --src 8.8.8.8 -j QUEUE', shell=True) subprocess.call('iptables -I OUTPUT -p udp --dst 8.8.8.8 -j QUEUE', shell=True) print('running..') nfqueue.run()except KeyboardInterrupt: print('bye') 执行python dns_hijacking_observation.py,再使用dig @8.8.8.8 twitter.com应该可以看到package past through me。这就说明DNS的请求和答案都经过了python代码了。 上一步主要是验证NetfilterQueue是不是工作正常。这一步则要靠dpkt的了。代码如下,文件名相同(https://gist.github.com/4524299): 123456789101112131415161718192021222324252627282930from netfilterqueue import NetfilterQueueimport subprocessimport signalimport dpktimport tracebackimport socket def observe_dns_hijacking(nfqueue_element): try: ip_packet = dpkt.ip.IP(nfqueue_element.get_payload()) dns_packet = dpkt.dns.DNS(ip_packet.udp.data) print(repr(dns_packet)) for answer in dns_packet.an: print(socket.inet_ntoa(answer['rdata'])) nfqueue_element.accept() except: traceback.print_exc() nfqueue_element.accept() nfqueue = NetfilterQueue()nfqueue.bind(0, observe_dns_hijacking) def clean_up(*args): subprocess.call('iptables -D OUTPUT -p udp --dst 8.8.8.8 -j QUEUE', shell=True) subprocess.call('iptables -D INPUT -p udp --src 8.8.8.8 -j QUEUE', shell=True) signal.signal(signal.SIGINT, clean_up) try: subprocess.call('iptables -I INPUT -p udp --src 8.8.8.8 -j QUEUE', shell=True) subprocess.call('iptables -I OUTPUT -p udp --dst 8.8.8.8 -j QUEUE', shell=True) print('running..') nfqueue.run()except KeyboardInterrupt: print('bye') 执行python dns_hijacking_observation.py,再使用dig @8.8.8.8 twitter.com应该可以看到类似如下的输出: DNS(ar=[RR(type=41, cls=4096)], qd=[Q(name=’twitter.com’)], id=8613, op=288)DNS(an=[RR(name=’twitter.com’, rdata=’;\x18\x03\xad’, ttl=19150)], qd=[Q(name=’twitter.com’)], id=8613, op=33152)59.24.3.173DNS(an=[RR(name=’twitter.com’, rdata=’\xc7;\x95\xe6’, ttl=27), RR(name=’twitter.com’, rdata=’\xc7;\x96\x07’, ttl=27), RR(name=’twitter.com’, rdata=”\xc7;\x96’”, ttl=27)], ar=[RR(type=41, cls=512)], qd=[Q(name=’twitter.com’)], id=8613, op=33152)199.59.149.230199.59.150.7199.59.150.39 可以看到我们发出去了一个包,收到了两个包。其中第一个收到的包是GFW发回来的错误答案,第二个包才是正确的答案。但是由于dig只取第一个返回的答案,所以我们实际看到的解析结果是错误的。 观测劫持发生的位置利用IP包的TTL特性,我们可以把TTL值从1开始递增,直到我们收到错误的应答为止。结合TTL EXECEEDED ICMP返回的IP地址,就可以知道DNS请求是在第几跳的路由器分光给GFW的。代码如下(https://gist.github.com/4524927): 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687from netfilterqueue import NetfilterQueueimport subprocessimport signalimport dpktimport tracebackimport socketimport sys DNS_IP = '8.8.8.8'# source http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93WRONG_ANSWERS = { '4.36.66.178', '8.7.198.45', '37.61.54.158', '46.82.174.68', '59.24.3.173', '64.33.88.161', '64.33.99.47', '64.66.163.251', '65.104.202.252', '65.160.219.113', '66.45.252.237', '72.14.205.99', '72.14.205.104', '78.16.49.15', '93.46.8.89', '128.121.126.139', '159.106.121.75', '169.132.13.103', '192.67.198.6', '202.106.1.2', '202.181.7.85', '203.161.230.171', '207.12.88.98', '208.56.31.43', '209.36.73.33', '209.145.54.50', '209.220.30.174', '211.94.66.147', '213.169.251.35', '216.221.188.182', '216.234.179.13'} current_ttl = 1def locate_dns_hijacking(nfqueue_element): global current_ttl try: ip_packet = dpkt.ip.IP(nfqueue_element.get_payload()) if dpkt.ip.IP_PROTO_ICMP == ip_packet['p']: print(socket.inet_ntoa(ip_packet.src)) elif dpkt.ip.IP_PROTO_UDP == ip_packet['p']: if DNS_IP == socket.inet_ntoa(ip_packet.dst): ip_packet.ttl = current_ttl current_ttl += 1 ip_packet.sum = 0 nfqueue_element.set_payload(str(ip_packet)) else: if contains_wrong_answer(dpkt.dns.DNS(ip_packet.udp.data)): sys.stdout.write('* ') sys.stdout.flush() nfqueue_element.drop() return else: print('END') nfqueue_element.accept() except: traceback.print_exc() nfqueue_element.accept() def contains_wrong_answer(dns_packet): for answer in dns_packet.an: if socket.inet_ntoa(answer['rdata']) in WRONG_ANSWERS: return True return Falsenfqueue = NetfilterQueue()nfqueue.bind(0, locate_dns_hijacking) def clean_up(*args): subprocess.call('iptables -D OUTPUT -p udp --dst %s -j QUEUE' % DNS_IP, shell=True) subprocess.call('iptables -D INPUT -p udp --src %s -j QUEUE' % DNS_IP, shell=True) subprocess.call('iptables -D INPUT -p icmp -m icmp --icmp-type 11 -j QUEUE', shell=True) signal.signal(signal.SIGINT, clean_up) try: subprocess.call('iptables -I INPUT -p icmp -m icmp --icmp-type 11 -j QUEUE', shell=True) subprocess.call('iptables -I INPUT -p udp --src %s -j QUEUE' % DNS_IP, shell=True) subprocess.call('iptables -I OUTPUT -p udp --dst %s -j QUEUE' % DNS_IP, shell=True) print('running..') nfqueue.run()except KeyboardInterrupt: print('bye') 执行 dig +tries=30 +time=1 @8.8.8.8 twitter.com 可以得到类似下面的输出: === 隐去 ====== 隐去 ====== 隐去 ===219.158.100.166219.158.11.150 219.158.97.30 219.158.27.30 72.14.215.130 209.85.248.60 216.239.43.19 END 出现*号前面的那个IP就是挂了GFW的路由了。脚本只能执行一次,第二次需要重启。另外同一个DNS不能被同时查询,把8.8.8.8改成你没有在用的DNS。这个脚本的一个“副作用”就是dig返回的答案是正确的了,因为错误的答案被丢弃了。 反向观测前面我们已经知道从国内请求国外的DNS服务器大体是怎么一个被劫持的过程了。接下来我们在国内搭建一个服务器,从国外往国内发请求,看看是不是可以观测到被劫持的现象。 把路由器的WAN口的防火墙打开。配置本地的dnsmasq为使用非标准端口代理查询从而保证本地做dig查询的时候可以拿到正确的结果。然后在国外的服务器上执行 dig @国内路由器ip twitter.com可以看到收到的答案是错误的。执行前面的路由跟踪代码,结果如下: === 隐去 ====== 隐去 ====== 隐去 ===115.160.187.13213.248.76.73219.158.33.181219.158.29.129219.158.19.165 219.158.96.225 219.158.101.233END 可以看到不但有DNS劫持,而且DNS劫持发生在非常靠近国内路由器的位置。这也证实了论文中提出的观测结果。GFW并没有严格地部署在出国境前第一跳的位置,而是更加靠前。并且是双向的,至少DNS劫持是双向经过实验证实了。 通过避免GFW重建请求反DNS劫持使用非标准端口这个实验就非常简单了。使用53之外的端口查询DNS,观测是否有错误答案被返回。 dig @208.67.222.222 -p 5353 twitter.com使用的DNS服务器是OpenDNS,端口为5353端口。使用非标准端口的DNS服务器不多,并不是所有的DNS服务器都会提供非标准端口供查询。结果如下: ; <<>> DiG 9.9.1-P3 <<>> @208.67.222.222 -p 5353 twitter.com; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5367;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 8192;; QUESTION SECTION:;twitter.com. IN A ;; ANSWER SECTION:twitter.com. 5 IN A 199.59.150.39twitter.com. 5 IN A 199.59.148.82twitter.com. 5 IN A 199.59.148.10 ;; Query time: 194 msec;; SERVER: 208.67.222.222#5353(208.67.222.222);; WHEN: Mon Jan 14 11:47:46 2013;; MSG SIZE rcvd: 88 可见,非标准端口还是可以得到正确结果的。但是这种穿墙并不能被应用程序直接使用,因为几乎所有的应用程序都不支持使用非标准端口查询。有很多种办法把端口变成53端口能用。 使用本地DNS服务器转发(dnsmasq,pdnsd)用NetfilterQueue改写IP包用iptables改写IP包:iptables -t nat -I OUTPUT --dst 208.67.222.222 -p udp --dport 53 -j DNAT --to-destination 208.67.222.222:5353 使用TCP查询这个实验就更加简单了,也是一条命令: dig +tcp @8.8.8.8 twitter.comGFW在日常是不屏蔽TCP的DNS查询的,所以可以得到正确的结果。但是和非标准端口一样,几乎所有的应用程序都不支持使用TCP查询。已知的TCP转UDP方式是使用pdnsd或者unbound转(http://otnth.blogspot.jp/2012/05/openwrt-dns.html?m=1)。 但是GFW现在不屏蔽TCP的DNS查询并不代表GFW不能这么干。做一个小实验: root@OpenWrt:~# dig +tcp @8.8.8.8 dl.dropbox.com;; communications error to 8.8.8.8#53: connection reset 可以看到GFW是能够知道你在查询什么的。与HTTP关键字过滤一样,一旦发现查询的内容不恰当,立马就发RST包过来切断连接。那么为什么GFW不审查所有的TCP的DNS查询呢?原因很简单,用TCP查询的绝对少数,尚不值得这么去干。而且就算你能查询到正确域名,GFW自认为还有HTTP关键字过滤和封IP等后着守着呢,犯不着在DNS上卡这么死。 ##使用单向代理 严格来说单向代理并不是穿墙,因为它仍然需要在国外有一个代理服务器。使用代理服务器把DNS查询发出去,但是DNS查询并不经由代理服务器而是直接发回客户端。这样的实现在目前有更好的反劫持的手段(比如非标准端口)的情况下并不是一个有实际意义的做法。但是对于观测GFW的封锁机制还是有帮助的。据报道在敏感时期,对DNS不仅仅是劫持,而是直接丢包。通过单向代理可以观测丢包是针对出境流量的还是入境流量的。 客户端需要使用iptables把DNS请求转给NetfilterQueue,然后用python代码把DNS请求包装之后发给中转代理。对于应用程序来说,这个包装的过程是透明的,它仍然认为请求是直接发给DNS服务器的。 客户端代码如下,名字叫smuggler.py: 12345678910111213141516171819202122232425262728from netfilterqueue import NetfilterQueueimport subprocessimport signalimport tracebackimport socket IMPERSONATOR_IP = 'x.x.x.x'IMPERSONATOR_PORT = 19840udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) def smuggle_packet(nfqueue_element): try: original_packet = nfqueue_element.get_payload() print('smuggled') udp_socket.sendto(original_packet, (IMPERSONATOR_IP, IMPERSONATOR_PORT)) nfqueue_element.drop() except: traceback.print_exc() nfqueue_element.accept() nfqueue = NetfilterQueue()nfqueue.bind(0, smuggle_packet) def clean_up(*args): subprocess.call('iptables -D OUTPUT -p udp --dst 8.8.8.8 --dport 53 -j QUEUE', shell=True) signal.signal(signal.SIGINT, clean_up) try: subprocess.call('iptables -I OUTPUT -p udp --dst 8.8.8.8 --dport 53 -j QUEUE', shell=True) print('running..') nfqueue.run()except KeyboardInterrupt: print('bye') 服务器端代码如下,名字叫impersonator.py: 1234567891011121314151617181920import socketimport dpkt.ip def main_loop(server_socket, raw_socket): while True: packet_bytes, from_ip = server_socket.recvfrom(4096) packet = dpkt.ip.IP(packet_bytes) dst = socket.inet_ntoa(packet.dst) print('%s:%s => %s:%s' % (socket.inet_ntoa(packet.src), packet.data.sport, dst, packet.data.dport)) raw_socket.sendto(packet_bytes, (dst, 0)) server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)try: server_socket.bind(('0.0.0.0', 19840)) raw_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) try: raw_socket.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) main_loop(server_socket, raw_socket) finally: raw_socket.close()finally: server_socket.close() 在路由器上运行的时候要把WAN的防火墙规则改为接受INPUT,否则进入的UDP包会因为没有对应的出去的UDP包而被过滤掉。这是单向代理的一个缺陷,需要在墙上开洞。把防火墙整个打开是一种开洞的极端方式。后面专门讨论单向代理的时候会有更多关于防火墙凿洞的讨论。 第二个运行的条件是服务器所在的网络没有对IP SPROOFING做过滤。服务器实际上使用了和GFW发错误答案一样的技术,就是伪造SRC地址。通过把SRC地址填成客户端所在的IP地址,使得DNS查询的结果不需要经过代理服务器中装直接到达客户端。 通过丢弃错误答案反DNS劫持###使用iptables过滤前两种方式都是针对GFW的重建这一步。因为GFW没有在日常的时候监听所有UDP端口以及监听TCP流量,所以非标准端口或者TCP的DNS查询可以被放行。选择性丢包则针对的是GFW的应对措施。既然GFW发错误的答案回来,只要我们不认它给的答案,等正确的答案来就是了。有两篇相关文档 使用ipfilter过滤GFW滴DNS污染 AntiDNSPoisoning (作者有更新:https://github.com/hackgfw/openwrt-gfw)改写成python脚本是这样的(https://gist.github.com/4530465),实现来自于AntiDNSPoisoning: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051import sysimport subprocess # source http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93WRONG_ANSWERS = { '4.36.66.178', '8.7.198.45', '37.61.54.158', '46.82.174.68', '59.24.3.173', '64.33.88.161', '64.33.99.47', '64.66.163.251', '65.104.202.252', '65.160.219.113', '66.45.252.237', '72.14.205.99', '72.14.205.104', '78.16.49.15', '93.46.8.89', '128.121.126.139', '159.106.121.75', '169.132.13.103', '192.67.198.6', '202.106.1.2', '202.181.7.85', '203.161.230.171', '207.12.88.98', '208.56.31.43', '209.36.73.33', '209.145.54.50', '209.220.30.174', '211.94.66.147', '213.169.251.35', '216.221.188.182', '216.234.179.13'} rules = ['-p udp --sport 53 -m u32 --u32 "4 & 0x1FFF = 0 && 0 >> 22 & 0x3C @ 8 & 0x8000 = 0x8000 && 0 >> 22 & 0x3C @ 14 = 0" -j DROP']for wrong_answer in WRONG_ANSWERS: hex_ip = ' '.join(['%02x' % int(s) for s in wrong_answer.split('.')]) rules.append('-p udp --sport 53 -m string --algo bm --hex-string "|%s|" --from 60 --to 180 -j DROP' % hex_ip) try: for rule in rules: print(rule) subprocess.call('iptables -I INPUT %s' % rule, shell=True) print('running..') sys.stdin.readline()except KeyboardInterrupt: print('bye')finally: for rule in reversed(rules): subprocess.call('iptables -D INPUT %s' % rule, shell=True) 本地有了这些iptables规则之后就可以丢弃掉GFW发回来的错误答案,从而得到正确的解析结果。这个脚本用到了两个iptables模块一个是u32一个是string。这两个内核模块不是所有的linux机器都有的。比如大部分的Android手机都没有这两个内核模块。所以上面的脚本适合内核模块很容易安装的场景,比如你的ubuntu pc。因为linux的内核模块与内核版本(每次编译基本都不同)是一一对应的,所以不同的linux机器是无法共享同样的内核模块的。所以基于内核模块的方案天然地具有安装困难的缺陷。 ###使用nfqueue过滤对于没有办法自己安装或者编译内核模块的场景,比如最常见的Android手机,厂家不告诉你内核的具体版本以及编译参数,普通用户是没有办法重新编译linux内核的。对于这样的情况,iptables提供了nfqueue,我们可以把内核模块做的ip过滤的工作交给用户态(也就是普通的应用程序)来完成。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889CLEAN_DNS = '8.8.8.8' RULES = []for iface in network_interface.list_data_network_interfaces(): # this rule make sure we always query from the "CLEAN" dns RULE_REDIRECT_TO_CLEAN_DNS = ( {'target': 'DNAT', 'iface_out': iface, 'extra': 'udp dpt:53 to:%s:53' % CLEAN_DNS}, ('nat', 'OUTPUT', '-o %s -p udp --dport 53 -j DNAT --to-destination %s:53' % (iface, CLEAN_DNS)) ) RULES.append(RULE_REDIRECT_TO_CLEAN_DNS) RULE_DROP_PACKET = ( {'target': 'NFQUEUE', 'iface_in': iface, 'extra': 'udp spt:53 NFQUEUE num 1'}, ('filter', 'INPUT', '-i %s -p udp --sport 53 -j NFQUEUE --queue-num 1' % iface) ) RULES.append(RULE_DROP_PACKET) # source http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93WRONG_ANSWERS = { '4.36.66.178', '8.7.198.45', '37.61.54.158', '46.82.174.68', '59.24.3.173', '64.33.88.161', '64.33.99.47', '64.66.163.251', '65.104.202.252', '65.160.219.113', '66.45.252.237', '72.14.205.99', '72.14.205.104', '78.16.49.15', '93.46.8.89', '128.121.126.139', '159.106.121.75', '169.132.13.103', '192.67.198.6', '202.106.1.2', '202.181.7.85', '203.161.230.171', '203.98.7.65', '207.12.88.98', '208.56.31.43', '209.36.73.33', '209.145.54.50', '209.220.30.174', '211.94.66.147', '213.169.251.35', '216.221.188.182', '216.234.179.13', '243.185.187.39'} def handle_nfqueue(): try: nfqueue = NetfilterQueue() nfqueue.bind(1, handle_packet) nfqueue.run() except: LOGGER.exception('stopped handling nfqueue') dns_service_status.error = traceback.format_exc() def handle_packet(nfqueue_element): try: ip_packet = dpkt.ip.IP(nfqueue_element.get_payload()) dns_packet = dpkt.dns.DNS(ip_packet.udp.data) if contains_wrong_answer(dns_packet): # after the fake packet dropped, the real answer can be accepted by the client LOGGER.debug('drop fake dns packet: %s' % repr(dns_packet)) nfqueue_element.drop() return nfqueue_element.accept() dns_service_status.last_activity_at = time.time() except: LOGGER.exception('failed to handle packet') nfqueue_element.accept() def contains_wrong_answer(dns_packet): if dpkt.dns.DNS_A not in [question.type for question in dns_packet.qd]: return False # not answer to A question, might be PTR for answer in dns_packet.an: if dpkt.dns.DNS_A == answer.type: resolved_ip = socket.inet_ntoa(answer['rdata']) if resolved_ip in WRONG_ANSWERS: return True # to find wrong answer else: LOGGER.info('dns resolve: %s => %s' % (dns_packet.qd[0].name, resolved_ip)) return False # if the blacklist is incomplete, we will think it is right answer return True # to find empty answer 其原理是一样的,过滤所有的DNS应答,如果发现是错误的答案就丢弃。因为是基于nfqueue的,所以只要linux内核支持nfqueue,而且iptables可以添加nfqueue的target,就可以使用以上方式来丢弃DNS错误答案。目前已经成功在主流的android手机上运行该程序,并获得正确的DNS解析结果。另外,上面的实现利用iptables的重定向能力,达到了更换本机dns服务器的目的。无论机器设置的dns服务器是什么,通过上面的iptables规则,统统给你重定向到干净的DNS(8.8.8.8)。 自此DNS穿墙的讨论基本上就完成了。DNS劫持是所有GFW封锁手段中最薄弱的一环,有很多种方法都可以穿过。如果不想写代码,用V2EX DNS的非标准端口是最容易的部署方式。如果愿意部署代码,用nfqueue丢弃错误答案是最可靠通用的方式,不依赖于特定的服务器。fqdns集成了所有的克服DNS劫持的手段,其为fqrouter的组成部分之一: https://github.com/fqrouter/fqdns ##封IP观测 ###观测twitter.com首先使用dig获得twitter.com的ip地址: root@OpenWrt:~# dig +tcp @8.8.8.8 twitter.com ; <<>> DiG 9.9.1-P3 <<>> +tcp @8.8.8.8 twitter.com; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8015;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 512;; QUESTION SECTION:;twitter.com. IN A ;; ANSWER SECTION:twitter.com. 7 IN A 199.59.149.230twitter.com. 7 IN A 199.59.150.39twitter.com. 7 IN A 199.59.150.7 根据前面的内容我们知道使用dns over tcp,大部分的域名解析都不会被干扰的。这里得到了三个ip地址。先来测试199.59.149.230 root@OpenWrt:~# traceroute 199.59.149.230 -ntraceroute to 199.59.149.230 (199.59.149.230), 30 hops max, 38 byte packets1 123.114.32.1 19.862 ms 4.267 ms 101.431 ms2 61.148.163.73 920.148 ms 5.108 ms 3.868 ms3 124.65.56.129 7.596 ms 7.742 ms 7.735 ms4 123.126.0.133 5.310 ms 7.745 ms 7.573 ms5 6 这个结果是最常见的。在骨干路由器上,针对这个ip丢包了。这种封锁方式就是最传统的封IP方式,BGP路由扩散,现象就是针对上行流量的丢包。再来看199.59.150.39 root@OpenWrt:~# traceroute 199.59.150.39 -ntraceroute to 199.59.150.39 (199.59.150.39), 30 hops max, 38 byte packets1 123.114.32.1 14.046 ms 20.322 ms 19.918 ms2 61.148.163.229 7.461 ms 7.182 ms 7.540 ms3 124.65.56.157 4.491 ms 3.342 ms 7.260 ms4 123.126.0.93 6.715 ms 7.309 ms 7.438 ms5 219.158.4.126 5.326 ms 3.217 ms 3.596 ms6 219.158.98.10 3.508 ms 3.606 ms 4.198 ms7 219.158.33.254 140.965 ms 133.414 ms 136.979 ms8 129.250.4.107 132.847 ms 137.153 ms 134.207 ms9 61.213.145.166 253.193 ms 253.873 ms 258.719 ms10 199.16.159.15 257.592 ms 258.963 ms 256.034 ms11 199.16.159.55 267.503 ms 268.595 ms 267.590 ms12 199.59.150.39 266.584 ms 259.277 ms 263.364 ms 在我撰写的时候,这个ip还没有被封。但是根据经验,twitter.com享受了最高层次的GFW关怀,新的ip基本上最慢也是隔日被封的。不过通过这个traceroute可以看到219.158.4.126其实就是那个之前捣乱的服务器,包是在它手里被丢掉的(严格来说并不一定是219.158.4.126,因为ip包经过的路由对于不同的目标ip设置不同的端口都可能会不一样)。再来看199.59.150.7 root@OpenWrt:~# traceroute 199.59.150.7 -ntraceroute to 199.59.150.7 (199.59.150.7), 30 hops max, 38 byte packets1 123.114.32.1 11.379 ms 10.420 ms 23.008 ms2 61.148.163.229 6.102 ms 6.777 ms 7.373 ms3 61.148.153.61 5.638 ms 3.148 ms 3.235 ms4 123.126.0.9 3.473 ms 3.306 ms 3.216 ms5 219.158.4.198 2.839 ms !H * 6.136 ms !H 这次同样是封IP,但是现象不同。通过抓包可以观察到是什么问题: root@OpenWrt:~# tcpdump -i pppoe-wan host 199.59.150.7 or icmp -vvv07:46:11.355913 IP (tos 0x0, ttl 251, id 0, offset 0, flags [none], proto ICMP (1), length 56) 219.158.4.198 > 123.114.40.44: ICMP host r-199-59-150-7.twttr.com unreachable, length 36 IP (tos 0x0, ttl 1, id 0, offset 0, flags [DF], proto UDP (17), length 38) 123.114.40.44.45021 > r-199-59-150-7.twttr.com.33449: UDP, length 10 原来219.158.4.198发回来了一个ICMP包,内容是地址不可到达(unreachable)。于是traceroute就在那里断掉了。 root@OpenWrt:~# iptables -I INPUT -p icmp --icmp-type 3 -j DROP如果把unreachable类型的ICMP包丢弃掉,会发现ip包仍然过不去 root@OpenWrt:~# traceroute 199.59.150.7 -ntraceroute to 199.59.150.7 (199.59.150.7), 30 hops max, 38 byte packets1 123.114.32.1 4.866 ms 3.165 ms 3.212 ms2 61.148.163.229 3.107 ms 3.104 ms 3.270 ms3 61.148.153.61 6.001 ms 7.246 ms 7.398 ms4 123.126.0.9 7.840 ms 7.223 ms 7.443 ms5 * 这次就和被丢包了是一样的观测现象了。 root@OpenWrt:~# iptables -L -v -n | grep icmp 3 168 DROP icmp – 0.0.0.0/0 0.0.0.0/0 icmp type 3 同时,可以看到我们仍然是收到了icmp地址不可到达的包的,只是被我们drop掉了。 观测被封ip的反向流量之前的观测中,被封的ip是ip包的dst。如果我们从国外往国内发包,其src是被封的ip,那么ip包是否会被GFW过滤掉呢?登录到一台国外的vps上执行下面的python代码 12from scapy.all import *send(IP(src="199.59.150.7", dst="123.114.40.44")/ICMP()) 然后在国内的路由器(123.114.40.44)上执行抓包程序 root@OpenWrt:~# tcpdump -i pppoe-wan host 199.59.150.7 or icmp -vvvtcpdump: listening on pppoe-wan, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes10:41:14.294671 IP (tos 0x0, ttl 50, id 1, offset 0, flags [none], proto ICMP (1), length 28) r-199-59-150-7.twttr.com > 123.114.40.44: ICMP echo request, id 0, seq 0, length 810:41:14.294779 IP (tos 0x0, ttl 64, id 25013, offset 0, flags [none], proto ICMP (1), length 28) 123.114.40.44 > r-199-59-150-7.twttr.com: ICMP echo reply, id 0, seq 0, length 8 可以看到,如果该ip是src而不是dst并不会被GFW过滤。这一行为有两种可能:要么GFW认为封dst就可以了,不屑于再封src了。另外一种可能是GFW封twitter的IP用的是路由表扩散技术,而传统的路由表是基于dst做路由判断的(高级的路由器可以根据src甚至端口号做为路由的依据),所以dst路由表导致的路由黑洞并不会影响该ip为src的情况。我相信是后者,但是GFW在封个人翻墙主机上所表现的实力(对大量的ip做精确到端口的全国性丢包)让我们相信,GFW很容易把封锁变成双向的。不过说实话,在这个硬实力的背后,靠的更多的是CISCO下一代骨干网路由器的超强处理能力,而不是GFW自身。 ##单向代理 因为GFW对IP的封锁是针对上行流量的,所以使得单向代理就可以突破封锁。上行的IP包经过单向代理转发给目标服务器,下行的IP包直接由目标服务器发回给客户端。代码与DNS(UDP协议)的单向代理是一样的。因为单向代理利用的是IP协议,所以TCP与UDP都是一样的。除了单向代理,目前尚没有其他的办法穿过GFW访问被封的IP,只能使用传统的翻墙技术,SOCKS代理或者VPN这些。 使用中国IP访问twitter一文中有更详细的描述: http://fqrouter.tumblr.com/post/38463337823/ip-twitter-1-nfqueue-packethttp://fqrouter.tumblr.com/post/38465016969/ip-twitter-2-nathttp://fqrouter.tumblr.com/post/38468377418/ip-twitter-3-raw-sockethttp://fqrouter.tumblr.com/post/38469096940/ip-twitter-4有一个小工具来实现单向代理: https://github.com/fqrouter/fquni]]></content>
</entry>
<entry>
<title><![CDATA[个人Markdown Web 编辑器产品设计&部署]]></title>
<!--modified by Nora
<url>%2F2016%2F10%2F01%2F%E4%B8%AA%E4%BA%BAMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%AE%BE%E8%AE%A1-%E9%83%A8%E7%BD%B2%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F10%2F01%2F%E4%B8%AA%E4%BA%BAMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%AE%BE%E8%AE%A1-%E9%83%A8%E7%BD%B2%2F</url>
-->
<url>/2016%2F10%2F01%2F%E4%B8%AA%E4%BA%BAMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%AE%BE%E8%AE%A1-%E9%83%A8%E7%BD%B2%2F</url>
<content type="text"><![CDATA[最近迷上了Markdown,希望积累些文字。同时,Sublime的Markdown插件已经能满足我对写作体验的追求,作为一个WebApp的狂热爱好者,决定使用StackEdit作为我的Markdown终端。另外,因为已经利用GitHub Page和Hexo建了一个Blog,必须兼容历史数据的同时具备一定的健壮性,起码,单独使用Hexo和StackEdit时,都要符合产品的使用预期。 #为什么使用StackEdit作为我的Markdown终端?-Markdown Web 编辑器产品调研 我的场景 Web端Markdown撰写 - StackEdit 客户端Markdown撰写 - Sublime 已经利用Hexo撰写过文章并发布到GitHub Pages,GitHub Pages只负责展现 其他背景Hexo项目逻辑Hexo会根据<%hexo-root-directory>/source/_posts下的*.md文件每次生成(hexo generate)静态HTML文件保存于<%hexo-root-directory>/.deply_git文件夹,每次部署(hexo deploy),把.deply_git文件夹中的HTML文件同步到GitHub。利用GitHub Pages设置,自动从master分支build成网页。 (.md是Markdown文件后缀)#reference-hexojs/hexo-deployer-git#reference-deployment 构思基于以上场景,简单的概念模型如下:我需要考虑的点包括: 对所有终端,“发布”后应该保持同步 撰写包括增 、删 、改 、查 操作 两个终端都能互相撰写对方创建的md 两个终端应该有一套同步&冲突处理机制 三个终端支持相同的Markdown语法 目前的解决方案涉及的开源项目:GitHub/GitHub Pages+Hexo+StackEdit StackEdit充当Web端Markdown编辑器 关联StackEdit到Github,StackEdit固定发布到repo Markdown Github repo Markdown与本地文件夹<%hexo-root-directory>/source/_posts>同步 关联Hexo到GitHub repoNoraGithub.github.io,利用Hexo项目逻辑中,<%hexo-root-directory>/source/_posts> 和 <%hexo-root-directory>/.deply_git 的映射关系,发布到GitHub Pages 冲突处理机制 由上图可见,和概念模型的区别是,StackEdit没办法获取(git pull/git fetch)到本地Sublime编写过的md文件,甚至无法知道产生过冲突,只能 git push --force。 为了防止冲突,应该尽量StackEdit撰写文章,然后单向发布文章。 实在必须本地Sublime编写,先git pull ,解决冲突后,在online的情况下, CV(复制+黏贴)回到StackEdit,进入单向发布文章流程。 #Advancedgit modulegit工具-子模块 StackEditgitwhat “github update” do windows 下奇怪路径导致不能git pull 个性化方案##couchdbapache couchdb-wikipediacouched github commitInstalling on OSXApache CouchDB INSTALL.Unixcouchdb setupcouchdb的安装时使用couchdb stackedit 教程 如果可以的话理想状态下,Editor和Publisher应该在部署在同一个Server,作为同一个Website。 理想模型未发布WYSIWYG:What You See Is What You Get,所见即所得 发布一旦冲突可以出线命令行界面,用于merge或rebase。由于本文所有项目都是开源的,相信终有一天能实现。 [x] GitHub Pages只负责展现,不能撰写 [x] 对StackEdit/Sublime,存在“发布”/“未发布”状态,“发布”后应该尽量同步,“未发布”状态相互独立即可 []所谓“同步”,完全按照Git Flow管理(如果有协作,可能GitHub Flow更好) []三个终端支持相同且完整的Markdown语法,这套语法应该涵盖UML、LaTex公式& MathJax在内的分析师、PM常用表现工具]]></content>
</entry>
<entry>
<title><![CDATA[如何利用计算机网络协议知识访问Google]]></title>
<!--modified by Nora
<url>%2F2016%2F09%2F29%2F%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE%E7%9F%A5%E8%AF%86%E8%AE%BF%E9%97%AEGoogle%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F29%2F%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE%E7%9F%A5%E8%AF%86%E8%AE%BF%E9%97%AEGoogle%2F</url>
-->
<url>/2016%2F09%2F29%2F%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE%E7%9F%A5%E8%AF%86%E8%AE%BF%E9%97%AEGoogle%2F</url>
<content type="text"><![CDATA[留坑,关于路由翻墙。 网络访问的简单模型#reference-前端经典面试题: 从输入URL到页面加载发生了什么? 当我们在浏览网页的时候,计算机做了什么? 在计算机眼里,没有域名概念,只有IP概念。当我们输入域名的时候,其实计算机做了一次 DNS查询,把域名和IP的映射(一个域名对应多个IP)获取并访问。 和服务器(指定IP机器)进行“三次握手”后展示网页内容。(略)TCP“四次挥手”结束访问(略)SSL/TLS“四次握手”(略) 提炼问题所以如果我们需要浏览Google,要做好两点。 如何获取正确的IP地址(防止DNS污染) 保护好网页内容。(可以检查ip包内数据然后评比访问) 好的。OpenWrt智能、自动、透明翻墙路由器教程 应该是参考Shadowsocks + GfwList 实现 OpenWRT 路由器自动翻墙的DNS方案二进行实现。 如何防治DNS污染? a. 维护一个域名白名单list,白名单外域名请求自己vps,白名单请求114DNS b. 维护一个域名黑名单list,黑名单域名请求自己vps,黑名单外114DNS c. 借助ChinaDNS作为上游服务器,判断dns 如何保护网页内容? 针对ip决定tcp请求的访问通道。 计算机网络模型TCP/IP 网络模型(五层)OSI 网络模型(七层)应用层DNS,IP,HTTP 传输层TCP UDP 网络层###Advanced 问题 解决方案openwrt本质是什么呢?是翻墙的路由器fanqiang解决方案,一套基于路由器硬件的Linux OS。它整合了一系列基于计算机网络的小工具。比如shadowsocks,dnsmasq,iptable(防火墙进行转发功能) dnamasq:dnsiptable:防火墙(ip包过滤规则)chinadnschnrouter:区分国内外ip段Shadowsocks是一个轻量级socks5代理,以python写成; 码农对于shadowsocks应该不陌生,而一般普通网民可能知之甚少。shadowsocks实质上也是一种socks5代理服务,类似于ssh代理。与vpn的全局代理不同,shadowsocks仅针对浏览器代理,不能代理应用软件,比如youtube、twitter客户端软件。如果把vpn比喻为一把屠龙刀,那么shadowsocks就是一把瑞士军刀,轻巧方便,功能却非常强大。 这里发布的shadowsocks分为两个版本,说明如下: ###shadowsocks-libev官方原版包含 ss-{local,redir,tunnel} 三个可执行文件。默认启动 ss-local 建立本地SOCKS代理 ###shadowsocks-libev-spec针对OpenWrt 路由器的优化版本包含 ss-{redir,rules,tunnel} 三个可执行文件。 ###ss-tunnel###做 DNS 查询转发,ss-tunnel 默认转发127.0.0.1:5353 至 8.8.4.4:53 通过 ShadowSocks 服务器查询 DNS 用于线路优化。ss-rules 可设置 ignore.list 中的 IP 流量不走代理。 ###ss-redir###建立透明代理, 做tcp转发。 Advanced1、apn是什么?2、as、bgp概念3、什么是骨干路由4、openwrt(嵌入式框架,固件)(主流路由器固件有 dd-wrt,tomato,openwrt三类)。固件是嵌入在硬件设备中的软件,类似操作系统?5、opkg(包管理工具,适用于嵌入式。类似apt/dpkg) BGP国外:Client–dnsmasq–ChinaDNS–shadowsocks–代理服务器–GoogleDNS国内:Client–dnsmasq–ChinaDNS–114DNS 域名和空间没有必然联系,域名的作用就是作为一个字符串映射到一个IP地址上,因为(1)IP地址太难记了(2)IP地址数目有限(同一个IP上可以放N个域名)所以才需要域名这么个东西。这就意味着,你有换空间的自由。哪天对空间服务商不高兴了,可以直接把他踹了,把域名解析到别家去,用另一家空间。哎哎,等会儿,啥叫域名解析? 域名服务器?name.servera记录mx纪录cname纪录 shadowsocks-libev:shadowsocks-libev 是一个 shadowsocks 协议的轻量级实现,是 shadowsocks-android, shadowsocks-ios 以及 shadowsocks-openwrt 的上游项目。其具有以下特点:opensslpolarssl:嵌入式设备的精简ssl DNS 抢答机制PAC代理本质其实就是传统的HTTP代理? Reference:翻墙路由器的原理与实现linux平台下防火墙iptables原理(转)匿名、透明、HTTP、SSL、SOCKS代理的区别 ·SSH是标准的网络协议,可用于大多数UNIX操作系统,能够实现字符界面的远程登录管理,它默认使用22号端口,采用密文的形式在网络中传输数据,相对于通过明文传输的Telnet,具有更高的安全性。 路由选择算法Dijkstra Algorithmdv]]></content>
</entry>
<entry>
<title><![CDATA[【译】安装Canopy]]></title>
<!--modified by Nora
<url>%2F2016%2F09%2F29%2F%E3%80%90%E8%AF%91%E3%80%91%E5%AE%89%E8%A3%85Canopy%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F29%2F%E3%80%90%E8%AF%91%E3%80%91%E5%AE%89%E8%A3%85Canopy%2F</url>
-->
<url>/2016%2F09%2F29%2F%E3%80%90%E8%AF%91%E3%80%91%E5%AE%89%E8%A3%85Canopy%2F</url>
<content type="text"><![CDATA[本文主要翻译了Canopy相关的几篇文档,主要用于理解Canopy环境和快速安装/启动Canopy。 安装首先从下载一个Canopy安装包。32位还是64位?标准还是完整?在32位系统,你必须使用32位的安装包,64位系统 环境设置一旦完成安装,最后一步就是完成你Python环境的设置。GUI将贯穿整个设置过程。 这一节剩余部分将描述标准GUI设置过程。但请注意,有两种主要方式设置你的Python环境: 对于用户从来不用Canopy GUI的管理员,请看这里创建一个EPD-like的环境,例如,这些用户像在EPD环境一样,只用基础命令行。 对于那些对在一个多用户的机器或者网络设置Canopy感兴趣的系统管理员也许会对创建一个共享的Canopy环境感兴趣。 标准GUI设置:Canopy可以在安装目录直接启动,如果Canopy安装在~/Canopy,使用脚本: \$ ~/Canopy/canopy Canopy第一次启动的时候,它会自动配置你的Python环境,要么在默认地址,要么用户自定义定制。这一步允许每一个用户在一个多用户的机器上 Canopy命令行接口创建一个EPD-like的环境一个EPD-like的环境意味这个场景里,用户只使用命令行。 On Windows: Canopy\App\Canopy_cli.exe –no-gui-setup setup C:\Python27 On Mac: /Applications/Canopy.app/Contents/MacOS/Canopy_cli –no-gui-setup setup ~/canopy On Linux: ~/Canopy/canopy_cli –no-gui-setup setup ~/canopy 创建一个共享的Canopy环境ReferenceLinux InstallationWhere are all of the Python packages in my User Python Environment?Canopy Command Line Interface (CLI)]]></content>
</entry>
<entry>
<title><![CDATA[Markdown Web 编辑器产品调研]]></title>
<!--modified by Nora
<url>%2F2016%2F09%2F29%2FMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%B0%83%E7%A0%94%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F29%2FMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%B0%83%E7%A0%94%2F</url>
-->
<url>/2016%2F09%2F29%2FMarkdown-Web-%E7%BC%96%E8%BE%91%E5%99%A8%E4%BA%A7%E5%93%81%E8%B0%83%E7%A0%94%2F</url>
<content type="text"><![CDATA[Markdown Web 编辑器产品调研Markdown 写作工具链总结 [ 免费篇 ]我有我的young【markdown+stylish】Markdown寫作部落格Logdown 的啓發 - StackEdit—>有趣的HTML5:离线存储(一直认为stackedit的文件在本地会对应一份文件,今儿打算同步这份文件,然而使用的cache local技术,所以这个方案并不可行)]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<!--modified by Nora
<url>%2F2016%2F09%2F29%2Fhello-world%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F29%2Fhello-world%2F</url>
-->
<url>/2016%2F09%2F29%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
<entry>
<title></title>
<!--modified by Nora
<url>%2F2016%2F09%2F29%2FREADME%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F29%2FREADME%2F</url>
-->
<url>/2016%2F09%2F29%2FREADME%2F</url>
<content type="text"><![CDATA[Personal markdown article used to synchronize between stackedit and hexo. This file is used to initialize github, as recommended.]]></content>
</entry>
<entry>
<title><![CDATA[部署Jupyter/IPython Notebook Memo]]></title>
<!--modified by Nora
<url>%2F2016%2F09%2F24%2F%E9%83%A8%E7%BD%B2Jupyter-IPython-Notebook-Memo%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F09%2F24%2F%E9%83%A8%E7%BD%B2Jupyter-IPython-Notebook-Memo%2F</url>
-->
<url>/2016%2F09%2F24%2F%E9%83%A8%E7%BD%B2Jupyter-IPython-Notebook-Memo%2F</url>
<content type="text"><![CDATA[需求: 基于 web 的 itervactive debug 公网访问的 Web Python解析脚本 支持更强大、可视化和可交互的文档 Brief of 解决方案 部署IPyhon/Jupyter Notebook到服务器 购买域名并绑定到服务器 和Markdown/Rmarkdown结合(未完成) IPython 和 JupyterIPython 通常指的是一个 Python REPL(交互式解释器) shell。提供了远比 Python shell 强大的 shell 环境。IPython 是 Iteractive Python shell 的缩写。 Notebook 是一个基于 IPython 的 web 应用。 截止 IPython 3.0 ,IPython 变得越来越臃肿,因此, IPython 4.x 后,IPython被分离成为了IPython kernel’s Jupyter(IPython shell combined with jupyter kernel) 和Jupyter subprojects ,Notebook 是 Jupyter 的一个 subproject 。官方将此称为The Big Split™。#reference-The Big Split™ IPython 把与语言无关(language-agnostic)的 components 和 Python 解释器剥离,独立为Jupyter发行。因此,Jupyter 可以和 Python 之外的解析器结合,提供新的、强大的服务。比如 Ruby REPL 环境 IRuby 和 Julia REPL 环境 IJulia。 因此,Jupyter 和 IPyhon 指代同一个项目, Jupyter 特指 Ipython4 后的, 非python kernel框架。 Jupyter的架构#reference-A Visual Overview of Projects 怎么选择Jupyter下的子项目 #reference-How do I decide which packages I need? nbviewerIPython 运行结果可以单独保存,格式为 .ipynb 。nbviewer是 Jupyter notebook viewer 的缩写,用于渲染 .ipynb 文件,并支持 HTML 和 PDF 输出。现 GitHub 已提供 Notebook 的在线预览。 环境配置服务器&操作系统bandwagonhost的VPS 配置: 1CPU256 MB Memory10 GB Disk(SSD)Ubuntu 16.04 x86_64500GB Transfer/Bandwidth Jupyter / IPython安装Notebook本文通过安装Canopy的发行版本实现Jupyter / IPython安装。Canopy是一个科学计算的python发行版本。包括了需要的依赖package,包括IPython、Jupiter、Tornado(web服务器)以及其他科学计算库,例如Numpy、Scipy、Matplotlib等。 因为在服务器端,这里有两种方案可用于下载并安装Canopy。 最简单直接方案,服务器下载并安装:由于服务器端wget命令出现是否支持SSL协议、403 forbidden、超时 等一系列问题,实际采用的方案是: 先下载到本地再上传到服务器安装: DownloadCanopy下载地址,选择合适版本即可。 SSH 协议上传到服务器先在服务器mkdir目录。 \$ mkdir path/to/save/server/canopy 再把本地文件通过 SSH 协议上传到服务器 \$ scp -P <%服务端SSH端口> path/to/save/local/canopy/<canopy-to-insall>.sh <%服务端user-name>@<%服务器IP>:path/to/save/server/canopy ‘’’PS:scp命令,如果服务端地址有空格,如~/server_doc_to_save/python\ for\ data/ml-1m这个地址下的python for data文件夹,需要加单引号(起码我是的),如'~/server_doc_to_save/python\ for\ data/ml-1m'否则会引起歧义问题scp: ambiguous target。这个歧义问题(可能)是服务器以为你要上传文件到多个文件夹,其中,多个文件夹以空格 区隔。‘’’ #reference-利用ssh传输文件#reference-scp copy to external hard drive ambiguous target#reference-scp: Ambiguous Target Error Solved 安装Canopy建议安装前先执行可能遇到的问题脚本,以防万一。 执行安装 \$ bash path/to/save/server/canopy/<canopy-to-insall>.sh 初始化&激活Canopy安装完后,得到以下信息 You can run the Canopy graphical environment by running the script:/root/Canopy/canopyor by selecting ‘Canopy’ in your Applications menu.On your first run, your Canopy User Python environment will be initialized, and you will have the opportunity to make Canopy be your default Python at the command line. Details at support.enthought.com/forums Thank you for installing Canopy! 由于我不需要graphical environment,因此我们执行~/Canopy/canopy_cli --no-gui-setup setup ~/canopy配置EPD-like environment即可reference-【译】安装Canopyreference-Install Linux Option防火墙转发由于Ubuntu默认未启动防火墙,需要的可以看一下这里: To function correctly, the firewall on the computer running the ipython server must be configured to allow connections from client machines on the c.NotebookApp.port port to allow connections to the web interface. The firewall must also allow connections from 127.0.0.1 (localhost) on ports from 49152 to 65535. These ports are used by the server to communicate with the notebook kernels. The kernel communication ports are chosen randomly by ZeroMQ, and may require multiple connections per kernel, so a large range of ports must be accessible. #reference-Firewall Setup 存放文件目录哪个目录启动(执行jupyter notebook --config=path/to/your/profile_root/profile_<%profile_name>/ipython_notebook_config.py的目录),进入后web后就显示哪个目录里面的文件,也会在该目录存放所有文件。可以自行设置更改,我mkdir了一个server_doc_to_save文件夹使用。 后台运行至此,有一个问题是,客户端logout之后,服务器的进程会killed,需要保证服务器在logout之后依然运行。当然需要保留想办法保留这个进程的日志。 \$ nohup <语句> & 原程序的的标准输出被自动改向到当前目录下的nohup.out文件,起到了log的作用。#reference-linux的nohup命令的用法#reference-Linux 技巧:让进程在后台可靠运行的几种方法 Bookstore By default, the notebook server stores the notebook documents that it saves as files in the working directory of the notebook server, also known as the notebook_dir. This logic is implemented in the FileNotebookManager class. However, the server can be configured to use a different notebook manager class, which can store the notebooks in a different format.The bookstore package currently allows users to store notebooks on Rackspace CloudFiles or OpenStack Swift based object stores.Writing a notebook manager is as simple as extending the base class NotebookManager.The simple_notebook_manager provides a great example of an in memory notebook manager, created solely for the purpose of illustrating the notebook manager API. #reference-Using a different notebook store 启动 Notebook#reference-Jupyter Notebook 配置 profileIPython 4以前通过新建 profile ,每个profile可以有独立的配置 \$ ipython profile create <profile-name># 创建一个新的notebook profile,命名为 <profile-name> 配置文件路径~/.ipython/profile_<profile-name>/ipython_notebook_config.py JupyterJupyter 没有 profile 概念了,只有默认配置,在~/.jupyter/jupyter_notebook_config.py。但个人认为 profile 概念非常好的,独立于默认配置,耦合性低。所以会自己按照IPython的处理方式模拟出profile。 \$ mkdir path/to/your/profile_root/profile_<%profile_name> # 为profile创建目录\$ cp -i ~/.jupyter/jupyter_notebook_config.py path/to/your/profile_root/profile_<%profile_name> # 复制默认配置 到 profile 中作为初始配置\$ mv jupyter_notebook_config.py ipython_notebook_config.py# 重命名为ipython_notebook_config.py,方便自己记忆,这是模拟的ipython 至此,基于web,支持HTTP协议的Jupyter Notebook 服务完成部署了。执行jupyter notebook --config=path/to/your/profile_root/profile_<%profile_name>/ipython_notebook_config.py,然后访问你的IP:http://<your-ip>:8888即可。 PS:jupyter notebook --config=path/to/your/profile_root/profile_<%profile_name>/ipython_notebook_config.py和直接运行 jupyter notebook --port=9999 --ip='*' 效果完全一样。 #reference-Porfiles#reference-Since Jupyter does not have profiles, how do I customize it?#reference-IPython - ipython_notebook_config.py missing#reference-Where should I place my settings and profiles for use with IPython/Jupyter 4.0?#reference-How do I get IPython profile behavior from Jupyter 4.x?#reference-jupyter配置ipython notebook not creating/ignoring settings in ‘ipython_notebook_config.py’ #8818#reference-how to set up a jupyter notebook to run python on ubuntu 16.04 Option设置登录密码 In [1]: from IPython.lib import passwdIn [2]: passwd()Enter password:Verify password:Out[2]: ‘sha1:<your-hash-passwd>’ 输入两次密码后(用来登录Jupyter的密码),就会输出一个哈希密钥。profile应该知道这个密钥,因此编辑profile配置: $ vi path/to/your/profile_root/profile_<%profile_name>/ipython_notebook_config.py i(insert): 1c.NotebookApp.password = u'sha1:<your-hash-passwd>' 执行jupyter notebook --config=path/to/your/profile_root/profile_<%profile_name>/ipython_notebook_config.py,然后输入密码访问: 修改默认访问端口12# It's a good idea to put it on a known, fixed portc.NotebookApp.port = 9999 # 默认端口8888 SSL协议访问SSL协议访问需要证书,一般需要通过第三方颁发/认证。为求方便,这里自己为自己颁发/认证,这个证书为一个pem文件。 \$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem 执行后,在当前路径生成一个 mycert.pem 文件。 修改profile 的配置12# 证书地址,获取第三方认证证书后,修改即可c.NotebookApp.certfile = u'/root/mycert.pem' 执行ipython notebook --profile=nbserver然后访问你的IP,记得要加https:https://<your-ip>:9999 reference-SSL的原理,充当裁判的角色这里的使用的是自签名证书(Self-Signed Certificate),由于未经过第三方认证。以下现象都是正常的,使用经过认证的证书即可。 #reference-Jupyter Notebook 配置#reference-支持三方认证的SSL#reference-三方机构Let’s Encrypt#reference-三方机构StartSSL 其他配置12345678910111213141516# 谨慎出现中文# c = get_config() # 这一行并非必须有# Kernel configc.IPKernelApp.pylab = 'inline' # if you want plotting support always# Notebook configc.NotebookApp.certfile = u'/root/mycert.pem'c.NotebookApp.password = u'sha1:<your-hash-passwd>'# It's a good idea to put it on a known, fixed portc.NotebookApp.port = 9999 # 默认端口8888c.NotebookApp.ip = '*' # 接受访问的ipc.NotebookApp.open_browser = False # 启动后默认打开浏览器... #reference-Config file and command line options#reference-IPython Notebook: 交互计算新时代校正:这篇reference,有一个错误,以下用#标记了。123456c.IPKernerlApp.pylab = 'inline'c.NotebookApp.ip = '*'c.NotebookApp.open_browser = Falsec.NotebookApp.password = u'sha1:xxxx your hashed password'c.NotebookApp.port = 9999 #可设为其他端口#c.Notebook.App.port = 9999 #可设为其他端口 IPython 到 Jupyter 的兼容/迁移(migrate)通过执行jupyter migrate自动迁移配置|解析语言|默认目录||:–:|:–:||IPython|~/.ipython||Jupyter|~/.jupyter| IPython location Jupyter location ~/.ipython/profile_default/static/custom ~/.jupyter/custom ~/.ipython/profile_default/ipython_notebook_config.py ~/.jupyter/jupyter_notebook_config.py ~/.ipython/profile_default/ipython_nbconvert_config.py ~/.jupyter/jupyter_nbconvert_config.py ~/.ipython/profile_default/ipython_qtconsole_config.py ~/.jupyter/jupyter_qtconsole_config.py ~/.ipython/profile_default/ipython_console_config.py ~/.jupyter/jupyter_console_config.py 这片reference阐述了从IPython 到 Jupyter的配置改变及如何兼容Migrating from IPython Notebook 可能遇到的问题升级apt-get更新资源并升级: \$ sudo apt-get update && sudo apt-get upgrade 包依赖1 desktop-file-install:command not foundupdate-desktop-database command not found 解决 \$ sudo apt-get install desktop-file-utils #reference-desktop-file-install:command not found 包依赖2 update-mime-database: command not found 系统语言1SSH连接上服务器,会传递部分变量,由于服务端和客户端变量(eg.LANG)冲突,可以选择注释掉locale setting(SSH服务端配置地址:/etc/ssh/sshd_config)1# AcceptEnv LANG LC_* #reference-How can I fix a locale warning from perl 系统语言2安装过程中,Ubuntu系统语言不支持en_US.UTF-8,需要支持: \$ sudo locale-gen en_US.UTF-8 #reference-将Ubuntu系统语言环境改为英文的en_US.UTF-8 设置 timezone \$ dpkg-reconfigure tzdata could not connect to X server当运行/root/Canopy/canopy企图使用graphical environment的时候,会显示could not connect to X server问题,可能是GUI或者权限问题 #reference-Ubuntu服务端GUI配置 当在Jupyter Notebook中使用图表功能时,例如pandas的plot,如果出现Invalid DISPLAY variable,很可能是X server没有启动成功。 #reference-Invalid DISPLAY variable #reference-What’s wrong with Pandas plot?End如不特殊声明,本文大部分脚本在服务端执行。]]></content>
</entry>
<entry>
<title><![CDATA[test diagram]]></title>
<!--modified by Nora
<url>%2F2016%2F07%2F30%2Ftest-diagram%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F07%2F30%2Ftest-diagram%2F</url>
-->
<url>/2016%2F07%2F30%2Ftest-diagram%2F</url>
<content type="text"><![CDATA[]]></content>
</entry>
<entry>
<title><![CDATA[项目介绍]]></title>
<!--modified by Nora
<url>%2F2016%2F07%2F30%2F%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F07%2F30%2F%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D%2F</url>
-->
<url>/2016%2F07%2F30%2F%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[Brief of Each Project 产品简介把企业内部数据产品搬到公开数据领域,连接爬虫工程师和数据分析师,利用社交的力量构建最大的公开数据仓库。基于公有数据建立垂直内容平台,让作者把知识通过新的数据交互的方式和读者进行交流。 一份可交互的文章demo 时间表 时间 1-3月 产品调研、设计,商业模式设计,决策技术外包并寻找合伙人 4-6月 开发业务逻辑,对云服务调研选择合适的Hadoop服务并对接 7-9月 前后端分离,重新对接,前端完善可视化 10月 面向广州投资者收集投资意见反馈 11-12月 重构前端可视化+支持SEO coming Scrapy爬虫收集基础数据进入用户/内容运营 BES_AMS服务对接主流的人群标签以及第三方数据主要是基于访客的浏览数据进行建模,对于toB类广告主,这类人群标签显然不够精准。BES是百度最大的ADX。AMS(Audience Matching Service)服务是百度关键词服务,通过API返回固定数量关键词对应的搜索人群(cookie),用于RTB过程中的(re)targeting。 The_Media_Trust对接进行海外投放过程中,像Imobi/Google/Mopub/Pubmatic渠道,有严格的创意&政策要求。The Media Trust是保证创意、落地页无恶意代码以及政策违反的第三方服务机构,通过定时轮询,保证投放免受政策惩罚。 Peer_39/ADbug_brandsafety(品牌安全)对接品牌类广告主对于品牌美誉度有要求,要求广告位所在的页面不影响品牌调性。brandsafety指的正是这一类ad verification服务。Peer 39/ADbug是国内外brandsafety服务最出名的。北京机房竞价前请求(国内/海外)服务器page级别的属性数据,判断是否危害广告主品牌安全。用于保护品牌类广告主广告调性 Sizmek_ad_serving对接Sizmek为4A提供广告投放服务,包括brandsafety(品牌安全)/viewability(可视率)监测。通过对接,保证创意、落地页和竞价服务(DSP端、渠道端)相互兼容。 高通DSP投放高通是2016年最重要的品牌类广告主,品牌类广告主的需求变化多且个性化。解决业务层面为当前产品/技术架构带来的挑战。 跨渠道跨创意的频次控制(by adx&by creative&by cookie) 无法预测的累积时间段的频次控制(by cookie&by time) 创意轮流显示且平分显示次数(by creative&by cookie) 控制商务期望 移动RTB/全渠道PMP/PDB市场对接RTB/PMP/PDB市场业务有区别,需要根据协议和商务业务调整竞价服务。例如竞价返量以及按比例返量。兼容跨渠道差异以及现有产品功能。 SEM品牌推广负责BiddingX品牌的SEM&DSP营销,整合Site Tracking和PDMP重定向目标人群,并利用数据可视化展现工作成果。 日均消费提高400%(700–>3000) 点击率提升100%(0.3%–>0.7%) 展示量提升100% 有赞商城DSP推广APP推广,包括banner创意和视频创意,尝试引导网页流量转化为APP激活。 整合第三方DMP数据到DSP进行目标人群重定向 利用S2S scheme统计激活数据 优化后,满足Android端KPI 移动SSP_iOS_SDK第一版两周内完成移动SSP iOS SDK第一版。 砍次要需求 保证BD团队和开发团队沟通,确保上线日期 总裁看板利用Oracle BIEE实现销售、流量业务数据报表。接手旧系统,并对管理层需求快速反馈。(5天内) 针对数据中的问题提供分析报告 根据业务变化进行ETL工作 协调资源保证产品按时上线 创新APP项目包括母婴特卖垂直领域电商APP 和 基于库存的个性化推荐APP。 负责整合旧系统的推广、渠道、账户业务逻辑 负责H5推广活动页产品实现,通过微博、微信渠道吸引新用户 功能测试和数据埋点 移动司南全面负责面向管理层的移动端分析产品,包括产品设计,数据探索和数据可视化。 唯品司南为品牌销售和区域销售提供数据决策的可视化产品。对常规化的分析进行ETL后,利用high-chart,e-chart框架展现,包括档期分析,退拒分析等。获得管理层高度认可。一期参与到后期数据验证,负责黑白盒数据测试&验证,确保数据正确性。二期作为功能性产品经理,参与到品类分析、品牌分析的产品规划。并对数据可视化提供UE建议。 唯品经纬唯品经纬是利用订单地址数据实现的业务可视化,为市场进行地面推广,职业信息建模提供支持。对唯品经纬数据部分进行黑盒测试。由于ETL离职,作为ETL开发参与到白盒测试,保证数据正确性。 微信特卖100%对微信商城的BI分析负责,形成虚拟团队向品牌副总裁汇报。利用微信公众号,在微信端进行销售。 每周提供微信数据周报以及数据分析 对微信常态分析产品化 建议微信端代金券全场使用,促进跨品牌销售; 归因模型利用归因(助攻模型)模型衡量渠道贡献价值,尝试利用图论实现可视化,提供业务决策支持。 管理培训生唯品会管理培训生项目,是把有潜力的应届毕业生塑造为全方面的管理者的项目。半年的轮岗经验,更熟悉跨部门的合作: 在市场(1月)、BI(2月)、商务(1月)、智能客服(1周)作为数据分析师,从数据角度出发做业务运营决策支持以及零售商品分析; 3周在线客服接线,经历719大促洗礼,能逻辑性更强地绘制业务流程; 电子商务代运营Speed零售团队合作 协助进行市场调研,数据分析,执行运营优化工作,当月实现12万销售业绩(50%增长); 学习能力:1周学习CPC(按点击付费)推广达到ROI(1:1.5)并收集数据分析改进; 策划、执行活动营销方案; 南雪集团电子商务化 团队配合执行方案,接手天猫半年实现50万的月销售业绩,3皇冠,4*500人会员管理; 运营微博,获得效果:5转发/天&4uv/天; 策划、执行每月1次微博活动并进行数据收集及分析总结;微博活动平均700+转发/条; 主导与搜道网“喜从盒来”微博体验营销,负责前期公关,洽谈,方案策划(9页话题反馈); 跟进并负责品牌在华南理工大学的“起航杯”比赛,完善方案和结合SNS进行校园推广: 执行,吸引了200+参与者(30+队伍)及共300+微博转发; 建立了人才梯队(10人分销团队),了解企业校园渠道的拓展及人才策略; 个性化推荐系统数据挖掘方向的毕业设计。基于社会化电子商务的推荐系统,利用python,对协同过滤,隐语义模型,随即梯度算法等挖掘算法做出实现。 vps ip定向刷广告部署海外虚拟服务器做DNS查询以及访问转发实现跨GFW,同时解决广告投放过程中难刷广告的业务痛点。(办公室网络环境下,有多个出口IP)]]></content>
</entry>
<entry>
<title><![CDATA[Resume]]></title>
<!--modified by Nora
<url>%2F2016%2F07%2F02%2FResume%2F</url>
-->
<!--
<url>check_hexo_config_url/2016%2F07%2F02%2FResume%2F</url>
-->
<url>/2016%2F07%2F02%2FResume%2F</url>
<content type="text"><![CDATA[Summary陈驹峥 15920548066 [email protected] 软件工程的数据挖掘方向,了解商业数据与工程建模,致力于从数据出发的商业决策与运营优化 互联网广告投放(24月)+零售运营(18月)+快消品运营(12月)-大学生创业 熟悉数字营销和互联网广告市场,包括 DSP/ADX/SSP/RTB/PMP/PDB/SEM 熟悉电子商务推广运营和数据分析,包括活动运营/流量运营/DMP/Site Tracking/Cookie Mapping 工具:熟悉HQL & SQL ,了解分布式架构与数据库基础知识Excel & Tableau 实现分析可视化Python & R 实现商业分析 模型 & 技术:协同过滤,K-means聚类,隐语义+随即梯度下降,决策树较了解 数据库,数据仓库,BI,项目管理(git) 计算机网络 工作经验DSP产品经理舜飞科技 2015.01至今偏后端项目管理,偏Scrum Master。数据驱动衡量需求价值并协调前端、后端和测试资源保证项目顺利上线。协助流量变现,移动端广告投放占比增长到50%以上。 构建产品部文档和项目管理制度; 负责渠道/媒介对接,衡量渠道价值,安排优先级; 重点客户/项目跟进; 渠道资源分析&数据探索 “计划”一期设计&测试 广点通DMP服务对接 BES AMS服务对接  [STAR介绍]-2016.08-2016.10 The Media Trust对接  [STAR介绍]-2016.08-2016.10 Peer 39/ADbug brandsafety(品牌安全)对接  [STAR介绍]-2016.04-2016.06 Sizmek ad serving对接  [STAR介绍]-2015.12-2016.06 高通DSP投放  [STAR介绍]-2016.02-2016.04 移动RTB/全渠道PMP/PDB市场对接  [STAR介绍]-2015.03-2016.11 SEM品牌推广  [STAR介绍]-2015.05-2015.06 有赞商城DSP推广  [STAR介绍]-2015.05-2015.08 移动SSP iOS SDK第一版  [STAR介绍]-2015.04-2015.04 数据产品经理唯品会(中国)有限公司 2014.07-2015.01数据分析产品化。涉及数据产品的主要流程,从PRD文档、到产品的UI设计,以及跨系统的产品整合,甚至部分ETL,黑白盒数据测试和项目管理工作。 负责面向管理层的APP数据产品, 产品化常规的运营分析流程,支持决策; 接手旧产品,并根据管理层反馈做快速迭代; 参与创新APP项目,如, 我是妈咪 ( 母婴闪购电商)和 hey!购物 (基于库存的个性化推荐APP); 总裁看板  [STAR介绍]-2014.07-2015.01 创新APP项目  [STAR介绍]-2014.10-2015.01 移动司南  [STAR介绍]-2014.10-2014.11 唯品司南  [STAR介绍]-2014.07-2014.07 唯品经纬  [STAR介绍]-2014.07-2014.07 数据分析师唯品会(中国)有限公司 2013.07-2014.07作为运营分析组一员,每周提供运营分析以及日常快捷反馈到总裁会,并根据反馈进行运营层面的分析建议。负责市场运营方面日常分析: 广告投放&渠道:为衡量渠道价值进行资源分配而构造归因模型; 促销组合:不同促销形式的效果&组合分析,并形成报表系统; 财务:市场费用的构成分析; 微信特卖  [STAR介绍]-2014.06-2014.09 归因模型  [STAR介绍]-2013.11-2014.04 管理培训生-2013.07-2014.02 教育背景华南理工大学 软件工程 2009-2013 电子商务代运营  [STAR介绍]-2011.09-2012.06 个性化推荐系统-2012.09-2013.06 其它托福:82托业:755软件学院篮球队 微博| 简书 |知乎|Github Jupyter NotebookHexo + Git Blog一万字讲清楚如何翻墙文字识别初探]]></content>
</entry>
</search>