tag:blogger.com,1999:blog-65322525798033573852024-02-07T14:09:22.957+08:00松鼠博士的魔法眼鏡小松鼠嚇了一跳,有了魔法眼鏡後,這世界看起來完全不一樣了tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.comBlogger324125tag:blogger.com,1999:blog-6532252579803357385.post-82005245682152718362019-06-09T23:58:00.002+08:002019-06-09T23:58:27.759+08:00找到你的熱情所在是個爛建議因為都 2019 年了,還是很多人建議年輕人:「尋找自己的熱情」。這是一個爛建議。<br />
簡單的說,熱情並不是用找到的,而是培養的。如果你還沒跟上,快一點,這個已經是一種很主流的建議了:<br />
<ul>
<li> <a href="http://“Find your passion” is bad advice, say Yale-NUS and Stanford psychologists" target="_blank">“Find your passion” is bad advice, say Yale-NUS and Stanford psychologists</a></li>
<li> <a href="https://www.nytimes.com/2019/04/21/smarter-living/why-find-your-passion-is-such-terrible-advice.html" target="_blank">Why ‘Find Your Passion’ Is Such Terrible Advice</a></li>
<li><a href="https://www.themuse.com/advice/why-follow-your-passion-is-pretty-bad-advice" target="_blank">Why "Follow Your Passion" is Pretty Bad Advice</a> </li>
<li><a href="https://mightyinvestor.com/how-to-find-your-passion/" target="_blank">“Find Your Passion” Is Really Bad Advice. Do This Instead….</a> </li>
<li><a href="https://www.forbes.com/sites/susanobrien/2019/01/31/why-follow-your-passion-is-bad-career-advice/#49984ace42bd" target="_blank">Why "Follow Your Passion" Is Bad Career Advice</a> </li>
</ul>
或者可以上網搜尋一下 Growth mindset vs Fixed mindset. 成長性思維與固定思維。雖然這個論點也有爭議,但目前還是一種可以接受的學說,有實驗證據。<br />
接受成長型思維,不代表要否定人之與人之間的差異。人和人之間,絕對有天賦以及天性上的差異。但這個差異可能比你想像中的小。看看這個影片吧。<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/eZ-Ads8Qr40/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/eZ-Ads8Qr40?feature=player_embedded" width="320"></iframe></div>
如果沒有手,都能彈鋼琴,那很多不會彈琴的人,問題很難歸因於天賦上的差異。多數人哀怨自己天賦不夠,有這麼大嗎?<br />
<br />
當然,「尋找自己的熱情」,也可以解讀成內在探索上的意義。這點其實不算錯,不過要能真正找到自己真正的熱情,先決條件是要探索自我,搞清楚「我」到底是什麼。但真的要能了解真正的自己,差不多就能算是「明心見性」了。這個太難了。<br />
而且,大多數需要「尋找自己熱情」這種建議的人,都會說類似「我不擅長記憶」、「我不喜歡算術」這類話。畢竟,如果他已經有喜歡的事物,也就不需要煩惱是否要「尋找自己的熱情」。大多數這樣的人,心態都在「這個也不是、那個也不是,我還在摸索還在找啊」這個情境下。<br />
請注意,這些我喜歡什麼、我討厭什麼、我怎麼樣、我怎麼樣、我怎麼樣的的想法,都是「執著」。<br />
抱持著執著之心,想要探索自我,明心見性,不是緣木求魚嗎?<br />
<br />
我以前寫過「<a href="http://weijr-note.blogspot.com/2013/12/blog-post.html?m=1" target="_blank">要當個興趣人</a>」一文,有人會把我歸類為「跟隨熱情」一派。但那篇文章中,完全沒有說「要找到自己的興趣」,而是「要有對事物產生興趣的能力」: <br />
<blockquote class="tr_bq">
就像一個電玩迷,通常不會只迷一個遊戲。一個電影迷也很少只看一部電影。一個「興趣迷」自然會不斷尋求新興趣。一直反覆看同一本小說的,也難稱為小說迷。<br />
有多次這種「興趣」的經驗後,你甚至可以刻意誘導自己,讓自己對特定事物產生興趣。</blockquote>
我有時也會跟學生說,既然你已經決定要念某一門課,即使再不喜歡,也要強顏歡笑,假裝自己很愛他。久而久之,就會假戲真做。<br />
很多人可能沒有真的聽進去。還是抱持著「我不需要喜歡這門科目,靠努力和決心就能學會了」。<br />
但是你的決心和努力夠嗎? 強顏歡笑,然後甚至假戲真做的人,才是真正有決心,有努力的人。真正有決心想學的人,為了學會,是連自我都可以捨棄的。拋棄掉那個「我覺得微積分很無聊」、「我不是理科人」的那個自我。真正努力的人,是想盡方法增加自己的極限。泯滅自我,讓自己愛上「微積分」,你才有辦法擠出更多能量來學習。不會每次學習,都要消耗不必要的恆毅力。<br />
一般人常常會把「真正的努力」誤認為「天賦」,而把「讓自己很累」誤認為「努力」。<br />
<br />
差不多就是這樣了。<br />
對了,愛因斯坦從來沒有說過「假如你讓一條魚爬樹的話,牠會永遠相信自己是一個笨蛋。」那段話。<br />
--<br />
<br />
<br />
然後,問題才真正開始。有一個問題比較簡單,可以先回答。 <br />
「既然成長性思維,不需要跟隨熱情,而且熱情是可以培養的,那是不是可以任意逼小孩去念醫科?」 <br />
「先等你有能力可以對任何領域,都能自由自在產生熱情再說。」<br />
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com2tag:blogger.com,1999:blog-6532252579803357385.post-62652549026246566082019-05-10T12:24:00.002+08:002019-05-10T12:24:32.044+08:00未來的志向<div class="" data-block="true" data-editor="96nb4" data-offset-key="6snh1-0-0">
<div class="_1mf _1mj" data-offset-key="6snh1-0-0">
<span data-offset-key="6snh1-0-0"><span data-text="true">分享給當老師的朋友或者好的大人。</span></span></div>
</div>
<div class="" data-block="true" data-editor="96nb4" data-offset-key="daula-0-0">
<div class="_1mf _1mj" data-offset-key="daula-0-0">
<span data-offset-key="daula-0-0"><span data-text="true">常看到有人會問年輕人未來有什麼理想、想從事什麼職業,希望他們能立定志向和目標,更有動力。 </span></span></div>
</div>
<div class="" data-block="true" data-editor="96nb4" data-offset-key="ejk5d-0-0">
<div class="_1mf _1mj" data-offset-key="ejk5d-0-0">
<span data-offset-key="ejk5d-0-0"><span data-text="true">但未來不是如此明確而自私的存在,是一團糾纏不清旋轉閃爍的亂流。 </span></span></div>
</div>
<div class="" data-block="true" data-editor="96nb4" data-offset-key="2eb15-0-0">
<div class="_1mf _1mj" data-offset-key="2eb15-0-0">
<span data-offset-key="2eb15-0-0"><span data-text="true">較好的方式是想像你希望的世界模樣(世界包含自己),然後讓未來的軌跡收縮到這個想像上。 </span></span></div>
</div>
<div class="" data-block="true" data-editor="96nb4" data-offset-key="3dg17-0-0">
<div class="_1mf _1mj" data-offset-key="3dg17-0-0">
<span data-offset-key="3dg17-0-0"><span data-text="true">自己的位置,會自然而然顯現在收斂的不動點上。</span></span></div>
<div class="_1mf _1mj" data-offset-key="3dg17-0-0">
<span data-offset-key="3dg17-0-0"><span data-text="true"> </span></span></div>
</div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com2tag:blogger.com,1999:blog-6532252579803357385.post-38327001833513580192018-07-27T11:30:00.001+08:002018-07-27T11:34:10.448+08:00黃色小鴨除錯法及其他<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimtax6pKYj0FQ-WHoYrfWu92q16y7ix6pKprG7pCogkVJ6xs63tn-hSQxB7iftlKO-j0Iz2uUP5ofFT_CroVqaogQpgVfuW5a2-i1S79C8ALPeKyKFzEFCEB-nUZ6ZZ5yQfyQqYj-ZoxA/s1600/StackExchange_Rubber_Duck_Avatar_April_Fools_2018.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="270" data-original-width="270" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimtax6pKYj0FQ-WHoYrfWu92q16y7ix6pKprG7pCogkVJ6xs63tn-hSQxB7iftlKO-j0Iz2uUP5ofFT_CroVqaogQpgVfuW5a2-i1S79C8ALPeKyKFzEFCEB-nUZ6ZZ5yQfyQqYj-ZoxA/s640/StackExchange_Rubber_Duck_Avatar_April_Fools_2018.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">StackExchange Rubber Duck Avatar April Fools 20</td></tr>
</tbody></table>
黃色小鴨除錯法是一個還算有名的概念,維基百科的介紹足夠清楚。
<br />
<blockquote>
(Rubber Duck Debugging)是可在軟體工程中使用的一種偵錯代碼的方法。方法就是在程式的偵錯、除錯或測試過程中,操作人耐心地向小黃鴨解釋每一行程式的作用,以此來激發靈感與發現矛盾。
此概念是參照於一個故事。故事中程式大師隨身攜帶一隻小黃鴨,在偵錯代碼的時候會在桌上放上這隻小黃鴨,然後詳細地向鴨子解釋每行代碼。
</blockquote>
乍看之下有點荒謬,但實際上類似的概念很實用也很常出現。
有一個腳踏車實驗,可能你也聽過,因為這本書「知識的假象:為什麼我們從未獨立思考?」的關係:
<br />
<blockquote>
很多人都覺得他們了解腳踏車的原理,但是在一項知名的心理學研究中,自信滿滿的受試者有四○%無法正確畫出腳踏車的構造,有些人甚至會用鏈條把前後輪都連在一起,這樣的腳踏車要怎麼騎啊?</blockquote>
這種症狀可以被稱為是知識的幻覺。也不只是腳踏車,我們常常會高估自己對事物的了解程度。<br />
<br />
但同一本書上也提到了,在實驗中,只要先請受試者解釋一下事物原理(沒有人批改),再請他評估自己理解的程度。則這種自我感覺良好的情形就會得到改善。
<br />
<br />
不是所有人都有這種「知識幻覺」的症狀,很多人其實讀了那本書,可能會覺得莫名其妙。覺得「我本來就能正確評估我對抽水馬桶和汽車運行原理的了解程度啊」。的確很多人是這樣的,靠理解思考的人。但根據書上所說的,有更多人是另外一種,偏向靠感覺的人,所以會分不清「常見」和「理解」,兩種都能被稱為「熟悉」。
<br />
<br />
這在某種程度上面解釋了黃色小鴨除錯法的原理,執行解釋這個動作,可以讓我們進入另外一種心理狀態。而這種狀態,能讓我們破除一些主觀的盲點。
<br />
<br />
同樣的原理也會發生在閱讀自己的文章以及閱讀別人文章的差別。很多人閱讀自己寫的文章時,無法發現錯誤、錯字。所以會需要其他人幫忙校稿。因為對於自己的文章太過熟悉了。但是閱讀別人文章時,頭腦內部會有一個「讀懂」也就是解釋的步驟,所以是不同的心理狀態。
<br />
<br />
更類似而常見的,是宗教上的禱告祈願告解。當你對著神像解釋著你的人生難題時,就是在執行黃色小鴨除錯法。
<br />
<br />
從更大一點框架來看,這也可以看成是一種目的外部性的方式,將觀點轉移到小鴨、神明或不特定對象為理解主體,來述說整個故事。tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-13356060136571478002018-07-27T10:33:00.000+08:002018-07-27T10:33:53.052+08:00舊文補發:「賈伯斯搶功 創意奇才很受傷」
<b>賈伯斯搶功 創意奇才很受傷</b><br />
今天在中國時報看了跨版大篇幅報導<a href="http://news.chinatimes.com/focus/50109796/112011101700455.html">賈伯斯搶功 創意奇才很受傷</a>,說到<br />
<blockquote>
艾夫對賈伯斯搶去太多功勞感到十分不爽,對賈伯斯「把我的創意講成是他的」十分受傷。當外界把賈伯斯形容為蘋果「創意之王」時,艾夫更會變得渾身是刺。
</blockquote>
以及<br />
<blockquote>
「他常常會瀏覽一遍我的創意與設計,然後說,這個不太好,這個也普普;我比較喜歡那個,」艾夫說:「然後,過一陣子,我就會坐在觀眾席裡,看著他在講台上
口沫橫飛,彷彿一切都來自他的創意。我非常講究分辨自己的創意從何而來,我甚至有一本筆記本,裡面記滿了我的想法與點子。因此,當他把我的創意講成是他
的,我會覺得很不舒服。」
</blockquote>
<br />
想像一下這種情形,在團體中聊天,偶然間你發現到了某個有趣的事,也許是諧音,也許是新奇的想法,所以你講了句幽默的話把這個想法點了出來。團體中有幾個人聽到了,也笑了,其中有一個人過了兩三秒之後,終於恍然大悟你的意思,似乎相當興奮,開始把你的想法複述了一遍,由於很興奮,所以聲音比較大,剛才沒聽到或者還沒注意聽你講話的人,也聽到了。而且似乎也因為興奮的緣故,所以在附和和複述你的想法時,有點太過細節了(比方把諧音或者笑點解釋出來),就你的品味來說,其實是有點太囉唆而且破壞趣味。但也許就在當時,也許之後,奇怪的是,團體中大部份人都認為那個人才是想到有趣點子的人,甚至可能連那個人都自己這麼認為的。<br />
<br />
這也就是為什麼常常指導教授覺得關鍵 idea 都是自己告訴學生的,但是學生覺得教授只會出一張嘴。或者腦力激盪中,明明提出新點子的人是你,但大家卻覺得是別人的點子。或者艾夫和賈伯斯之間的情節也是如此。<br />
<br />
原因在於彼此的認知基礎不同。你覺得你已經明白點出關鍵了,剩下的東西其實早就知道,沒什麼好解釋。但其他人也許不這麼覺得,那個搶功者花了兩三秒才明白你說什麼,表示那兩三秒鐘他思考了,他二次創造了你的點子。從他的角度來說,你只是個啟發或者現象,真正的發現者和創造者是他自己。你只是剛好講到那個諧音或者某個關鍵字,真正把東西聯想起來的是他。教授和研究生的例子就更典型了,因為研究生要花上更多時間來重新發現教授的想法(先不論教授嘴砲的情形)。<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-47600673566507091572017-01-01T12:58:00.004+08:002017-01-01T16:10:25.716+08:002017 is not just another prime numberGood bye year 2016. Hello year 2017.<br />
We all know that 2017 is a prime number, but it is more than just another prime number.<br />
<br />
<ul>
<li>2017π (rounds to nearest integer) is a prime </li>
<li>2017e (rounds to nearest integer ) is a prime. </li>
<li>The sum of all odd primes up to 2017 is a prime number, i.e. 3+5+7+11+...+2017 is a prime number. </li>
<li>The sum of the cube of gap of primes up to 2017 is a prime number. That is (3-2)^3 + (5-3)^3 + (7-5)^3 + (11-7)^3 + ... + (2017-2011)^3 is a prime number.</li>
<li>The prime number before 2017 is 2017+(2-0-1-7), which makes it a <a href="https://en.wikipedia.org/wiki/Sexy_prime" target="_blank">sexy prime</a>, and the prime after 2017 is 2017+(2+0+1+7). 2017 itself is of course equal to 2017+(2*0*1*7) </li>
<li>Insert 7 into any two digits of 2017, it is still a prime number,
i.e. 27017, 20717, 20177 are all primes. Plus, 20177 is also a prime
number</li>
<li>Since all digits of 2017 is less than 8, it can be viewed as an octal. 2017 is still a prime number as an octal.</li>
<li>2017 can be written as a sum of three cubes of primes, i,e, p^3 +q^3 +r^3 for some primes p, q, r. </li>
<li>2017 can be written as a sum of cubes of five distinct integers.</li>
<li>2017 can be written as x^2+y^2, x^2+2y^2, x^2+3y^2, x^2+4y^2 x^2+6y^2, x^2+7y^2, x^2+8y^2, x^2+9y^2 (for positive integers x, y)</li>
<li>20170123456789 is also a prime</li>
<li>the 2017th prime number is 17539 and 201717539 is also a prime.</li>
<li>Let p=2017, then both (p+1)/2 and (p+2)/3 are prime numbers.</li>
<li>The first ten digits of the decimal expansion of the cubic root of 2017 contains all different digits 0~9. 2017 is the least integer has this property. </li>
<li>2017 = 2^11 - 11th prime </li>
</ul>
<div>
<br /></div>
<div>
You can check OEIS for more interesting facts for your favorite numbers :)<br />
<br />
Update: a <a href="https://cloud.sagemath.com/projects/4a5f0542-5873-4eed-a85c-a18c706e8bcd/files/support/2017.sagews" target="_blank">sagemath worksheet</a> to verify these facts by William Stein</div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-23455522749374918182016-08-17T12:52:00.002+08:002016-08-17T13:06:11.658+08:00其實我們都是半桶水<div data-contents="true">
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="2e07b-0-0">
<div class="_1mf _1mj" data-offset-key="3rpik-0-0">
<span data-offset-key="3rpik-0-0"><span data-text="true">像 pokemon go 這樣的社會級現象,往往有很多不同的面向。每個人真的都只是一知半解。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="3rpik-0-0">
<div class="_1mf _1mj" data-offset-key="3rpik-0-0">
</div>
<div class="_1mf _1mj" data-offset-key="3rpik-0-0">
<span data-offset-key="3rpik-0-0"><span data-text="true">除了 pokemon 這個傳承外,還有很多。比方很多人可能也會因此開始認識 ingress 或其他 location based game.</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="fpdms-0-0">
<div class="_1mf _1mj" data-offset-key="fpdms-0-0">
<span data-offset-key="fpdms-0-0"><span data-text="true"> gps 模擬、certificate pinning、VPN、MITM proxy 這些,也許很多人覺得稀鬆平常。但對很多人來說,可能是新大陸,正開始展開熱情研究。</span></span></div>
<div class="_1mf _1mj" data-offset-key="fpdms-0-0">
</div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="5ncct-0-0">
<div class="_1mf _1mj" data-offset-key="5ncct-0-0">
<span data-offset-key="5ncct-0-0"><span data-text="true">可能也有人因為這個"不入流"的 AR 應用開始對 AR 產生興趣。</span></span></div>
<div class="_1mf _1mj" data-offset-key="5ncct-0-0">
<span data-offset-key="5ncct-0-0"><span data-text="true">有人則好奇要怎麼樣的伺服器和架構才能處理這麼多玩家。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="3ldnj-0-0">
<div class="_1mf _1mj" data-offset-key="3ldnj-0-0">
<span data-offset-key="3ldnj-0-0"><span data-text="true">有人可能因為這個遊戲開始對「圓」產生的興趣(不是獵人那個,雖然有點像),因為要找出正確位置,開始研究圓心、割線、兩圓焦點、中垂線等等性質,開始對數學感興趣。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="4ao90-0-0">
<div class="_1mf _1mj" data-offset-key="4ao90-0-0">
</div>
<div class="_1mf _1mj" data-offset-key="4ao90-0-0">
<span data-offset-key="4ao90-0-0"><span data-text="true">還有非常多的面向,多數我甚至根本列不出來。因為我只是一知半解。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="d7mcq-0-0">
<div class="_1mf _1mj" data-offset-key="d7mcq-0-0">
<span data-offset-key="d7mcq-0-0"><br data-text="true" /></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="cji6t-0-0">
<div class="_1mf _1mj" data-offset-key="cji6t-0-0">
<span data-offset-key="cji6t-0-0"><span data-text="true">當他們分享他們的心得,被一些更外行人的崇拜。也許很多真正的專家會覺得不爽、不屑。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="efv7b-0-0">
<div class="_1mf _1mj" data-offset-key="efv7b-0-0">
<span data-offset-key="efv7b-0-0"><span data-text="true">level 30 的人看 level 1 的人崇拜 level 3 、level 5 的人,覺得不爽、不屑。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="34ivs-0-0">
<div class="_1mf _1mj" data-offset-key="34ivs-0-0">
<span data-offset-key="34ivs-0-0"><span data-text="true">但多想一下,其實反而應該開心才對,因為這代表有更多外行人認同了你的價值觀、你的方向。即使他們目前還無法領略真正精妙之處,但至少方向是正確了。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="eua7g-0-0">
<div class="_1mf _1mj" data-offset-key="eua7g-0-0">
<span data-offset-key="eua7g-0-0"><br data-text="true" /></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="cfj07-0-0">
<div class="_1mf _1mj" data-offset-key="cfj07-0-0">
<span data-offset-key="cfj07-0-0"><span data-text="true">本來,要開心欣慰,還是不爽不屑,是個人自由。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="ena1u-0-0">
<div class="_1mf _1mj" data-offset-key="ena1u-0-0">
<span data-offset-key="ena1u-0-0"><span data-text="true">只是要改變大眾對次文化的害怕和誤解,是很困難的工作。光是站在那裡,沒有惡意,就很容易嚇跑人了(專家的溝通方式,通常外行人看來會太過尖銳、嚴厲)。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="on8b-0-0">
<div class="_1mf _1mj" data-offset-key="on8b-0-0">
<span data-offset-key="on8b-0-0"><span data-text="true"> </span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="5228l-0-0">
<div class="_1mf _1mj" data-offset-key="5228l-0-0">
<span data-offset-key="5228l-0-0"><span data-text="true">而且別忘了,我們剛開始時,也曾經被這種名不符實的稱讚激勵、推動過。像是因為在親友間的表演被大大的稱讚,或是從朋友、班上最強開始我們的旅程。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="bdkf1-0-0">
<div class="_1mf _1mj" data-offset-key="bdkf1-0-0">
<span data-offset-key="bdkf1-0-0"><br data-text="true" /></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="7olr5-0-0">
<div class="_1mf _1mj" data-offset-key="7olr5-0-0">
<span data-offset-key="7olr5-0-0"><span data-text="true">有一個說法是,如果有一件事,是所有成年人都該知道的事,那每一天都有 24 萬人第一次聽到。(<a href="https://xkcd.com/1053/">https://xkcd.com/1053/</a>)</span></span></div>
<div class="_1mf _1mj" data-offset-key="7olr5-0-0">
<br />
<div class="separator" style="clear: both; text-align: center;">
<span data-offset-key="7olr5-0-0"><span data-text="true"><a href="https://imgs.xkcd.com/comics/ten_thousand.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://imgs.xkcd.com/comics/ten_thousand.png" /></a></span></span></div>
<span data-offset-key="7olr5-0-0"><span data-text="true">
</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="31qiu-0-0">
<div class="_1mf _1mj" data-offset-key="31qiu-0-0">
<span data-offset-key="31qiu-0-0"><br data-text="true" /></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="4lrlt-0-0">
<div class="_1mf _1mj" data-offset-key="4lrlt-0-0">
<span data-offset-key="4lrlt-0-0"><span data-text="true">一面踢初入門者、不屑人,一方面又抱怨大眾都不了解你,其實是有點自我矛盾。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="cps2b-0-0">
<div class="_1mf _1mj" data-offset-key="cps2b-0-0">
<span data-offset-key="cps2b-0-0"><br data-text="true" /></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="2nhha-0-0">
<div class="_1mf _1mj" data-offset-key="2nhha-0-0">
<span data-offset-key="2nhha-0-0"><span data-text="true">其實換個角度,當作是回憶一下自己最初的感動也挺好的。</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="fer84-0-0">
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/oWCtSIqubUA/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/oWCtSIqubUA?feature=player_embedded" width="320"></iframe></div>
<div class="_1mf _1mj" data-offset-key="fer84-0-0">
<span data-offset-key="fer84-0-0"><span data-text="true">https://www.youtube.com/watch?v=oWCtSIqubUA</span></span></div>
</div>
<div class="" data-block="true" data-editor="2hpmk" data-offset-key="95en5-0-0">
<div class="_1mf _1mj" data-offset-key="95en5-0-0">
<span data-offset-key="95en5-0-0"><br data-text="true" /></span></div>
</div>
</div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-21711736366712360442016-01-26T11:32:00.002+08:002016-01-26T11:35:39.946+08:00人類是如何被 AI 統治的<div data-contents="true">
<div class="_45m_ _2vxa" data-block="true" data-offset-key="fen8u-0-0">
<span data-offset-key="fen8u-0-0"><span data-text="true"> 在這篇 <a href="https://www.washingtonpost.com/news/the-switch/wp/2015/03/24/apple-co-founder-on-artificial-intelligence-the-future-is-scary-and-very-bad-for-people/" target="_blank">Apple co-founder on artificial intelligence: ‘The future is scary and very bad for people’</a> 後面的留言中, Woz 澄清: "And if AI gets smarter than humans, it would have much more intelligence than we do and figure out ways to make the world work, not fight."</span></span></div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="3d4sk-0-0">
<span data-offset-key="3d4sk-0-0"><span data-text="true">這才是可怕的地方。</span></span></div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="7d296-0-0">
<span data-offset-key="7d296-0-0"><span data-text="true">雖然像是 Stephen Hawking, Elon Musk, Bill Gates 等人都擔心人類被 AI 統治(如 <a href="https://en.wikipedia.org/wiki/Open_Letter_on_Artificial_Intelligence" target="_blank">Open letter on AI</a>),但還是很多人覺得有點杞人憂天。</span></span><br />
<br />
<span data-offset-key="4gfap-0-0"><span data-text="true">我猜多數人心中想像被 AI 統治的情景,大概會是魔鬼終結者、 駭客任務那樣。</span></span><br />
<br />
<span data-offset-key="4gfap-0-0"><span data-text="true">其實 AI統治人類的未來,比較像 Psycho pass (表面上)或者關鍵報告那樣,至少剛開始會是這樣。而且時間上也至少五十年起跳,兩三個世紀這種數量級。</span></span><br />
</div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="4gfap-0-0">
</div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="fj6s7-0-0">
<span data-offset-key="fj6s7-0-0"><span data-text="true">說起來也不是什麼壞事,而且基本上也無可避免,除非被更壞的未來取代。只不過情感上,還是希望看到人類能以現在的模樣發揮更多的潛力和可能性(也許還能給 AI 一些留下人類的理由)。</span></span></div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="b1o6k-0-0">
</div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="b1o6k-0-0">
<span data-offset-key="b1o6k-0-0"><span data-text="true">真的要說,這種未來正在實現。比方導航就是一個例子。也許你會說,這只是一個工具,像汽車、機車一樣,人類還是有主導權。但也真的有人導航導到水裡面的狀況發生。 差別在哪裡? 如果你對於導航的原理、特性夠了解,就比較能知道它什麼時候會出錯,為什麼會出錯,甚至能除錯,而不會盲目相信(或者說即使懷疑,也沒有別的辦法)。</span></span></div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="56ajm-0-0">
<span data-offset-key="56ajm-0-0"><span data-text="true">所以延後 AI 統治人類時間的方式,就是跟他比賽,讓更多人更加了解它。</span></span></div>
<div class="_45m_ _2vxa" data-block="true" data-offset-key="8cp38-0-0">
<span data-offset-key="8cp38-0-0"><br data-text="true" /></span></div>
</div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-70109217290795194612015-12-14T13:08:00.002+08:002015-12-14T18:42:06.375+08:00刺客聶隱娘觀後感<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRws-Lrp9_smm3fDvn1x0Na0oifoCVtxC9UusJXxiCh1zwM9v5zb-ndg5fsmrqryhRfQKd9k9JPYC8MIphLqEB1kJXT5xTBN6j6C1NePIn0TQKr2TPJgzSREezOMlOLmW93z5llwZx2aI/s1600/The_Assassin_Poster.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRws-Lrp9_smm3fDvn1x0Na0oifoCVtxC9UusJXxiCh1zwM9v5zb-ndg5fsmrqryhRfQKd9k9JPYC8MIphLqEB1kJXT5xTBN6j6C1NePIn0TQKr2TPJgzSREezOMlOLmW93z5llwZx2aI/s640/The_Assassin_Poster.jpg" width="468" /></a></div>
<br />
昨天看了聶隱娘,感覺有點後勁。<br />
整部電影不斷在傳達孤獨、蒼涼這個感覺。剛看完其實覺得傳達的有點淺、有點不夠孤獨,但後來慢慢就不那麼確定了。可能也跟敘事有點複雜難解有關。<br />
這這種感覺當然從對白中被很直接的敘述出來了,但我被電影的影像和聲音打中的,也就那一堆被人詬病的無聊鏡頭很乏味的聲音。<br />
這種孤獨、蒼涼感,有點類似「前不見古人,後不見來者,念天地之悠悠,獨愴然而涕下」那種感覺,那種宇宙雖大、沒有同類的孤獨感。<br />
事實上,電影中窈七也哭了,為了另外一個代表孤獨這個概念的娘娘而哭。這個哭不是單純因為孤獨寂寞而哭。而是一種同理心的哭法。因為窈七能同理娘娘的訣別與孤寂。但這還不到哭點。關鍵點是,窈七同理到,娘娘因為自身的孤獨,也能同理窈七的孤獨。<br />
因為這種理解與被理解,能短暫的解放沒有同類的孤獨感。<br />
<br />
其實這類描寫沒有同類的孤獨感故事、電影很多。一堆中二救世主電影像是分歧者、飢餓遊戲這類莫名其妙的苦瓜臉主角,往往都是自認我很特別、你們沒有人了解我。<br />
影集 Doctor who 三不五時也在玩這個把戲。<br />
其實搜尋一下,the loneliest people are the kindest 的圖片,你就會了解有知道有多少這類的故事。而這句話也解釋了一部分聶隱娘的劇情與性格。<br />
但聶隱娘有趣的是他幾乎單純用影像、聲音、動作、節奏這種方式來刻劃這種感覺。<br />
<br />
要更進一步描述這部電影想傳達的孤寂感,我引用下面幾句話:<br />
<br />
“You can have everything in the world and still be the loneliest man. And that is the most bitter type of loneliness.” ―Freddie Mercury<br />
<br />
“People think being alone makes you lonely, but I don't think that's true. Being surrounded by the wrong people is the loneliest thing in the world.” ―Kim Culbertson<br />
<br />
<br />
“Loneliness - If you find yourself struggling with loneliness, you're not alone. And yet you are alone. So very alone.” ―Larry Kersten<br />
<br />
<br />
但真正要能接收到,可能還是要直接去看電影。tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-66589073692105933762015-07-02T11:50:00.000+08:002015-07-02T11:53:45.968+08:00心法: 解開封印<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIMaDUMEiUdj1Y7jJgaR6Sv5kQeu10ywKK40dDRPxCAUTMIMyZedIsaAQOvXYsqjX8bf_j8fAXmnk-71-E4SVjKtOwuTV_8r2IBA8PbtWx_UAqdLpeFmePJYENCk-U9ueMs4G9kAna_EY/s1600/4687465378_64ff08d78f_b.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIMaDUMEiUdj1Y7jJgaR6Sv5kQeu10ywKK40dDRPxCAUTMIMyZedIsaAQOvXYsqjX8bf_j8fAXmnk-71-E4SVjKtOwuTV_8r2IBA8PbtWx_UAqdLpeFmePJYENCk-U9ueMs4G9kAna_EY/s640/4687465378_64ff08d78f_b.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a class="owner-name truncate" data-rapid_p="33" data-track="attributionNameClick" href="https://www.flickr.com/photos/ryochijiiwa/" title="移至 Ryo Chijiiwa 的所有相片">Ryo Chijiiwa</a>
<br />
<div class="view follow-view clear-float photo-attribution" id="yui_3_16_0_1_1435801934953_125188">
<span class="relationship">
</span></div>
<div class="view attribution-view clear-float photo-attribution" id="yui_3_16_0_1_1435801934953_124941">
<div class="attribution-info">
<div class="view follow-view clear-float photo-attribution" id="yui_3_16_0_1_1435801934953_125188">
<span class="relationship">
</span></div>
</div>
</div>
(CC-By)</td></tr>
</tbody></table>
<br />
這是我在教以及學上面,覺得能夠一以貫之的心法。那就是用解開封印的態度去面對學習的方法、目的或任何事,或者也可以叫做釋放、使自由。<br />
比方說,與其說培養獨立思考的能力,不如說釋放獨立思考的能力。與其說培養自學的能力,不如說釋放自學的能力。畢竟,被培養出來的能力,是否真的能稱為獨立思考或者自學還有疑慮。<br />
先看兩個故事:<br />
<br />
<b>剪斷翅膀的天鵝</b> (<a href="http://www.watchinese.com/article/2014/17662" target="_blank">來源</a> 以下引述並刪修)<br />
1968年,美國的一個媽媽發現他的三歲女兒居然認識了禮品盒上「OPEN」的第一個字母「O」,感到很吃驚。發現是幼稚園老師教的。這個媽媽一狀告上法庭,理由是幼稚園剝奪了女兒的想像力。因為她女兒在認識「O」之前可以把「O」說成太陽、蘋果、足球、鳥蛋之類的東西,然而在幼稚園教她認識「O」“之後,就失去了這種想像力。她要求幼稚園賠償精神傷殘費1000美元。<br />
三個月後,幼稚園居然敗訴了。因為全部陪審員都被這位媽媽講的故事感動了:「我曾到東方某國旅行,在一家公園裏見到兩隻天鵝,一隻被剪去了左邊的翅膀,被放養在較大水塘裏;另一隻完好無損,被放養在較小的水塘裏。管理員說,這樣能防止它們逃跑。剪去左邊翅膀的天鵝無法保持平衡,所以無法飛;放在小水塘裏,因沒有足夠的滑翔路程,也只能待在水裏。當時我非常震驚和悲哀,今天這場官司,是因為幼稚園剪掉了小孩一隻想像的翅膀,過早地把她投進了那片只有26個字母的小水塘。一隻沒有翅膀的天鵝是永遠都飛不起來的。」<br />
<br />
<b>四分鐘魔咒</b>(<a href="http://bm11tw.pixnet.net/blog/post/9494077-%E6%96%87%E7%AB%A0%E5%88%86%E4%BA%AB-%E5%9B%9B%E5%88%86%E9%90%98%E9%AD%94%E5%92%92%28%E5%9B%9B%E5%88%86%E9%90%98%E9%9A%9C%E7%A4%99%29" target="_blank">來源</a> 以下引述刪修) <br />
1945年,Gunder Andersson 創造了4分零1秒4跑完1英哩的紀錄。多數人認為,這超出了人的生理上限。有好幾年的時間,人們一直堅信,不可能在四分鐘的時間內跑完一英哩。慢慢就被大家公認、稱為「四分鐘障礙」。運動員們、甚至連生物學家也確定,這已經超過了人類身體和心理的生物極限。<br />
這個障礙維持了九年。 1954年5月6日,英國醫學院學生羅傑班尼斯特率先以3分59秒4突破了這一障礙,新增了一個更強大的障礙。但事實上,他的記錄僅維持了 46天,之後四分鐘極限也一次又一次被打破。<br />
現在,世界上能夠在四分鐘內跑完一英哩的運動員超過幾百名,他們中甚至還包括許多高中生。 <br />
當羅傑班尼斯特成功突破「四分鐘障礙」後,其他運動員就看見了自己的可能性,他讓每個人都明白了,四分鐘內跑完一英哩是可能的。魔咒也就消失了。<br />
<br />
<b>封印以及解封</b><br />
這兩個故事都說明了想像以及思考自由的重要性,兩個故事分別對應了封印以及解開封印這兩件事情。<br />
我們所有的學習,其實都能視為一種解開封印、釋放、使自己自由過程。學習語言、文字是如此。與其將語言文字視為鍛鍊、訓練出來的能力,不如說,我們本來就有使用文字語言的潛能,教育及學習的過程,只是讓這項潛能發揮出來而已。而學習之後,可以使我們更自由,能接觸很多原本接觸不到的資訊,開放了一個新的世界中。反而原本不識字的情形,像是被鎖在一個小房間中一樣。<br />
同樣的,程式語言、數學、運動、藝術、音樂的各項技能也是如此。 很多時候學到的東西是幫你清掉路障的。<br />
<br />
<b>教育的現況:加上封印</b> <br />
但有趣的事情來了,有時候,這種路障是無形的。比方你想探索一個新的世界,固然有一種路障是很直接的,把你的路擋住了。但相反的,如果出現成千上百個選項,你不知道從何選起,一樣不容易找到正確的路。這等於是一種無形的路障。<br />
你要怎麼清掉這種無形的路障呢?最簡單的方法就是,就是把不能走的路,標上此路不通然後擋住。或者更輕鬆,直接標出可以走的路,其他的路全部當成不能走。<br />
原本的目的是要釋放更開闊的世界,但實際上卻很容易退化成加上無數的封印。 兩者表面上的差異很微妙。<br />
這就是第一個故事的點。關鍵就在於,學到 O 這個字母,到底是多了一種想像的可能,還是減少了無數的可能?<br />
<br />
另外一個情形是,當你試著當一個解開封印者,解開了一些封印後,你很可能會被你放出來的東西嚇到,想用另外一個封印把他封回去。有點像是五行山腳下放出的孫悟空,馬上就加上頭箍一樣。<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl-6j0JUi47dHSYLee-3nWKpH-3zJRXCUxBQlwPc9jnjK6pdJVaHbXWhwgPARwHovy5UU1XElUkuF5KnynexupA9xK0vy_W-zCx70GUnCvTGaeI3WtIzrCwf_8Tks07USL4HxeAR2ixX8/s1600/battle_room.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="616" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl-6j0JUi47dHSYLee-3nWKpH-3zJRXCUxBQlwPc9jnjK6pdJVaHbXWhwgPARwHovy5UU1XElUkuF5KnynexupA9xK0vy_W-zCx70GUnCvTGaeI3WtIzrCwf_8Tks07USL4HxeAR2ixX8/s640/battle_room.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">xkcd 241: Battle Room</td><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
<br />
<br />
其實這只是個心法,有點像是 "<a href="http://ender.wikia.com/wiki/Battle_Room" target="_blank">the enemy's gate is down</a>"。比方,不是克服懶惰、戒除懶惰,而是從懶惰中釋放出來。打電玩是好還是壞?要看你是被電玩限制住了,還是電玩打開了你一個可能的世界? 是不是你除了電玩之外的路都被東西檔住了?<br />
大概就是如此。 <br />
<br />
--<br />
附註:這兩個故事都有一些爭議,特別是第一個故事,很有可能是大量添油加醋甚至是偽造的故事。 而第二個故事大致上是事實,不過有一些歷史上的<a href="http://www.wikiwand.com/en/Four-minute_mile" target="_blank">小爭議</a>。<br />
<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-32681906526140095892015-06-12T11:38:00.003+08:002015-06-12T11:38:48.294+08:00弱者思維<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJf1jsldaO0rgm5qnOPHIdYr1BU6erOBmjjORJIvVLkh79LZszEAtYPzwexF6RoFh01boUkf2lNjPUgiH-axm0B1sh5GMotk0qsstBHrRHMFdmIqtW77QbiRxaWUppEcmI3xkculQHhu0/s1600/B-1qb1WWkAIwBlD.jpg%253Alarge.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJf1jsldaO0rgm5qnOPHIdYr1BU6erOBmjjORJIvVLkh79LZszEAtYPzwexF6RoFh01boUkf2lNjPUgiH-axm0B1sh5GMotk0qsstBHrRHMFdmIqtW77QbiRxaWUppEcmI3xkculQHhu0/s640/B-1qb1WWkAIwBlD.jpg%253Alarge.jpeg" width="640" /></a></div>
<br />
今天看到這個臉書上看到關於 2013 年的這個事件(<a href="http://www.solidot.org/story?sid=37355" target="_blank">solidot</a>) 的討論:<br />
<blockquote class="tr_bq">
谋智中国开发的<a href="http://download.firefox.com.cn/chinaedition/addons/cehomepage/cehomepage-latest.xpi" target="_blank"><u>定制主页及快速拨号页扩展</u></a>(xpi文件)<a href="http://bbs.kafan.cn/thread-1655820-9-1.html" target="_blank"><u>被发现</u></a>会
修改用户的快捷方式,如果中国用户在主页建立了京东商城(www.jd.com)的拨号快捷键,那么该快捷键会被定向到一个返利链接
(http://count.chanet.com.cn/click...www.jd.com”)。该扩展的中国开发者还非常幽默的在源代码里加了一
个注解——“do some evil here”。 </blockquote>
在 news group 中,就有人質疑這件事情,然後得到的回答是說,中國很多廠商都做同樣的事情,不是我們的錯。別人都用賤招,你還在那裡乾淨的對決是沒用的。當你被擊倒淘汰時,什麼都別說了。 <br />
當然這有點斷章取義,不過這種想法很常見。所以我想討論的是這段文字討論的想法。<br />
<br />
無獨有偶的,剛好也在臉書上看到有人提到魔術師抄襲的事情。當他勸他們不要抄襲的時候,得到的回應是:「剛開始總要抄襲啊,我又不像你,那麼厲害有創意。」<br />
<br />
常常我們要別人思考理解,再來解數學題,而不要先看答案、背解答時,得到的回應也常常是:「一般人不像你們那麼聰明,你們不了解一般人只能靠背的來學數學。」<br />
<br />
看出共同點了嗎? 就是滿滿的「沒辦法、沒辦法、沒辦法」,然後把原因歸咎到環境什麼的,不願意嘗試改變自己。<br />
<br />
就是因為一直否認自己需要成長,所以才無法成長啊。<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-88748571714848146062015-05-13T14:34:00.003+08:002015-05-13T16:03:23.942+08:00文化剝煮產業<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIG4i10SAih5xnb-pC5FM6K6dpMCkB5SkOP-wE3YkV9JN5QBMmHPSDWc6CTaWI2ghKAOhuEWz2PQi-dgdwC4KjejTrfSF3vdt69Cw9euteAr5Yk5Q7V3FAEUTu0Of1-pgHwD1aezD2B2A/s1600/%E9%86%AB%E7%A7%91.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIG4i10SAih5xnb-pC5FM6K6dpMCkB5SkOP-wE3YkV9JN5QBMmHPSDWc6CTaWI2ghKAOhuEWz2PQi-dgdwC4KjejTrfSF3vdt69Cw9euteAr5Yk5Q7V3FAEUTu0Of1-pgHwD1aezD2B2A/s1600/%E9%86%AB%E7%A7%91.png" /></a></div>
遠看、近看各有不同的 Hybrid Image, 因為有 High pass, Low pass filter 以及高斯模糊化的概念,考慮拿來教學上當例子。 其實很早以前也弄過類似的東西" <a href="http://weijr-note.blogspot.tw/2007/08/blog-post_24.html" target="_blank">遠看林志玲,近看像國父</a>"。 也有人寫了 <a href="http://jeremykun.com/2014/09/29/hybrid-images/" target="_blank">Python 的逐步解釋</a>。 不過我用的是 GIMP。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUUgGAO5BWKm1M2Y-7jYgJYTKjUetHWQot-SLkC3a9l09J1VVeTbx7kDgTZ7EndVibERH26xwbSJdAYwYmODZGN5SC8pBx6QD0qK4RyKWur2hXgg_YrTigJwaCyP1vPj35ZdkowCNAUs/s1600/%E6%84%9B%E6%83%85.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="354" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUUgGAO5BWKm1M2Y-7jYgJYTKjUetHWQot-SLkC3a9l09J1VVeTbx7kDgTZ7EndVibERH26xwbSJdAYwYmODZGN5SC8pBx6QD0qK4RyKWur2hXgg_YrTigJwaCyP1vPj35ZdkowCNAUs/s640/%E6%84%9B%E6%83%85.png" width="640" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxOP4vWKH1EHvKEtMrbJE-QhYpsPf_CYEER01h3ZmHIZoJu4da-tf3uIsonIGxyrFuCxTHV21eWHtOodVfVqVRMVrfEa38vCnSjmmC5xWdQ7TnYaP253r0RA0yqeGZDsio5tHoUlkXlfc/s1600/%E6%96%87%E5%8C%96%E5%89%B5%E6%84%8F%E7%94%A2%E6%A5%AD.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxOP4vWKH1EHvKEtMrbJE-QhYpsPf_CYEER01h3ZmHIZoJu4da-tf3uIsonIGxyrFuCxTHV21eWHtOodVfVqVRMVrfEa38vCnSjmmC5xWdQ7TnYaP253r0RA0yqeGZDsio5tHoUlkXlfc/s1600/%E6%96%87%E5%8C%96%E5%89%B5%E6%84%8F%E7%94%A2%E6%A5%AD.png" /></a></div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-55539642958062607672015-04-25T21:45:00.001+08:002015-04-26T22:31:01.062+08:00Gigabyte 輕薄電競筆電 P34K V3 開箱<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPYphC7eoebL5MkXj8NOL7en2JMhtuj3Znhjj6SUdu3p1SLmLlJfpcOHtSPLgNOooXRxyIlRwh2x-n5AxJrsQIJ4W_n4Nt8ECVGkxpA3hSGrLsySvm_YR0FU-0lSA3KPjJXL-i6yQJQ14/s1600/P_20150425_162623.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPYphC7eoebL5MkXj8NOL7en2JMhtuj3Znhjj6SUdu3p1SLmLlJfpcOHtSPLgNOooXRxyIlRwh2x-n5AxJrsQIJ4W_n4Nt8ECVGkxpA3hSGrLsySvm_YR0FU-0lSA3KPjJXL-i6yQJQ14/s1600/P_20150425_162623.jpg" height="360" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
其實也不算開箱,因為已經開了一陣子了。 主要是想玩 CUDA(Theano, gpuarray, gputools) 但手邊沒有 nvidia 的顯卡,順便又能有台筆電玩 Unreal Engine 及 Kinect V2。</div>
<div class="separator" style="clear: both; text-align: left;">
鎖定的機型是 GTX965m 以上的輕薄筆電。 輕薄的考量是因為攜帶方便,而且能在演講使用。鎖定新的 nvidia 9 系列是因為據說效能及省電能力都有進步(從<a href="http://en.wikipedia.org/wiki/GeForce_900_series#GeForce_900M_.289xxM.29_series" target="_blank">資料</a>上來說,965m以上的與 960m 以下的是不同等級)。</div>
<div class="separator" style="clear: both; text-align: left;">
考慮的候選人有 Gigabyte P34K, P34W, P35W, P35X, MSI GS60 系列, AORUS X3。</div>
<div class="separator" style="clear: both; text-align: left;">
ASUS 的 UX501, GL501 也觀望中,不過因為是 GTX960m,所以捨棄。P35 系列重量約 2.2-2.3 公斤,雖然也不太重,但因為常常要攜帶,選擇了 2.0公斤以下的機種。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
最後再考慮其實真正要做計算時,還是會用遠端的方式,所以選了使用新9系列入門級的 965m 的 P34K V3,耗電量可能比較低。</div>
<div class="separator" style="clear: both; text-align: left;">
簡單列一下 P34K V3 的規格:</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li>NVIDIA® GeForce® GTX 965M GDDR5 2GB</li>
<li>8G DDR3L 1600 </li>
<li>僅1.71-1.8Kg、20.9mm</li>
<li>128GB mSATA SSD + 1TB HDD</li>
<li>Full HD廣視角液晶螢幕 </li>
<li>Intel® Core™ i7 4720HQ </li>
</ul>
雖然硬碟只有 5400 rpm,記憶體只有 8G 感覺有點弱,但價格不到 45k, 送個背包和滑鼠。<br />
<div class="separator" style="clear: both; text-align: left;">
自動光源調整背光鍵盤搭車時使用很實用。同時有 D-SUB 和 HDMI 接頭,到處演講報時比較方便。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
實際拿到的第一感是有點重。但這大概是因為用習慣 MBA 11" (1.1Kg) 及 Sony VAIO DUO 13(1.33Kg),過一陣子之後就覺得還好。</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLoKB-9GXjqfHzIfK7hRZFOkyJ78BXKfHHu_avpu9WuAjYPc6ozXimG7iAVXsjTRKgmCDGRM7xNLJ1JCY_65qJSwFKJFhV291e9VNN6gmAhYwri2qRBTnbDgm0X3ps06TNigfO91zGuk0/s1600/P_20150426_202819.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLoKB-9GXjqfHzIfK7hRZFOkyJ78BXKfHHu_avpu9WuAjYPc6ozXimG7iAVXsjTRKgmCDGRM7xNLJ1JCY_65qJSwFKJFhV291e9VNN6gmAhYwri2qRBTnbDgm0X3ps06TNigfO91zGuk0/s1600/P_20150426_202819.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">實測重量 1818g</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSpUVc3r_bYtlcm8gAL_Zk6CJvc8TuavGnVEyfWMvuA3tajTQmvZ1zR2JADsZQYqrVFlzFvcRHqvOj9LEuvT2i7Z_6PFGdtlsm79NikNty4TltjdTq6T4i1ha5OzGJG6H978AeEbRJMTA/s1600/P_20150425_162841.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSpUVc3r_bYtlcm8gAL_Zk6CJvc8TuavGnVEyfWMvuA3tajTQmvZ1zR2JADsZQYqrVFlzFvcRHqvOj9LEuvT2i7Z_6PFGdtlsm79NikNty4TltjdTq6T4i1ha5OzGJG6H978AeEbRJMTA/s1600/P_20150425_162841.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">MBA 11" vs P34K</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaCvMSVqfDh4zL6M9ddyRGKR5WYUpWwUY7I9iL1wMoZESG80Q6ZZEQ5Ul6UeePDab7EhXhLJChHAK-jyy0q84flza-0vLo3itA5XVxUQbawlVws2xkB6aGJ64x-Re4Wf9A6X0cFKoS5B0/s1600/P_20150425_163050.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaCvMSVqfDh4zL6M9ddyRGKR5WYUpWwUY7I9iL1wMoZESG80Q6ZZEQ5Ul6UeePDab7EhXhLJChHAK-jyy0q84flza-0vLo3itA5XVxUQbawlVws2xkB6aGJ64x-Re4Wf9A6X0cFKoS5B0/s1600/P_20150425_163050.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">DUO 13 vs P34K</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_0uz-i3stYeFituJHh9kq4fJQHP5Uj5nKq54mEuON-_anJFsdf-OvkyaJfcvHjBk-G28iwqJ5I4V-vX6wImI8gt3_4EChIhHvuMlQkz-ENAlX0ZXCAW5jw2BBvVWNf0o5XWNqNbl5QTE/s1600/P_20150425_163216.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_0uz-i3stYeFituJHh9kq4fJQHP5Uj5nKq54mEuON-_anJFsdf-OvkyaJfcvHjBk-G28iwqJ5I4V-vX6wImI8gt3_4EChIhHvuMlQkz-ENAlX0ZXCAW5jw2BBvVWNf0o5XWNqNbl5QTE/s1600/P_20150425_163216.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">3台打開了比較</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
變壓器有點大,但不算太重。</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE5pOkmpFIWw9hDz9LZBREpMEcKijjkBe1nGB_AZMcCXJhyC5FD7Rnx3VmyFopfzP8kn-1np2MU392yuX_XVahfEBYMqo6MWpqIySp5JAVz2dod9ANYM6hWH2WfOSEO1dXwFvZI8wgmMk/s1600/P_20150426_203001.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE5pOkmpFIWw9hDz9LZBREpMEcKijjkBe1nGB_AZMcCXJhyC5FD7Rnx3VmyFopfzP8kn-1np2MU392yuX_XVahfEBYMqo6MWpqIySp5JAVz2dod9ANYM6hWH2WfOSEO1dXwFvZI8wgmMk/s1600/P_20150426_203001.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">實測變壓器重量 424g</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOve5STj68rF-A-y5EamYYAmLRKnfqY0zg587180gu4ZVHu0HyccJlyHMsBbI-gp6J-9VwPBpn-TSPlEfMF1BCQ-gp-Fumy6cY5P354cBLiZB0sgqYU6FbE0m5hpt3O_UV9h0p3virTz0/s1600/P_20150425_163405.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOve5STj68rF-A-y5EamYYAmLRKnfqY0zg587180gu4ZVHu0HyccJlyHMsBbI-gp6J-9VwPBpn-TSPlEfMF1BCQ-gp-Fumy6cY5P354cBLiZB0sgqYU6FbE0m5hpt3O_UV9h0p3virTz0/s1600/P_20150425_163405.jpg" height="360" width="640" /></a></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtO5-q8WgO2D8lQsSDp4lGS_DlZGmbzt8Pet_LxorokP22uarOUd4hZvR5bHxn5qlz0gnshqYRTOwVAPiw2JY5GA9e2kTXoiLyDLjJOqmGKtjn3ej_ojrox-UXFCHDLaeQ5QwzPE15AF4/s1600/P_20150425_163627.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtO5-q8WgO2D8lQsSDp4lGS_DlZGmbzt8Pet_LxorokP22uarOUd4hZvR5bHxn5qlz0gnshqYRTOwVAPiw2JY5GA9e2kTXoiLyDLjJOqmGKtjn3ej_ojrox-UXFCHDLaeQ5QwzPE15AF4/s1600/P_20150425_163627.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">和 Duo 13 及 MBA 11 變壓器比較</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
實際攜帶上,因為贈送的背包很輕,筆電連同變壓器背起來感覺不太到重量。加上都很薄,也不會顯得很笨重。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
風扇有時沒什麼事也會吵,但 GPU 和 CPU 熱度都還好。電池電量用起來感覺也還好,但沒有完整實測。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
拿到機器之後的第一件事是裝 Linux。把 Ubuntu 14.10 裝進 1TB 的硬碟沒有太大的困難。裝上 nvidia 346, Bumblebee, cuda 7 要多花一點力氣,不過也 OK。</div>
<div class="separator" style="clear: both; text-align: left;">
實際上裝玩 Theano + OpenBLAS,跑 check_blas.py 來測, CPU 是 1.5-1.6 秒, GPU 則是 0.15-0.16 秒。 GPU 約等於 GTX770 在 CUDA 6.5 的成績。</div>
<div class="separator" style="clear: both; text-align: left;">
另外一個可以拿來測試 CUDA 的是 Blender 的 <a href="http://blenderartists.org/forum/showthread.php?239480-2-7x-Cycles-benchmark-(Updated-BMW)" target="_blank">BMW Benchmark</a> 。在 blender 2.74 下, CPU 跑到 5 分 51 秒, GPU 在適當的切割下,可以跑到 2分 35 秒(win64/linux 下都差不多,linux 下最好可以到 2:30) 不曉得怎麼比,不過看到 GTX 980 有 1分 44 秒的紀錄。</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPXeumBu3HO32oblujaquYrdss60evM6MegEV5jn_V-XUbq203iL8za8D0EtCThFqyBbOs_OOgVjRmLpHZfId0TKrFUHu6_1uZ1RCSC_kpMWbve38Z4wwEG7qhr7UcrXAaFeXAWVnzC-4/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(4).png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPXeumBu3HO32oblujaquYrdss60evM6MegEV5jn_V-XUbq203iL8za8D0EtCThFqyBbOs_OOgVjRmLpHZfId0TKrFUHu6_1uZ1RCSC_kpMWbve38Z4wwEG7qhr7UcrXAaFeXAWVnzC-4/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(4).png" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Win8.1 64 bit, Blender 2.74</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
如果是舊版的 BMW 測試檔,則可以到 1分4秒。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpgsPdLWCUNADUJjOsVgUOIUtbR5jezwy8pHRIldlOJ-GOelq7WmGCDz2M6wlSVCKGqN-eD8julvMkMZYOwAFrrZEQLhsemW9_BlIN8LNwbb4noVAXD3h07a76o53YTSbC3sNN_ymHzBw/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(8).png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpgsPdLWCUNADUJjOsVgUOIUtbR5jezwy8pHRIldlOJ-GOelq7WmGCDz2M6wlSVCKGqN-eD8julvMkMZYOwAFrrZEQLhsemW9_BlIN8LNwbb4noVAXD3h07a76o53YTSbC3sNN_ymHzBw/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(8).png" height="360" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
總之, CUDA 的效能還算可以,只是僅限於 float32, float64 太弱。</div>
<div class="separator" style="clear: both; text-align: left;">
至於遊戲方面,我沒有太常玩,所以手上積了一堆舊遊戲,只能拿舊遊戲來測,像是 Witcher 2, Alice Madness Return, Tomb Raider 都還算順。</div>
<div class="separator" style="clear: both; text-align: left;">
另外也用Dolphin 跑了 Super Mario Galaxy 看看,D3D 也算順暢(60fps)。</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3hgx8T6IBYzquuDkG-eCGiVu7Qvu4QIeD7DAjbQKeJ20OLnFvVt27e3d0tALAIGYH6wN8Y21r0Dlku7f3VjlbCsoXZ7PJLKhBH2hxj92kjrXSCe8DgFB_jTZeIcVTvcjDRZclZNXhRjg/s1600/vlcsnap-2015-04-24-16h06m41s600.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3hgx8T6IBYzquuDkG-eCGiVu7Qvu4QIeD7DAjbQKeJ20OLnFvVt27e3d0tALAIGYH6wN8Y21r0Dlku7f3VjlbCsoXZ7PJLKhBH2hxj92kjrXSCe8DgFB_jTZeIcVTvcjDRZclZNXhRjg/s1600/vlcsnap-2015-04-24-16h06m41s600.png" height="360" width="640" /></a></div>
<br />
Tomb raider 在 Full HD 的測試結果為<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAuoYD81yMVh5PqjegYfdG5MZZnUAHnybG4RNeFgvB8oKk-N_TtSFC57tB-WXUHRMWmH7tf-LYN4MQcmjdQSF_fh7CH18aa3xDdh6K4w4jXLESFy6_XiQ-fOFQ9AiO4S4qjvP85V6d9Cw/s1600/2015-04-24_00001.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAuoYD81yMVh5PqjegYfdG5MZZnUAHnybG4RNeFgvB8oKk-N_TtSFC57tB-WXUHRMWmH7tf-LYN4MQcmjdQSF_fh7CH18aa3xDdh6K4w4jXLESFy6_XiQ-fOFQ9AiO4S4qjvP85V6d9Cw/s1600/2015-04-24_00001.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">最高 Ultimate 1920x1080</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWiFL7KH_DJRhZJdN37aUgjkAWqICvthNTmyiF3-sbdzF9hyphenhyphenpCFcOT1ErhHhL6fc6Vx0NCttcUQQ83nolr9ZAcM2oC8FtF6BjBDS812D4mWf8FKRc5Mgww5jMz6ruidpNEzJWt52DvxY4/s1600/2015-04-24_00002.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWiFL7KH_DJRhZJdN37aUgjkAWqICvthNTmyiF3-sbdzF9hyphenhyphenpCFcOT1ErhHhL6fc6Vx0NCttcUQQ83nolr9ZAcM2oC8FtF6BjBDS812D4mWf8FKRc5Mgww5jMz6ruidpNEzJWt52DvxY4/s1600/2015-04-24_00002.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">很高 Ultra 1920x1080</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtuC4afiN56jf0o1tN6H3ri9_f9T5JbOVsevPaow-v_mUk_FMG2qja2RKvWlIM0elg-1udp3hIQMZLt64twpf73L6-CTSvs2Xl_upHYABO2RH_P4u-FDLFP6VFQjbbyxXvnsyH5NnUM04/s1600/2015-04-24_00003.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtuC4afiN56jf0o1tN6H3ri9_f9T5JbOVsevPaow-v_mUk_FMG2qja2RKvWlIM0elg-1udp3hIQMZLt64twpf73L6-CTSvs2Xl_upHYABO2RH_P4u-FDLFP6VFQjbbyxXvnsyH5NnUM04/s1600/2015-04-24_00003.jpg" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">高 High 1920x1080</td></tr>
</tbody></table>
以最低、平均來說<br />
<br />
<ul>
<li>High: 58.0/60.0</li>
<li>Ultra: 48.0/57.4</li>
<li>Ultimate: 29.1/38.5</li>
</ul>
<div>
既然提到 Kinect V2, Kinect V2 的測試也能維持穩定的 30 FPS</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDbCAWGAVAbUCARe0HTxhD1dMkYT1-qup6ON5S0bpOSyo-29QQKsNulZfE1b2p2-PbVpVUkA1O4xvBUV-Wq-8gvjUSjx_JizH2XkQjmuo8CYc5UFGHXgRCEq6BAQXk2s25rwq9HG-Lu1Q/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(6).png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDbCAWGAVAbUCARe0HTxhD1dMkYT1-qup6ON5S0bpOSyo-29QQKsNulZfE1b2p2-PbVpVUkA1O4xvBUV-Wq-8gvjUSjx_JizH2XkQjmuo8CYc5UFGHXgRCEq6BAQXk2s25rwq9HG-Lu1Q/s1600/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2+(6).png" height="360" width="640" /></a></div>
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
但是也有一些缺點<br />
<br />
<ul>
<li>螢幕蓋上時,風扇就會轉動(?)</li>
<li>Touch pad 有時會卡住一下,特別是在 BIOS 選硬碟開機啟動 Linux 時。</li>
<li>Linux 的 X 有時會卡住(我習慣用 Kubuntu)</li>
</ul>
<div>
這感覺是驅動硬體之間有點不穩的緣故。</div>
<div>
<br /></div>
<div>
有關安裝 Linux 及 Bumblebee 和 CUDA,那又是另外一個故事了,之後再來補充。</div>
<div>
<br /></div>
<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-44374979698933095132015-03-12T18:10:00.001+08:002015-03-12T18:16:17.889+08:00不要問國家能為你做什麼,要問你想拿國家來做什麼<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUG4V-BHJubgbW5dI9OQJk9m3pRUeht68dJg00vfC7ygoW-dwaFx0WNp3V7G8vltrQbxSbC-mGQ47Awlvn5EDkttMfx9njAM9y8UUTcx1Sv9xpvZi9oymoDZJpVQNX9aDVEYbkOBRwCAU/s1600/abraham-lincoln-437251_1280.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUG4V-BHJubgbW5dI9OQJk9m3pRUeht68dJg00vfC7ygoW-dwaFx0WNp3V7G8vltrQbxSbC-mGQ47Awlvn5EDkttMfx9njAM9y8UUTcx1Sv9xpvZi9oymoDZJpVQNX9aDVEYbkOBRwCAU/s1600/abraham-lincoln-437251_1280.jpg" height="400" width="300" /></a></div>
<br />
常常有人會問,「某某東西」有什麼用。<br />
其實這反應了兩件事情,一個是他可能不理解「某某東西」的用意或價值,另外外一個就是他可能缺乏利用這個「某某東西」的能力和想像力。這個不見得是人的錯,有時候「某某東西」真的就是無聊沒價值的東西,也有可能因為某些外在的因素,讓人無法或者不敢想像自己有利用「某某東西」的可能(比方說這個「某某東西」是國家的時候)。<br />
<br />
但既然會問這句話,那這個「某某東西」有價值的可能性不低,因為如果你肯定「某某東西」完全沒價值的話,你根本不會問這個問題。
所以這通常比較像是一個心理框架的轉換,當你想問「某某東西」有什麼用的時候,對的問題應該是「某某東西」能怎麼用。<br />
<br />
就像學到某個魔術技巧的時候,當你感受到這個技巧背後的巧思與神奇之後,你想的一定是要怎麼用這個技巧,如何用這個技巧來設計程序,而不是這個技巧有什麼用。
當然你更高階一點的話,你也不會執著於把技巧包裝成程序。你會把這個技巧的精神融入內化。當你想達成某個目標時,有需要時,自然會拿來使用。<br />
剛在 <a href="https://news.ycombinator.com/item?id=9189650">Hacker News</a> 上,看到一個人,在畢業多年,因為線上開放課程,再次接觸久違的線性代數時,留下這一段話:
<br />
<blockquote>
As someone who hadn't studied it for 20 years, it was invaluable to revisit the subject as an adult with the ability to reflect "ah, I can use that for... " instead of "Ok but when am I ever going to use this"</blockquote>
其實就是這個精神。化被動為主動, 役物而不役於物。<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-33184616052120608622015-01-28T19:54:00.000+08:002015-01-28T19:58:18.274+08:00馬力歐及樂高中正紀念堂的模型 <style>
.aspect-ratio {
position: relative;
width: 100%;
height: 0;
padding-bottom: 51%;
}
.aspect-ratio iframe {
position: absolute;
width: 100%;
height: 100%;
left: 0; top: 0;
}
</style>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAahNVOakSsis5hBFbh0kascLwdyFMWL9lBJkw6i-4JDrGuL4vjBHdSfscEe8AQO_g3335vUoc_gaFnPlOwUEijhAUw3BjmRt4SMbEYUp0vcIYdR682TgVW43TInezcPL9DS0m0fxI4F8/s1600/bde80e5b9e5e49d887b7e0ab144ea586.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAahNVOakSsis5hBFbh0kascLwdyFMWL9lBJkw6i-4JDrGuL4vjBHdSfscEe8AQO_g3335vUoc_gaFnPlOwUEijhAUw3BjmRt4SMbEYUp0vcIYdR682TgVW43TInezcPL9DS0m0fxI4F8/s1600/bde80e5b9e5e49d887b7e0ab144ea586.png" height="400" width="400" /></a></div>
<br />
今天帶小孩及跟爸媽看樂高展及逛了一下新光三越 A8 五樓的玩具店,順手用 123D Catch 掃描了幾個 3D 模型,然後下載成 obj 檔,匯入 blender 修剪以及簡化,最後再用 blend4web 匯出成 .html 檔,過程相當無腦。掃描的結果如下。<br />
<br />
<br />
<div class="aspect-ratio">
<iframe src="http://tjwei.github.io/cks.html" > </iframe>
</div>
<div class="aspect-ratio">
<iframe src="http://tjwei.github.io/mario.html"> </iframe>
</div>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-26929026414715396042014-12-31T11:44:00.000+08:002015-01-10T10:30:56.223+08:00別讓分數殺了你<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihR7pVFK2NET2zYslRf4u0LevnOR4plPcDf8PTzQ4QM_TPsSLFG-0QOFiBc2g8ZXHanrZoUmQrzG5XexGMMUS19CJTLhsE0hoogY67tNsvlydveV0meS9u7PR8pt0M_Pc68cgG4zx5J8A/s1600/37-1137575-506f7d4fd8ae82ef77e89479e2a8cbf9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihR7pVFK2NET2zYslRf4u0LevnOR4plPcDf8PTzQ4QM_TPsSLFG-0QOFiBc2g8ZXHanrZoUmQrzG5XexGMMUS19CJTLhsE0hoogY67tNsvlydveV0meS9u7PR8pt0M_Pc68cgG4zx5J8A/s1600/37-1137575-506f7d4fd8ae82ef77e89479e2a8cbf9.jpg" /></a></div>
<br />
先說個故事,關於一個充滿心機的老人的故事<br />
<blockquote class="tr_bq">
有個老人愛清靜,可附近常有小孩玩,吵得要命。 於是他把小孩召集過來, 說:「我這很冷清,謝謝你們讓這更熱鬧」
說完分給每人三顆糖孩子們很開心,之後就天天來玩。幾天後,每人只給2顆,再後來給1顆,最後就不給了。於是,孩子們生氣了,說:「以後再也不來這給你熱鬧了」 </blockquote>
很多人詬病考試領導教學,也有不少建議要如何改善考試的方式。 其中很多意見都不錯,雖然實際執行還有很多技術細節及現實上的問題(勿忘建構式數學),但整體來說,我對於整體教育的改善是樂觀的。<br />
不過,我想講一些更基本的東西。就像上面的故事一樣。<br />
上面的故事還挺有趣的,只不過,事實比虛構的故事更加有趣。<br />
<br />
70 年代的時候,心理學家 Lepper 和 Nisbett 就做過這樣的實驗。
他們在幼稚園(位於史丹佛大學的 Bing 托兒所)中找來一些本來就喜歡畫畫的四五歲小朋友,問他們願不願意畫一張畫給。接下來就是三組不同的地方了:
<br />
<br />
<ul>
<li>預期得獎組:事先告訴小朋友們,那個朋友帶了一些乖寶寶獎狀(上面有像下圖一樣,帶有權威認證的榮譽標誌),只要畫圖就能贏得獎狀。 </li>
<li>無獎品組:事先只問小朋友是否願意幫忙。最後只是謝謝他,不會給獎狀。 </li>
<li>意外得獎組:事先只問小朋友是否願意幫忙,但是畫完之後告訴他因為感謝他,所以給他乖寶寶獎狀。 </li>
</ul>
可能有人會擔心不同的問法會造成抽樣偏差, 不過其實大多數被問到的小朋友都答應了。<br />
每個小孩有六分鐘的時間畫圖,詳細的研究方式可以參考下面列的原始論文。<br />
結果呢?<br />
兩星期之後,觀察這些小朋友自由時間從事的活動,發現無獎品組以及意外得獎組還是一樣喜歡畫圖,但預期得獎組畫圖的時間卻只剩一半了。<br />
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj_O1_Z9nd6NSVdwPSsJBePC8vhIqKfw8kPedMFKEG9KIH4hy-2U_8RLGnlr9neMeroF_zCVQ6SCTHWYR-KW4CXaPTLNi_A2l_h_-p2S8YQfpVE-sq0A3a8HbwRUkhIEV-828m5KwN8XQ/s1600/gold_ribbon.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj_O1_Z9nd6NSVdwPSsJBePC8vhIqKfw8kPedMFKEG9KIH4hy-2U_8RLGnlr9neMeroF_zCVQ6SCTHWYR-KW4CXaPTLNi_A2l_h_-p2S8YQfpVE-sq0A3a8HbwRUkhIEV-828m5KwN8XQ/s1600/gold_ribbon.jpg" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div id="rdcTitle" style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">
<br /></div>
<div id="rdcTitle" style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">
Undermining children's intrinsic interest with extrinsic reward: A test of the "overjustification" hypothesis.</div>
<div id="rdcAuthors" style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">
Lepper, Mark R.; Greene, David; Nisbett, Richard E.</div>
<div id="rdcSource" style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">
Journal of Personality and Social Psychology, Vol 28(1), Oct 1973, 129-137. <a href="http://courses.umass.edu/psyc360/lepper%20greene%20nisbett.pdf">PDF</a></div>
<br />
仔細想想,短短的六分鐘,就足以造成這麼大的差別。<br />
你從小到大,有多少時間是處於類似的環境?<br />
會講「要考試了,怎麼還不唸書」這種話嗎?<br />
<br />
其實還有不少實驗( Daniel Pink 的 Drive 動機,單純的力量裡面列了不少),像知名的<a href="http://en.wikipedia.org/wiki/Candle_problem">蠟燭實驗</a>,不斷的印證了同樣的結果。不過對我來說,因為我還沒有真的仔細研究過這些論文,讓我相信這個結論的主因還是自身的經驗與觀察。<br />
<br />
想一想,你有多少次在考前準備考試的經驗? 據說考試是用來測量學習成效用的,那為什麼要在考前額外做一些事情來影響測量結果? 如果能在幾天一週加強準備,就能準備好的東西,為什麼要花一個學期來學?<br />
<br />
制度的存在,是為了解決 Scalability 的問題。 因為人(或者選項)太多,才需要評鑑來幫忙做。 制度可以改善,但再好的制度都無法完美,都是一種簡化的過程。最後一定會有比較,一定會有分數。<br />
而且,一定會有漏洞。<br />
一定有人會鑽漏洞。<br />
而一但有人開始鑽漏洞,就便成了軍備競賽了。<br />
你的評量標準是高度,就會有人揠苗助長。<br />
考試領導教學是無法避免的。但可以某種程度改善。<br />
讓鑽漏洞的難度變高,代價是 Scalability 就會下降,所以要投入更多資源。讓能鑽漏的人變少,所以要特別注意公平性的問題。<br />
隨著老師、社會整體觀念、素質的改變、提昇,進而引導制度越來越合理(才不會重蹈建構式數學的覆轍)。<br />
<br />
但有些問題是更本質性的,因為改革只能讓制度更合理、更合乎某種價值觀(不像現在的制度處處自我矛盾)。<br />
有人的地方就有江湖。<br />
個人能做的,就是意識到這件事情,不讓自己被殺掉。<br />
也許這樣講稍微誇張一點。比較持平的講法是,不讓自己的內在動機被殺掉。<br />
不過一個人的自我扣掉內在動機後,我很懷疑還剩下什麼。<br />
<br />
當然,老師們就處在一個更重要的位置了。<br />
<br />
但問題是,如果你自己平常就被評鑑、金錢、榮譽、績效追著跑(或者追著他們跑),你要怎麼教學生用平常心來對待分數?<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-75922457997181703582014-12-19T17:24:00.000+08:002014-12-19T17:24:15.779+08:00Leap motion 擴增實境<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO8QuKmhLerDpZRkJaDnc42u0_Ldve1jMXA2zGfE0nfaCqIrtLsxc1WzObcXCvL681Wozz3D7mLwXZbe7r06WmUVNMG1knQC_KQaEv8YIpd5UB3uEACEmv_34bSuFn_yqEGXzGc6v7RkI/s1600/10679860_977934825557230_3360354809841658203_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO8QuKmhLerDpZRkJaDnc42u0_Ldve1jMXA2zGfE0nfaCqIrtLsxc1WzObcXCvL681Wozz3D7mLwXZbe7r06WmUVNMG1knQC_KQaEv8YIpd5UB3uEACEmv_34bSuFn_yqEGXzGc6v7RkI/s1600/10679860_977934825557230_3360354809841658203_o.jpg" height="266" width="400" /></a></div>
<br />利用 OpenCV 校正攝影機以及 Leap Motion Sensor 的位置,再利用 Three.js 來達成擴增實境的效果。<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/2qUu4Wovt-g?feature=player_embedded' frameborder='0'></iframe></div>
<br />
這是試玩的結果(還是跟 IPython notebook 一起探索),所以並沒有以最佳效果為目標。實際上 leap motion sensor 可以放在比較好的位置。<br />
<br />
稍早拿來和 google hangout api 配合的效果。<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ZEZQjnoG2hw?feature=player_embedded' frameborder='0'></iframe></div>
<br />
原理上不難,只是中間要解決一堆技術上的小麻煩。<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-37216398120816750602014-11-30T16:48:00.001+08:002014-11-30T17:03:16.938+08:00用 Spynner 來抓 8Comic 的漫畫 (4): 多線程<style type="text/css">
.prompt {display: none;}
.highlight {color: #000000;}
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #7D9029 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0000FF } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
@media print {
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
.rendered_html h1 {color: #cc7; background-color: #000;}
.rendered_html h2 {color: #cc7; }
.rendered_html h3 {color: #cc7; }
</style>
<br />
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWnHl2CN2QYsA9oqraeH1dcvqP3jn9iGIDHAkhkXSQ8N5OfysR8UYxEVcV8D9KhRrhrAFgk0eFfwervfrMyb1o-GPuSttSCXRxCRyXZnLPxR8nJ8mZ5gr4QPwNSqEipIcA0USE9cmmz08/s1600/Screen+Shot+2014-11-30+at+4.30.25+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWnHl2CN2QYsA9oqraeH1dcvqP3jn9iGIDHAkhkXSQ8N5OfysR8UYxEVcV8D9KhRrhrAFgk0eFfwervfrMyb1o-GPuSttSCXRxCRyXZnLPxR8nJ8mZ5gr4QPwNSqEipIcA0USE9cmmz08/s1600/Screen+Shot+2014-11-30+at+4.30.25+PM.png" height="251" width="400" /></a></div>
<h2 id="平行化的好處">
</h2>
<h2 id="平行化的好處">
平行化的好處</h2>
之前抓檔案的方式是,<br />
<ol style="list-style-type: decimal;">
<li>用瀏覽器抓 .html,找到圖片 url。</li>
<li>下載圖片</li>
<li>換下一頁,跳到第一步。</li>
</ol>
但是網路可以同時開好幾個連線。所以我們要利用這點來加速。我們的策略是,<br />
<ol style="list-style-type: decimal;">
<li>用瀏覽器抓 .html,找到圖片 url。</li>
<li>把圖片 url 丟進一個 Thread Pool 裡面(平行下載,但是不等待)。</li>
<li>換下一頁,跳到第一步。</li>
</ol>
當然也有其他的方式平行化,比方連抓 .html 的工作也一併丟入 Thread Pool。不過這樣代表要多開好幾個 browser,而且相對來說,抓網頁會比抓圖片快,所以我們選擇上面的方式。</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="import-將會用到的-module">
import 將會用到的 module</h2>
因為使用 python 2.7, 我們利用 urllib2 來抓圖, ThreadPool 來 multi threading 平行處理。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="kn">import</span> <span class="nn">spynner</span>
<span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtWebKit</span> <span class="kn">import</span> <span class="n">QWebSettings</span> <span class="c"># 用來設定 QtWebKit</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtNetwork</span> <span class="kn">import</span> <span class="n">QNetworkAccessManager</span><span class="p">,</span> <span class="n">QNetworkRequest</span> <span class="c"># 控制 browser 的網路連線</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtCore</span> <span class="kn">import</span> <span class="n">QUrl</span> <span class="c"># Qt 的 Url 類別</span>
<span class="c"># 下面是新增的兩個 module</span>
<span class="kn">import</span> <span class="nn">urllib2</span>
<span class="kn">from</span> <span class="nn">multiprocessing.pool</span> <span class="kn">import</span> <span class="n">ThreadPool</span>
<span class="c"># 下面是 IPython 相關</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">display</span><span class="p">,</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">IPython.html.widgets</span> <span class="kn">import</span> <span class="n">ImageWidget</span><span class="p">,</span> <span class="n">IntProgressWidget</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="建立瀏覽器">
建立瀏覽器</h2>
這部份一樣</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 建立瀏覽器</span>
<span class="n">browser</span> <span class="o">=</span> <span class="n">spynner</span><span class="o">.</span><span class="n">Browser</span><span class="p">(</span><span class="n">debug_level</span><span class="o">=</span><span class="n">spynner</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">debug_stream</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="c"># 建立一個 webview</span>
<span class="n">browser</span><span class="o">.</span><span class="n">create_webview</span><span class="p">()</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">webview</span><span class="o">.</span><span class="n">settings</span><span class="p">()</span>
<span class="c"># settings.setAttribute(QWebSettings.AutoLoadImages, False)</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">JavaEnabled</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span> <span class="c"># 不需要 Java</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">DnsPrefetchEnabled</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span> <span class="c"># 試著節省 Dns 花的時間</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">PrivateBrowsingEnabled</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span> <span class="c"># 不需要瀏覽紀錄或者 cookie</span>
<span class="c"># 建立一個空的 url</span>
<span class="n">BLANK_REQUEST</span> <span class="o">=</span> <span class="n">QNetworkRequest</span><span class="p">(</span><span class="n">QUrl</span><span class="p">())</span>
<span class="c"># 建立一個空的圖片 url</span>
<span class="n">DUMMY_IMG_REQUEST</span> <span class="o">=</span> <span class="n">QNetworkRequest</span><span class="p">(</span><span class="n">QUrl</span><span class="p">(</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="</span><span class="p">))</span>
<span class="c"># 客製化的 NetworkAccessManager</span>
<span class="k">class</span> <span class="nc">EightComicNetworkAccessManager</span><span class="p">(</span><span class="n">QNetworkAccessManager</span><span class="p">):</span>
<span class="c"># 只需要取代 createRequest 這個 method 即可 </span>
<span class="k">def</span> <span class="nf">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="n">url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="p">()</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span> <span class="c"># 參數很多,但只取 url 就夠用 </span>
<span class="k">if</span> <span class="s">'comic'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">url</span><span class="p">[:</span><span class="mi">20</span><span class="p">]:</span>
<span class="c"># 用很醜的方式來判斷非 8comic 網站的 url </span>
<span class="c"># 用空的 url 取代原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">GetOperation</span><span class="p">,</span> <span class="n">BLANK_REQUEST</span><span class="p">)</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">url</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'js'</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">url</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'css'</span><span class="p">)</span> <span class="ow">and</span> <span class="s">'.html'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">url</span><span class="p">:</span>
<span class="c"># 凡是 .js .css .html 之外的,都用空的圖片 url 取代原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">GetOperation</span><span class="p">,</span> <span class="n">DUMMY_IMG_REQUEST</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c"># 傳回原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">device</span><span class="p">)</span>
<span class="c"># 設定 browser 的 NetworkAccessManager</span>
<span class="n">browser</span><span class="o">.</span><span class="n">webpage</span><span class="o">.</span><span class="n">setNetworkAccessManager</span><span class="p">(</span><span class="n">EightComicNetworkAccessManager</span><span class="p">())</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="設定-widget">
設定 Widget</h2>
增加一個 Progress Bar, 分別來顯示分析過的 .html 數字以及已經下載的圖片數</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">browser</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
<span class="c"># 漫畫的網頁</span>
<span class="n">base_url</span> <span class="o">=</span> <span class="s">'http://new.comicvip.com/show/cool-5614.html?ch='</span>
<span class="c"># 要下載第一本</span>
<span class="n">book_no</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c"># 取得總頁數</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">base_url</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">book_no</span><span class="p">))</span>
<span class="n">total_pages</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'ps'</span><span class="p">)</span><span class="o">.</span><span class="n">toInt</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="c"># 建立 Image Widget 用來顯示圖片預覽</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">ImageWidget</span><span class="p">()</span>
<span class="n">img</span><span class="o">.</span><span class="n">set_css</span><span class="p">(</span><span class="s">"height"</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> <span class="c"># 讓圖片不要太大</span>
<span class="c"># 顯示下載進度的 Progress bar</span>
<span class="n">html_progress</span> <span class="o">=</span> <span class="n">IntProgressWidget</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="n">total_pages</span><span class="p">)</span>
<span class="n">img_progress</span> <span class="o">=</span> <span class="n">IntProgressWidget</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="n">total_pages</span><span class="p">)</span>
<span class="c"># 顯示 Widget</span>
<span class="n">display</span><span class="p">(</span><span class="n">html_progress</span><span class="p">)</span>
<span class="n">display</span><span class="p">(</span><span class="n">img_progress</span><span class="p">)</span>
<span class="n">display</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="利用-threadpool-來下載">
利用 ThreadPool 來下載</h2>
另用 ThreadPool 來達成 multithreading (多線程) 即為容易,只要將想丟進 pool 的程式碼包進函數裡面即可。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 建立一個下載目錄</span>
<span class="n">dir_name</span> <span class="o">=</span> <span class="s">"download/{:02d}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">book_no</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">dir_name</span><span class="p">):</span>
<span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">dir_name</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Download to {}/{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">(),</span> <span class="n">dir_name</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="c"># 建立 ThreadPool, 5 條 thread</span>
<span class="n">pool</span> <span class="o">=</span> <span class="n">ThreadPool</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="c"># 開始下載</span>
<span class="n">downloaded_images</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total_pages</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
<span class="c"># 取得 image url</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"{}{}-{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">base_url</span><span class="p">,</span> <span class="n">book_no</span><span class="p">,</span> <span class="n">page</span><span class="p">))</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'document.getElementById("TheImg").getAttribute("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="c"># 將下載圖片的工作包成 save_img,推進 pool 裡</span>
<span class="k">def</span> <span class="nf">save_img</span><span class="p">(</span><span class="n">img_url</span><span class="p">,</span> <span class="n">page</span><span class="p">):</span>
<span class="k">global</span> <span class="n">downloaded_images</span>
<span class="n">fn</span> <span class="o">=</span> <span class="s">"{}/{:03d}.jpg"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">dir_name</span><span class="p">,</span> <span class="n">page</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">img_url</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c"># 更新 widget 的狀態</span>
<span class="n">downloaded_images</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">img_progress</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="s">"img: </span><span class="si">%d</span><span class="s">/</span><span class="si">%d</span><span class="s">"</span><span class="o">%</span><span class="p">(</span><span class="n">downloaded_images</span><span class="p">,</span> <span class="n">total_pages</span><span class="p">)</span>
<span class="n">img_progress</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">downloaded_images</span>
<span class="n">img</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">Image</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">fn</span><span class="p">)</span><span class="o">.</span><span class="n">data</span>
<span class="n">pool</span><span class="o">.</span><span class="n">apply_async</span><span class="p">(</span><span class="n">save_img</span><span class="p">,</span> <span class="p">(</span><span class="n">img_url</span><span class="p">,</span> <span class="n">page</span><span class="p">))</span>
<span class="c"># 更新 Widget 的狀態</span>
<span class="n">html_progress</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="s">"html: </span><span class="si">%d</span><span class="s">/</span><span class="si">%d</span><span class="s">"</span><span class="o">%</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">total_pages</span><span class="p">)</span>
<span class="n">html_progress</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">page</span>
<span class="c"># 等待所有任務結束</span>
<span class="n">pool</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">pool</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="結果">
結果</h2>
到這裡,探索期正式結束,該有的技術已經完整。<br />
有興趣的話,也可以實測比較一下有 multithreading 和沒有 multithreading 的差異。但這裡就發現我們需要封裝了,因為沒有封裝、整理好的關係,要測試兩種程式碼,必須要把程式碼寫兩遍,而無法共用相同的部份。<br />
另外,程式碼裡面用到了 global 這個 keyword, 常常也代表我們需要封裝了。</div>
</div>
</div>
<br />
<hr />
<br />
<ul>
<li><a href="http://weijr-note.blogspot.tw/2014/11/blog-post.html"> 用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術 </a></li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-2.html">用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-3.html">用 Spynner 來抓 8Comic 的漫畫 (3): 節省頻寬</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-4.html">用 Spynner 來抓 8Comic 的漫畫 (4): 多線程</a>
</li>
</ul>tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-38775633141931955462014-11-30T14:38:00.001+08:002014-11-30T17:02:52.112+08:00用 Spynner 來抓 8Comic 的漫畫 (3): 節省頻寬<style type="text/css">
.prompt {display: none;}
.highlight {color: #000000;}
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #7D9029 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0000FF } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
@media print {
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
.rendered_html h1 {color: #cc7; background-color: #000;}
.rendered_html h2 {color: #cc7; }
.rendered_html h3 {color: #cc7; }
</style>
<br />
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="要解決的問題">
要解決的問題</h2>
<ul>
<li>還是有文字廣告這樣不需要的流量來浪費頻寬和時間。</li>
<li>QtWebKit 的 QWebSettings.AutoLoadImages=false 有 bug,記憶體已用不還。</li>
</ul>
<h2 id="解法">
解法</h2>
如同這裡 http://stackoverflow.com/questions/21357157/is-there-any-solution-for-the-qtwebkit-memory-leak 以及相關的連結、討論,記憶體的 bug 目前找得到的解法就是定期砍掉 process 重新再開。<br />
理論上,也可以深入 QtWebKit, 找到配置的記憶空間,然後手動釋放,順便將解法回給上游。這樣雖然是正解,但是與我們的主題無關。<br />
既然無解,那要等 QtWebKit 修正 bug 之後再來抓? 太久。<br />
定期砍 Process 重開? 可以,但太醜。<br />
不用 QtWebKit, 改用其他套件來解? 可以,但其他套件可能有其他問題,人生不能一直逃避,會養成習慣的。<br />
仔細思考,其實我們的目標是要讓 browser 不去抓不必要的網路資源,這個原則包含了圖片以及文字廣告,但不僅限於這兩個,還有像是 google 統計等等。<br />
所以只要我們封鎖這些不必要的連線,看起來瀏覽器的圖片就不會被顯示了,不需要真的設定 QWebSettings.AutoLoadImages=false<br />
比方可以設定 proxy,由 proxy 來控制網路資訊流。不過 QtWebKit 有幾個內建的功能,可以讓你控制網路的存取,比 proxy 更裡層一點。一個是 browser.webpage 的 acceptNavigationRequest, loadStarted, loadFinished. 這幾個搭配起來,你可以限制 browser 不抓取子 iframe。 但這個方法對我們來說,不夠用。我們想要控制更多,所以我們用另外一種方式,直接控制比較外層一點的 QNetworkAccessManager 。</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="一樣-import-所有我們將會用到的東西">
一樣 import 所有我們將會用到的東西</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="kn">import</span> <span class="nn">spynner</span>
<span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtWebKit</span> <span class="kn">import</span> <span class="n">QWebSettings</span> <span class="c"># 用來設定 QtWebKit</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtNetwork</span> <span class="kn">import</span> <span class="n">QNetworkAccessManager</span><span class="p">,</span> <span class="n">QNetworkRequest</span> <span class="c"># 控制 browser 的網路連線</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtCore</span> <span class="kn">import</span> <span class="n">QUrl</span> <span class="c"># Qt 的 Url 類別</span>
<span class="c"># 下面這行是 IPython 相關</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">display</span><span class="p">,</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">IPython.html.widgets</span> <span class="kn">import</span> <span class="n">ImageWidget</span><span class="p">,</span> <span class="n">IntProgressWidget</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="建立瀏覽器">
建立瀏覽器</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 建立瀏覽器</span>
<span class="n">browser</span> <span class="o">=</span> <span class="n">spynner</span><span class="o">.</span><span class="n">Browser</span><span class="p">(</span><span class="n">debug_level</span><span class="o">=</span><span class="n">spynner</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">debug_stream</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="c"># 建立一個 webview</span>
<span class="c"># 我們不設定 AutoLoadImages=False, 但增加一些其他設定</span>
<span class="c"># 這裡並不是重點,但適合我們的應用</span>
<span class="n">browser</span><span class="o">.</span><span class="n">create_webview</span><span class="p">()</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">webview</span><span class="o">.</span><span class="n">settings</span><span class="p">()</span>
<span class="c"># settings.setAttribute(QWebSettings.AutoLoadImages, False)</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">JavaEnabled</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span> <span class="c"># 不需要 Java</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">DnsPrefetchEnabled</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span> <span class="c"># 試著節省 Dns 花的時間</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">PrivateBrowsingEnabled</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span> <span class="c"># 不需要瀏覽紀錄</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="建立一個-qnetworkaccessmanager-子類別">
建立一個 QNetworkAccessManager 子類別</h2>
當 browser.webpage 在要求網路資源前,會先詢問 QNetworkAccessManager 來確定要不要抓,或者怎麼來抓這個資源。 我們可以用 browser.webpage.setNetworkAccessManager 來指定自己客製過的 manager。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 建立一個空的 url</span>
<span class="n">BLANK_REQUEST</span> <span class="o">=</span> <span class="n">QNetworkRequest</span><span class="p">(</span><span class="n">QUrl</span><span class="p">())</span>
<span class="c"># 建立一個空的圖片 url</span>
<span class="n">DUMMY_IMG_REQUEST</span> <span class="o">=</span> <span class="n">QNetworkRequest</span><span class="p">(</span><span class="n">QUrl</span><span class="p">(</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="</span><span class="p">))</span>
<span class="c"># 因為只需要用一次,可以取個又臭又長的名字</span>
<span class="k">class</span> <span class="nc">EightComicNetworkAccessManager</span><span class="p">(</span><span class="n">QNetworkAccessManager</span><span class="p">):</span>
<span class="c"># 只需要取代 createRequest 這個 method 即可 </span>
<span class="k">def</span> <span class="nf">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="n">url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="p">()</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span> <span class="c"># 參數很多,但只取 url 就夠用 </span>
<span class="k">if</span> <span class="s">'comic'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">url</span><span class="p">[:</span><span class="mi">20</span><span class="p">]</span><span class="p">:</span>
<span class="c"># 用很醜的方式來判斷非 8comic 網站的 url </span>
<span class="c"># 用空的 url 取代原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">GetOperation</span><span class="p">,</span> <span class="n">BLANK_REQUEST</span><span class="p">)</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">url</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'js'</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">url</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'css'</span><span class="p">)</span> <span class="ow">and</span> <span class="s">'.html'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">url</span><span class="p">:</span>
<span class="c"># 凡是 .js .css .html 之外的,都用空的圖片 url 取代原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">GetOperation</span><span class="p">,</span> <span class="n">DUMMY_IMG_REQUEST</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c"># 傳回原本的 url</span>
<span class="k">return</span> <span class="n">QNetworkAccessManager</span><span class="o">.</span><span class="n">createRequest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">device</span><span class="p">)</span>
<span class="c"># 設定 browser 的 NetworkAccessManager</span>
<span class="n">browser</span><span class="o">.</span><span class="n">webpage</span><span class="o">.</span><span class="n">setNetworkAccessManager</span><span class="p">(</span><span class="n">EightComicNetworkAccessManager</span><span class="p">())</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="後面的程式碼都一樣">
後面的程式碼都一樣</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 漫畫的網頁</span>
<span class="n">base_url</span> <span class="o">=</span> <span class="s">'http://new.comicvip.com/show/cool-5614.html?ch='</span>
<span class="c"># 顯示瀏覽器,確認 browser 內容乾淨清爽</span>
<span class="n">browser</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
<span class="c"># 要下載第一本</span>
<span class="n">book_no</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c"># 取得總頁數</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">base_url</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">book_no</span><span class="p">))</span>
<span class="n">total_pages</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'ps'</span><span class="p">)</span><span class="o">.</span><span class="n">toInt</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="c"># 建立 Image Widget 用來顯示圖片預覽</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">ImageWidget</span><span class="p">()</span>
<span class="n">img</span><span class="o">.</span><span class="n">set_css</span><span class="p">(</span><span class="s">"height"</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> <span class="c"># 讓圖片不要太大</span>
<span class="c"># 顯示下載進度的 Progress bar</span>
<span class="n">progress</span> <span class="o">=</span> <span class="n">IntProgressWidget</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="n">total_pages</span><span class="p">)</span>
<span class="c"># 顯示 Widget</span>
<span class="n">display</span><span class="p">(</span><span class="n">progress</span><span class="p">)</span>
<span class="n">display</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="c"># 建立一個下載目錄</span>
<span class="n">dir_name</span> <span class="o">=</span> <span class="s">"download/{:02d}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">book_no</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">dir_name</span><span class="p">):</span>
<span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">dir_name</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Download to {}/{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">(),</span> <span class="n">dir_name</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="c"># 開始下載</span>
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total_pages</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
<span class="c"># 取得 image url</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"{}{}-{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">base_url</span><span class="p">,</span> <span class="n">book_no</span><span class="p">,</span> <span class="n">page</span><span class="p">))</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'document.getElementById("TheImg").getAttribute("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="c"># 下載圖片</span>
<span class="n">fn</span> <span class="o">=</span> <span class="s">"{}/{:03d}.jpg"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">dir_name</span><span class="p">,</span> <span class="n">page</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">browser</span><span class="o">.</span><span class="n">download</span><span class="p">(</span><span class="n">img_url</span><span class="p">,</span> <span class="n">outfd</span><span class="o">=</span><span class="n">f</span><span class="p">)</span>
<span class="c"># 更新 Widget 的狀態</span>
<span class="n">progress</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="s">"</span><span class="si">%d</span><span class="s">/</span><span class="si">%d</span><span class="s">"</span><span class="o">%</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">total_pages</span><span class="p">)</span>
<span class="n">progress</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">page</span>
<span class="n">img</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">Image</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">fn</span><span class="p">)</span><span class="o">.</span><span class="n">data</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="結果">
結果</h2>
和第一篇的結果比較, <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT9M5OjJNSBV1rh8r9fK7GyVtT6lBq8Zca880ABkpT9RglYyeY6SVsCoI0XxH2HLJLD1ExvqkafTput8zEgLQxXAb8UAcVS7suJiSNA_sPfL2KgEMyULR28YKN0Njc4Uu9pyitYy8iA24/s1600/2014-11-30+13:34:19+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT9M5OjJNSBV1rh8r9fK7GyVtT6lBq8Zca880ABkpT9RglYyeY6SVsCoI0XxH2HLJLD1ExvqkafTput8zEgLQxXAb8UAcVS7suJiSNA_sPfL2KgEMyULR28YKN0Njc4Uu9pyitYy8iA24/s1600/2014-11-30+13:34:19+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" height="363" width="640" /></a></div>
的確清爽很多,廣告和圖片都消失了。速度也快了很多。其實 .css 檔案也可以不用抓,不過因為有 cache 的緣故, .css 和 .js 本來都只會抓一次,所以影響有限。<br />
因為這一部分的探索已經告一段落,所以現在是封裝的時候。不過因為之後我們要介紹兩種不同的封裝方式,所以在這之前,我們要再做一件事情,那就是更加節省一點。<br />
看起來不是我們不是已經夠節儉了嗎?只抓有需要的東西,沒有多抓任何東西。 也許 .html 也可以不用抓?沒錯,也許可以直接反推出每一頁漫畫圖片的 url,但即使這個例子可以,但一般來說,伺服器端完全可以將必要的資訊放在 .html 裡面,讓你必須要抓 .html 才能獲得必要資訊。 這系列的目的是介紹一個簡單的萬用抓資料方式,所以做到這裡就可以了。<br />
那還有什麼可以節省的? 頻寬就這樣了,但是時間還可以節省。 下一篇介紹簡單的 multithreading 抓圖。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
</div>
</div>
<br />
<hr />
<br />
<ul>
<li><a href="http://weijr-note.blogspot.tw/2014/11/blog-post.html"> 用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術 </a></li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-2.html">用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-3.html">用 Spynner 來抓 8Comic 的漫畫 (3): 節省頻寬</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-4.html">用 Spynner 來抓 8Comic 的漫畫 (4): 多線程</a>
</li>
</ul>tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-23116874291586272772014-11-29T22:42:00.000+08:002014-11-30T17:02:23.127+08:00用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面<style type="text/css">
.prompt {display: none;}
.highlight {color: #000000;}
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #7D9029 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0000FF } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
@media print {
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
.rendered_html h1 {color: #cc7; background-color: #000;}
.rendered_html h2 {color: #cc7; }
.rendered_html h3 {color: #cc7; }
</style>
<br />
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-utRYte_-EGCUcXaUayD8UyhpF0zL4wMM1BXEVrau6ymQMhimXjJxiD4LamM9wQvPZ5Wefz2aZu_BprLUDphhtqU8z5SBdGoYKBOgZfGp-nfI8qYO0dDT7of7HyVeR7s8w_EIgDn3yA0/s1600/Screen+Shot+2014-11-29+at+10.08.58+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-utRYte_-EGCUcXaUayD8UyhpF0zL4wMM1BXEVrau6ymQMhimXjJxiD4LamM9wQvPZ5Wefz2aZu_BprLUDphhtqU8z5SBdGoYKBOgZfGp-nfI8qYO0dDT7of7HyVeR7s8w_EIgDn3yA0/s1600/Screen+Shot+2014-11-29+at+10.08.58+PM.png" height="327" width="400" /></a></div>
<h2 id="已經可以抓了還有什麼問題">
</h2>
<h2 id="已經可以抓了還有什麼問題">
已經可以抓了,還有什麼問題?</h2>
<ul>
<li>browser 瀏覽頁面時,已經顯示圖了。之後,又再 download 一次,浪費頻寬。</li>
<li>介面不夠美觀,無法看到進度。</li>
</ul>
<h3 id="頻寬問題">
頻寬問題</h3>
概念上,有兩個方向。 一是既然 browser 顯示了圖片,表示 browser 有這份圖,我們跟 browser 要就好了。另一個剛好相反,告訴瀏覽器,不要顯示圖片,把圖片的 url 交給我們即可。<br />
這兩個方向各有利弊,以現在這個應用來說,我選擇第二個。原因有三:<br />
<br />
<ul>
<li> QtWebKit 有選項讓你這樣做。 </li>
<li>這樣可行。 browser 仍然會傳回正確的圖片 url。 </li>
<li>可以順便擋住廣告圖片。</li>
</ul>
<br />
<h3 id="介面問題">
介面問題</h3>
因為我們用使用 IPython notebook,所以使用 IPython notebook 的 interactive widget。</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="接下來一樣先-import-所有我們將會用到的東西">
接下來,一樣先 import 所有我們將會用到的東西</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="kn">import</span> <span class="nn">spynner</span>
<span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">PyQt4.QtWebKit</span> <span class="kn">import</span> <span class="n">QWebSettings</span> <span class="c"># 用來設定 QtWebKit</span><span class="c">
</span></pre>
<pre><span class="c"># 下面是 IPython 相關</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">display</span><span class="p">,</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">IPython.html.widgets</span> <span class="kn">import</span> <span class="n">ImageWidget</span><span class="p">,</span> <span class="n">IntProgressWidget</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="再來是建立瀏覽器並且設定不要載入圖片">
再來是建立瀏覽器,並且設定不要載入圖片</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">base_url</span> <span class="o">=</span> <span class="s">'http://new.comicvip.com/show/cool-5614.html?ch='</span>
<span class="c"># 建立瀏覽器</span>
<span class="n">browser</span> <span class="o">=</span> <span class="n">spynner</span><span class="o">.</span><span class="n">Browser</span><span class="p">(</span><span class="n">debug_level</span><span class="o">=</span><span class="n">spynner</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">debug_stream</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="c"># 建立一個 webview,並且設定不要自動載入圖片</span>
<span class="n">browser</span><span class="o">.</span><span class="n">create_webview</span><span class="p">()</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">webview</span><span class="o">.</span><span class="n">settings</span><span class="p">()</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">QWebSettings</span><span class="o">.</span><span class="n">AutoLoadImages</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>
<span class="c"># 要下載第一本</span>
<span class="n">book_no</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c"># 取得總頁數</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">base_url</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">book_no</span><span class="p">))</span>
<span class="n">total_pages</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'ps'</span><span class="p">)</span><span class="o">.</span><span class="n">toInt</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="再來是建立-interactive-widget">
再來是建立 Interactive Widget</h2>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 建立 Image Widget 用來顯示圖片預覽</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">ImageWidget</span><span class="p">()</span>
<span class="n">img</span><span class="o">.</span><span class="n">set_css</span><span class="p">(</span><span class="s">"height"</span><span class="p">,</span> <span class="mi">300</span><span class="p">)</span> <span class="c"># 讓圖片不要太大</span>
<span class="c"># 顯示下載進度的 Progress bar</span>
<span class="n">progress</span> <span class="o">=</span> <span class="n">IntProgressWidget</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="n">total_pages</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="下載">
下載</h2>
跟之前一樣,不過外加進度顯示</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 顯示之前建立的 Widget</span>
<span class="n">display</span><span class="p">(</span><span class="n">progress</span><span class="p">)</span>
<span class="n">display</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="c"># 建立一個下載目錄</span>
<span class="n">dir_name</span> <span class="o">=</span> <span class="s">"download/{:02d}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">book_no</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">dir_name</span><span class="p">):</span>
<span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">dir_name</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Download to {}/{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">(),</span> <span class="n">dir_name</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="c"># 開始下載</span>
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total_pages</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
<span class="c"># 取得 image url</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"{}{}-{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">base_url</span><span class="p">,</span> <span class="n">book_no</span><span class="p">,</span> <span class="n">page</span><span class="p">))</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'document.getElementById("TheImg").getAttribute("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="c"># 下載圖片</span>
<span class="n">fn</span> <span class="o">=</span> <span class="s">"{}/{:03d}.jpg"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">dir_name</span><span class="p">,</span> <span class="n">page</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">browser</span><span class="o">.</span><span class="n">download</span><span class="p">(</span><span class="n">img_url</span><span class="p">,</span> <span class="n">outfd</span><span class="o">=</span><span class="n">f</span><span class="p">)</span>
<span class="c"># 更新 Widget 的狀態</span>
<span class="n">progress</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="s">"</span><span class="si">%d</span><span class="s">/</span><span class="si">%d</span><span class="s">"</span><span class="o">%</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">total_pages</span><span class="p">)</span>
<span class="n">progress</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">page</span>
<span class="n">img</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">Image</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">fn</span><span class="p">)</span><span class="o">.</span><span class="n">data</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="看起來還不錯">
看起來還不錯?</h2>
這樣似乎看起來還不錯,沒有幾行程式碼,邊抓還能邊看 preview。 盯著朝著目標奔跑的 progress bar,有種莫名的療癒效果。而且沒有重複抓圖,省了不少頻寬。似乎探索期已經結束,接下來只要把這些程式碼封裝起來,方便重複使用就行了。<br />
但仔細一看,還是有不少問題:<br />
<br />
<ul>
<li> 如果用 browser.show() 打開瀏覽器,會發現還是有許多廣告被載入。我們希望這些廣告也不見。 </li>
<li>用了一段時間之後,發現機器越來越慢,仔細一看,記憶體使用量大得驚人。相當詭異。 </li>
</ul>
第一個問題的成因很單純,有些廣告是文字廣告。第二個問題就比較棘手一點了, QWebSettings.AutoLoadImages 有 bug,會造成記憶體無法回收的問題: <a href="http://stackoverflow.com/questions/21357157/is-there-any-solution-for-the-qtwebkit-memory-leak">http://stackoverflow.com/questions/21357157/is-there-any-solution-for-the-qtwebkit-memory-leak</a> 仔細查看討論之後,會發現這個問題目前基本無解,要等 Qt 解決。(不然就只能很醜的 fork,然後 close process。<br />
那怎麼辦? 放棄這個方案? 碰到無解的問題,如果是駭客(hacker),要深入系統,修正 bug,釋放記憶體。身為自造者(maker),那要捨棄壞掉的系統,自幹一個。<br />
但不過是抓個漫畫,這樣搞,未免太累了點。下一篇,我們要用廢客(faker)的方式來解決問題。那就是,假裝解決問題就行了。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
</div>
</div>
<br />
<hr />
<br />
<ul>
<li><a href="http://weijr-note.blogspot.tw/2014/11/blog-post.html"> 用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術 </a></li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-2.html">用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-3.html">用 Spynner 來抓 8Comic 的漫畫 (3): 節省頻寬</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-4.html">用 Spynner 來抓 8Comic 的漫畫 (4): 多線程</a>
</li>
</ul>tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-88399425861706968872014-11-24T12:07:00.001+08:002014-11-30T17:13:45.612+08:00用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術<style type="text/css">
.prompt {display: none;}
.highlight {color: #000000;}
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #7D9029 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.highlight .no { color: #880000 } /* Name.Constant */
.highlight .nd { color: #AA22FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0000FF } /* Name.Function */
.highlight .nl { color: #A0A000 } /* Name.Label */
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #666666 } /* Literal.Number.Float */
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
@media print {
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
.rendered_html h1 {color: #cc7; background-color: #000;}
.rendered_html h2 {color: #cc7; }
.rendered_html h3 {color: #cc7; }
</style>
<br />
<div class="border-box-sizing" id="notebook" tabindex="-1">
<div class="container" id="notebook-container">
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h1 id="-python-8comic-">
</h1>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiujzzfUkRCKvw844sew3GlHMNRDK5DHDxOjQ7adCYURwO1KsfMlX6UI4CSMWIwcvBqtLSzSQI4ADkS-lEiN9F8EuKQZLj-SfcWHy8a23cXcApc7xH8UgCNOqyNsJxj81F4FSIjn2QOVtI/s1600/2014-11-24+10:38:34+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiujzzfUkRCKvw844sew3GlHMNRDK5DHDxOjQ7adCYURwO1KsfMlX6UI4CSMWIwcvBqtLSzSQI4ADkS-lEiN9F8EuKQZLj-SfcWHy8a23cXcApc7xH8UgCNOqyNsJxj81F4FSIjn2QOVtI/s1600/2014-11-24+10:38:34+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" height="220" width="320" /></a></div>
<br />
<h2 id="-">
需要套件</h2>
<ul>
<li>Spynner (需要 PyQt4 或 PySide, autopy)</li>
<li>IPython notebook (因為這個範例是用 IPython notebook 示範,不然跳過 IPython 相關部份也行)
安裝
先安裝 Python, IPython notebook, PyQT4</li>
<li><ul>
</ul>
</li>
</ul>
<h3 id="-windows-">
在 mac 下(如果 autopy 安裝不起來):</h3>
<ul>
<li>先安裝 Qt。 brew 的話, brew install qt 即可。</li>
<li>安裝 PyQt 。 brew, pip 都行。</li>
<li><code>easy_install -N spynner</code></li>
<li>在適當的地方, touch autopy.py,如 touch /usr/local/lib/python2.7/site-packages/autopy.py。假裝有 autopy 就行了,因為 autopy 其實用不到。</li>
</ul>
<ul>
</ul>
<h3 id="-windows-">
在 windows 下:</h3>
<ul>
<li>安裝 python(x,y) 2.7</li>
<li>接下來打開 IPython 然後輸入 <code>!easy_install spynner</code>(win8 可用搜尋找到 IPython)</li>
<li>最後,打開 IPython notebook,按下 New notebook 開始。</li>
</ul>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># This is for windows </span>
<span class="c"># on linux, simply sudo easy_install spynner in command line</span>
<span class="o">!</span>easy_install spynner
<span class="c"># restart the kernel</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="-import-">
先 import 所有我們將會用到的東西</h3>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="kn">import</span> <span class="nn">spynner</span>
<span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span>
<span class="c"># 下面這行是 IPython 相關</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">display</span><span class="p">,</span> <span class="n">Image</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="-import-">
再來我們試試看建立瀏覽器</h3>
</div>
<div class="input_area">
<div class="highlight">
<pre><span class="n">browser</span> <span class="o">=</span> <span class="n">spynner</span><span class="o">.</span><span class="n">Browser</span><span class="p">(</span><span class="n">debug_level</span><span class="o">=</span><span class="n">spynner</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">debug_stream</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
如果看起來什麼事情都沒發生,那大概就對了。 spynner 已經在背景建立了一個 webkit 瀏覽器(叫做 browser)。<br />
通常我們不需要 browser 真的被顯示出來,不過為了方便了解發生了什麼事情,我們先讓它能夠被顯示。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">browser</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> <span class="c"># 告訴 browser,要它之後不要隱身</span>
<span class="c"># 為了避免法律上的疑慮,這裡你要自己找到適當的 url,把 ???? 換掉</span>
<span class="n">base_url</span> <span class="o">=</span> <span class="s">'http://???.com/show/????-????.html?ch='</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span> <span class="n">base_url</span><span class="o">+</span><span class="s">'1'</span><span class="p">)</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
這時候,成功的話,一個瀏覽器會跳出來,顯示漫畫第 1 話的封面。<br />
瀏覽器能夠改變大小,但是看來像是當掉一樣,沒有回應。<br />
這其實是好事,因為我們希望能夠完全控制瀏覽器,所以先凍結它,再慢慢來蹂躪它。<br />
接下來,我們要把封面圖的 url 抓出來。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">browser</span><span class="o">.</span><span class="n">load_jquery</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span> <span class="c"># spynner 內建有 jquery,用這個 method 載入,比較方便。</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'$("#TheImg").attr("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="k">print</span> <span class="n">img_url</span>
<span class="c"># 當然不用 jquery 也可以</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'document.getElementById("TheImg").getAttribute("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="k">print</span> <span class="n">img_url</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
上面先用 runjs 跑 javascript 得到一個結果。 <br />
這個結果是一個 Qt (C++)物件,可能是數字、字串或者物件。因為我們知道我們要的是字串,所以用 .toString 讓他成為一個 Qt 字串。<br />
最後,再用 str 轉成 Python 字串。</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="-">
抓圖</h2>
知道了圖片的 url, 那要如何將圖片抓下來呢?<br />
可以用 browser.download(img_url, outfd=fd) 直接下載到檔案裏面。<br />
不過這裡先直接在 IPython notebook 裡面秀一下圖片。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 直接顯示 url 看看</span>
<span class="n">display</span><span class="p">(</span><span class="n">Image</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="n">img_url</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">200</span><span class="p">))</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="c"># 先用 browser 抓下圖檔內容, 然後顯示</span>
<span class="n">display</span><span class="p">(</span><span class="n">Image</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">browser</span><span class="o">.</span><span class="n">download</span><span class="p">(</span><span class="n">img_url</span><span class="p">),</span> <span class="n">width</span><span class="o">=</span><span class="mi">200</span><span class="p">))</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
漫畫每一頁的 url 格式是 .......ch=M-N 其中 M, N 是數字, 分別是卷數及頁數, 所以現在我們只要知道有幾頁就行了。<br />
一般來說,可以從 html 內容中找到資訊。 8comic 控制 UI 的 javascript 就有這個資訊了,我們直接利用。<br />
一樣先用 runjs 得到 ps 這個 javascript 變數的內容, 然後轉成整數。 <br />
因為 toInt 的結果包含一些額外資訊,所以我們用 [0] 取出數字。</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">total_pages</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'ps'</span><span class="p">)</span><span class="o">.</span><span class="n">toInt</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span> <span class="n">total_pages</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
所以我們用一個迴圈把每一頁都抓下來吧</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In []:
</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight">
<pre><span class="n">book_no</span> <span class="o">=</span> 1
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total_pages</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
<span class="n">browser</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"{}{}-{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">base_url</span><span class="p">,</span> <span class="n">book_no</span><span class="p">,</span> <span class="n">page</span><span class="p">))</span>
<span class="n">img_url</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">browser</span><span class="o">.</span><span class="n">runjs</span><span class="p">(</span><span class="s">'document.getElementById("TheImg").getAttribute("src")'</span><span class="p">)</span><span class="o">.</span><span class="n">toString</span><span class="p">())</span>
<span class="k">print</span> <span class="n">page</span><span class="p">,</span> <span class="n">img_url</span>
<span class="n">display</span><span class="p">(</span><span class="n">Image</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="n">img_url</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">100</span><span class="p">))</span>
<span class="k">continue</span>
<span class="c"># 上面只是顯示每一頁的圖片</span>
<span class="c"># 如果你現在就想真的抓檔案下來, 把上面那個 continue 註解掉</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"{}-{}.jpg"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">book_no</span><span class="p">,</span> <span class="n">page</span><span class="p">),</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">browser</span><span class="o">.</span><span class="n">download</span><span class="p">(</span><span class="n">img_url</span><span class="p">,</span> <span class="n">outfd</span><span class="o">=</span><span class="n">f</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"File saved in"</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">()</span>
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<br />
到這裡為止,基本的功能已經有了,下一篇將會討論一些細節問題。<br />
<br />
<br />
題外話。<br />
寫這篇一部份是因為「如何用 Python 抓網站內容」的詢問度一直很高,另一部分是因為發現 JComicDownloader 無法抓 8Comic 的圖。我看了一下,覺得這是一個不錯的例子。<br />
<br />
<br />
<hr />
<br />
<ul>
<li><a href="http://weijr-note.blogspot.tw/2014/11/blog-post.html"> 用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術 </a></li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-2.html">用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-3.html">用 Spynner 來抓 8Comic 的漫畫 (3): 節省頻寬</a> </li>
<li><a href="http://weijr-note.blogspot.tw/2014/11/spynner-8comic-4.html">用 Spynner 來抓 8Comic 的漫畫 (4): 多線程</a>
</li>
</ul>
tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-9605886547511571602014-11-21T12:18:00.002+08:002014-11-21T12:18:48.942+08:00地堡系列、14 號門、起點人系列、黑塔系列、別相信任何人、控制、記憶傳承人<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzf1uCLuqoo5Njuf_tYn5WCycExzY8LuAPVnjG8NdwXZW2TkJgPKrLMLKej3rkvUD-pF1UnNVt1e2PcuI0gS6SOEbfuPvIcddmz8mc-_ugEM63k0-w4dTQFu2mFx6obbr88D6eg43aFHg/s1600/Gone+Gril.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzf1uCLuqoo5Njuf_tYn5WCycExzY8LuAPVnjG8NdwXZW2TkJgPKrLMLKej3rkvUD-pF1UnNVt1e2PcuI0gS6SOEbfuPvIcddmz8mc-_ugEM63k0-w4dTQFu2mFx6obbr88D6eg43aFHg/s1600/Gone+Gril.jpg" height="265" width="400" /></a></div>
<br />
這半年看的小說。<br />
<b>地堡系列</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sLH1xTi4voZ1GeXImNYuyTYZzfT74LeFf7Cpd1XQ92h6I8pH_4lk-ZJX-AOOFrpqBiWsQcpTi5qvj_xjooIu4VUC325kV2y0LJAZEkDziJ31Z2mCiQKQDuZ38nrgQRM0rH-rRCZc2ds/s1600/wool.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sLH1xTi4voZ1GeXImNYuyTYZzfT74LeFf7Cpd1XQ92h6I8pH_4lk-ZJX-AOOFrpqBiWsQcpTi5qvj_xjooIu4VUC325kV2y0LJAZEkDziJ31Z2mCiQKQDuZ38nrgQRM0rH-rRCZc2ds/s1600/wool.jpg" height="200" width="200" /></a></div>
<b><br /></b>
羊毛記看起來還挺不錯的,特別是第一段。故事的感情與情境、情節融合的很好。<br />
續集星移、塵土故事也說得不錯,情節很吸引人。雖然也是一個未來反烏托邦的故事,但是主角(或者說作者)比飢餓遊戲、分歧者這類的要成熟一點。對這點感冒的人,可能會比較喜歡這本。整個故事的謎底還算令人滿意。<br />
整體來說,相當推薦,是好看的小說。當初買來羊毛記之後,我立刻熬夜讀完。<br />
<br />
<b>14 號門</b><br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzrTA0QTwc7sctEWCGD5rLjmXtNnsmLHlEgA3VN6IKHED79FQ05EWXdDUhhHc46I9HcJZCtvla8KrRTCOGpzTY8Poh0K36FwrTCjYvsqIUFOgB7UOmBJxv_N9i6KYi8Gc1Jw54T5uiG9E/s1600/14door.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzrTA0QTwc7sctEWCGD5rLjmXtNnsmLHlEgA3VN6IKHED79FQ05EWXdDUhhHc46I9HcJZCtvla8KrRTCOGpzTY8Poh0K36FwrTCjYvsqIUFOgB7UOmBJxv_N9i6KYi8Gc1Jw54T5uiG9E/s1600/14door.jpg" height="200" width="200" /></a></div>
<b><br /></b>
這本的情節很吸引人,但是謎底讓人有點失望。不是特別爛的結局,但感覺只是一個普通的結局。<br />
故事敘述一間公寓中,充滿著各種詭異的事情。一個被重重鎖住,相當可疑的門。奇怪的規矩和事件。中間解謎的過程可以說是緊張、懸疑中帶著歡樂。我還挺享受這一段的閱讀。相比之下,謎底和結局也不是那麼重要了。<br />
推薦程度中等。<br />
<br />
<b>起點人、終點人</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjl96p89WS6LLQw429yTua49DdXG0_CXtlh0ORiKu2kkWgDVWcOTHj65cKFHkODHhh_mZ4S5tw-fqtcEuyAob3LNSxua88x2m4j9UqXjdtClF39MClrneRnetfTEFARe8_UHfIYf9Of3c/s1600/terminal.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjl96p89WS6LLQw429yTua49DdXG0_CXtlh0ORiKu2kkWgDVWcOTHj65cKFHkODHhh_mZ4S5tw-fqtcEuyAob3LNSxua88x2m4j9UqXjdtClF39MClrneRnetfTEFARe8_UHfIYf9Of3c/s1600/terminal.jpg" height="200" width="141" /></a></div>
<b><br /></b>
這個系列也是以青少年當主角、青少年、兒童取向的反烏托邦小說。但跟飢餓遊戲和分歧者比起來,比較適合成年人的口味一點點。<br />
設定很有趣,講的是一個某種原因之下,所有的大人都死亡,只剩下起點人(小孩)和終點人(老人)的世界。老人掌握資源和權力,可以制定法律。這樣的法律表面上看起來冠冕堂皇,但沒有例外的偏袒掌權者。沒有老人照顧的小孩,因為未成年,連工作權也沒有。<br />
故事情節也不差。其中的謎團解答繞了幾圈,還算有趣。<br />
推薦程度中上。<br />
<br />
<b>黑塔系列</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimzXAWQkssKFrqjVglamZMSbUMOBxAuz9d-hJcK69KCsu3Qy_p2-3Ay3oSzI3-tBXL1sfakthBKsKlF47_lZWIQocZq_7Rjyh9VwD-UAwWuLfnTaToIjLSIKEqeYOkYkMYNpZyviaWLFw/s1600/darktower.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimzXAWQkssKFrqjVglamZMSbUMOBxAuz9d-hJcK69KCsu3Qy_p2-3Ay3oSzI3-tBXL1sfakthBKsKlF47_lZWIQocZq_7Rjyh9VwD-UAwWuLfnTaToIjLSIKEqeYOkYkMYNpZyviaWLFw/s1600/darktower.jpg" height="200" width="141" /></a></div>
<b><br /></b>
黑塔系列很有名,評論的人也相當多。我看了一到七集,還沒看外傳。<br />
對我來說,第一本不會構成障礙。不過第一本的確故事性較低。其實整個黑塔系列的故事進展都不算太快,就像史蒂芬金的其他小說一樣。有趣的是情境、心境描述、氣氛營造。<br />
這個系列我從圖書館每個星期兩本兩本的借,每一本的感覺都不太一樣。但多半都挺好看的。其中一本是出門時從圖書館借出,然後搭捷運的來回途中就看完了。回來剛好直接還書,借下一本。但其中讓我感受最深的是結局。以這個系列的份量來說,這是一個完美的結局。稍微有一點驚訝,一點失落,但也很感動。不是那種會掉眼淚的感動,而是感受到整個世界的深度。<br />
這本是否推薦是見仁見智,因為相對來說需要花一些力氣來讀。不過如果你能接受史蒂芬金的文字風格的話,我覺得會感受很多。<br />
<br />
<b>別相信任何人</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIPIqGuAOJ6hYp3CsyDuorE953LGZC0sGgBE9jolq1H_7Nj7kNhmhAf2H8u4jQMH5DXaaGGP9kqsZybLCh-S8_H9XgaYo2qpJ5cCrMZSMko1k7SyNVSituJZk5rL2mnVfDxpU-TE261FY/s1600/before+I+go+sleep.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIPIqGuAOJ6hYp3CsyDuorE953LGZC0sGgBE9jolq1H_7Nj7kNhmhAf2H8u4jQMH5DXaaGGP9kqsZybLCh-S8_H9XgaYo2qpJ5cCrMZSMko1k7SyNVSituJZk5rL2mnVfDxpU-TE261FY/s1600/before+I+go+sleep.jpg" height="200" width="150" /></a></div>
<b><br /></b>
從一開頭就相當吸引人。主角在設定上雖然年齡不小了,但是在心智成熟度上,感覺跟飢餓遊戲的主角差不了太多。<br />
主角有失憶症,所以寫本日記當成外部記憶體。也因此常會出現故事中的故事中的故事(像是小說中的主角讀著日記裡面寫著聽到別人說的事情)。因為這樣的設定,作者可以巧妙的運用各式各樣的手法技巧,來誘導、誤導、引導你。讓你分不清真假。<br />
各種線索間的衝突,新事證(或者偽證)不斷的像是擠牙膏一樣的慢慢被擠出來。爆點、疑點不斷,相當過癮。<br />
結局不算驚豔,但至少還合格。<br />
非常推薦。<br />
<br />
<b>控制</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMqbV-4FNYf3AaD5qdocZu4NkYKhVC7QZCcIs4Dw2FLMork8hRi2riGaQwO6aL9Np17k-M0iDjQxS5GQVZqr7een6MciCTsLIjqhqxU-qgnhgnDHHDXIWOAl5-5zzDvDFonRmN-0ZJGqk/s1600/gonegirlbook.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMqbV-4FNYf3AaD5qdocZu4NkYKhVC7QZCcIs4Dw2FLMork8hRi2riGaQwO6aL9Np17k-M0iDjQxS5GQVZqr7een6MciCTsLIjqhqxU-qgnhgnDHHDXIWOAl5-5zzDvDFonRmN-0ZJGqk/s1600/gonegirlbook.jpg" height="200" width="130" /></a></div>
<b><br /></b>
這本我讀英文版,讀了好久,前後大概快一個月吧。雖然別相信任何人也是讀英文,但份量多很多,而且用字也稍微難一點。<br />
控制跟別相信任何人一樣,主軸是夫妻的事情。一開始的情節並不像「別相信任何人」那麼吸引人,但也不算無聊。<br />
第一部的內容也參雜了許多日記,所以前面說的技巧,也一樣可以使用,只是沒有那麼霸氣外露,相對看起來比較平淡。即使是真實的場景,作者也用了一些手法來處理真假難分的氣氛。各種線索也像擠牙膏一樣慢慢擠出來。不過這些線索不像「別相信任何人」一樣接連引爆,很多只是默默的埋梗。<br />
但剛開始相對平實的步調只是用來襯托後來的瘋狂而已。從第一部大約三分之二開始,勁道就開始變強。前面的部份開始發酵。前面埋的一些梗開始爆了。<br />
第二部就直接露出瘋狂的本性,情節突然加快。除了埋更多梗之外,前面的梗也接二連三引爆。<br />
到了第三部以後,更可以說是超越了瘋狂,直接到達了變態的境界。前面你以為已經爆開的地雷,到這裡居然又爆開了一次。<br />
如果喜歡閱讀的人,這本非常推薦。<br />
<br />
<b>記憶傳承人</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTlmLsLVdqyvoBUbG-Wd_KopYQkkL5SvIIEH6dWcZLfmTw9VA4FQykE4m2O1hPFr9OHjAp_KM90nCSCQLVIEtuU2dI04Mws__g_qzewrrirYbXc0Y2GDAuOrORxpvGPbTi0sW3EdPvKRQ/s1600/giver.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTlmLsLVdqyvoBUbG-Wd_KopYQkkL5SvIIEH6dWcZLfmTw9VA4FQykE4m2O1hPFr9OHjAp_KM90nCSCQLVIEtuU2dI04Mws__g_qzewrrirYbXc0Y2GDAuOrORxpvGPbTi0sW3EdPvKRQ/s1600/giver.jpg" height="200" width="135" /></a></div>
<b><br /></b>
讀完前面兩本後(中間只看了鋼之鍊金術師漫畫),讀這本有種失速的感覺。<br />
除了內容上的差異外,份量上也輕薄很多。<br />
主角設定比之飢餓遊戲、分歧者、向達倫這類比較現代的青少年小說,沒有那麼.......要怎麼說,中二?這樣可能有點濫用。總之,就是沒有特別血緣,也不會設定成太勇敢、太特別、特救世主、太主角投射、太瑪麗蘇。主角就是很平常的青少年,帶有一點叛逆沒錯,有一點點的「特異能力」沒錯,但沒有那種莫名其妙的多愁善感。<br />
開頭的設定沒有太驚奇之處,情節描述也很平淡。但還算有趣,而且進展很快(因為篇幅小),所以讀起來沒什麼問題。中後段開始漸入佳境,幾個爆點不錯,頗有狂人日記的禮教吃人感覺。<br />
後段情節開始翻轉,有種青少年版的重裝任務的感覺。就當翻轉中又有轉折,計畫趕不上變化,開始讓人期待的時候,慢慢發現感覺有點怪,進展有點緩慢。然後情節戛然而止。<br />
這時候你就會開始計較設定模糊奇怪、謎底沒有解答。<br />
持平而論,這一系列有四本,也許看到後面會好一點,<br />
推薦程度: 比較推薦給小朋友看。大人看也可以,但是不要期待太高,注意裡面的優點即可。<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com1tag:blogger.com,1999:blog-6532252579803357385.post-82768543411142082672014-10-15T10:15:00.001+08:002014-12-24T21:47:41.595+08:00Python 超級新手教學<div class="separator" style="clear: both; display: none; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOvoRnzp_sPeUPtM0wmoq57DbJz2dE6d-WmNo6HZTsS-7fYp7DVR1Hjauz5sLUcc4OBkOMLvOs6ajgiBd3oQYdGpkXbi94vdcKClMiXN8a-SQo6brBK7-90bq3roD42WoQtDnw0lwl7c/s1600/hqdefault.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOvoRnzp_sPeUPtM0wmoq57DbJz2dE6d-WmNo6HZTsS-7fYp7DVR1Hjauz5sLUcc4OBkOMLvOs6ajgiBd3oQYdGpkXbi94vdcKClMiXN8a-SQo6brBK7-90bq3roD42WoQtDnw0lwl7c/s1600/hqdefault.jpg" height="240" width="320" /></a></div>
<br />
<iframe allowfullscreen="" frameborder="0" height="344" src="https://www.youtube.com/embed/videoseries?list=PLl6jkZVaP8zrH8SNpgEfWKg8ofJDYrf1w" width="425"></iframe><br />
<br />
暑假錄的 Python 入門教學影片,對象是程式語言的超級新手。所以進度非常的慢。<br />
<br />
一些細節會省略,等著有人問的時候再回答。<br />
<br />
但拍出來後,一直覺得效果還可以再改進,所以一直沒放上來。<br />
<br />
但現在想想,還是先放上再說。內容還會陸續增加,短期目標是寫出一個可玩的遊戲。<br />
有任何可以利用的地方,歡迎自行取用。<br />
<br />
<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-13488057704521514232014-10-13T12:23:00.001+08:002014-10-13T12:35:49.428+08:00桃蛙源記<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn0QesZadVCawG8befvx0xKIm0H8X0PQ8wYoM2faSm65DDBZcFzwrAbjfrwSnBQ86aZuPJTHAMIQYzcz3ZyWMdsuL-pBO-qnVuyX-HaQKmu69c-sKjCkvyYABK5JaEAY-uMzZVw5HHZ9E/s1600/f08430ded865284b6bf84f8d8cd3db2c.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn0QesZadVCawG8befvx0xKIm0H8X0PQ8wYoM2faSm65DDBZcFzwrAbjfrwSnBQ86aZuPJTHAMIQYzcz3ZyWMdsuL-pBO-qnVuyX-HaQKmu69c-sKjCkvyYABK5JaEAY-uMzZVw5HHZ9E/s1600/f08430ded865284b6bf84f8d8cd3db2c.jpg" height="488" width="640" /></a></div>
<br />
因為兒子看到報紙上電影廣告,指名要看的關係,就帶他去看了他生平第一部看的電影。<br />
我兒子對電影的品味實在不怎麼樣,在電視上看到「無敵鯊魚」、「賭城風雲」的片段,居然還連翻叫好。所以我對桃蛙源記的期待並不很高。 況且,光片名就像是有點年紀的人才會取的。<br />
<br />
<br />
<br />
先說 3D 動畫品質。一些水、天空等等的風景弄得還挺美的,青蛙的紋理大概是有考據的,可能也挺真實的。不過呢,要怎麼說,簡單來講,這部電影的成本只有三千萬。<br />
不要對 3D 動畫要求太高。大體上來說,我覺得動畫品質足以讓我順利融入劇情。<br />
<br />
我比較不喜歡的是說故事的技巧。<br />
比方青蛙離開故鄉的原因是:美國牛蛙入侵、農藥、農地被破壞(大「普」開發案)。在短短的時間,要塞入這麼多「有意義」的東西,其實需要良好的敘事技巧。雖然跟後面老鼠、蛇、鷹那段有點相互呼應,但還是過於隱晦、快速。表現大草澤美好的時間太少,很難突顯後面被破壞的反差。<br />
說故事的時間被拿去哪裡了呢? 大部分的時間被教育以及懷舊用掉了。<br />
比方一開始的片頭,就故事性來說,可以很快就帶過。但為了教育性,篇幅大概變成了三倍左右。又為了快速進入主題,所以根本沒時間突顯大草澤的美好。<br />
不過片頭的情節其實一小段一小段來看,其實還不錯,只是沒有跟後面連起來。但至少主題是相關的。<br />
<br />
後面有很多懷舊的橋段,就完全是天外飛來一筆,跟前後劇情、主題都沒有關係。<br />
<br />
<br />
但上面寫的這些完全不重要。 因為這部電影的目標對象不是我,而是小朋友。<br />
從現場的反應來看,小朋友的反應挺熱烈的。<br />
就像我一開頭說的,小鬼的品味實在不怎麼樣。但又有誰真的能決定品味是什麼?<br />
我還是推薦這部國片。<br />
如果你的小孩還小,讓他自己建立自己的品味。<br />
如果你的小孩年紀大一點,可以辯論溝通、互相了解。順便也試著理解一下電影想傳達、或無意間傳達的價值觀。<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-83395532481009158432014-09-27T22:09:00.003+08:002014-09-27T22:13:50.805+08:00新部落格:松鼠爸爸講故事<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBuzNgUsJBKETCfj9fLfzKT3PquCVyNxMAm5kYyiVEs1FXpvS0Zw_BR5PtxgjSrpaGNWcIC7Cjm1P4d2arSujXZXR0athS20MtVdem1vS9iHD3TmRXbLaa55VUyjfUmZvLfKRfIAQ2zho/s1600/lego-cube-under-ultraviolet-lighting_w725_h544.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBuzNgUsJBKETCfj9fLfzKT3PquCVyNxMAm5kYyiVEs1FXpvS0Zw_BR5PtxgjSrpaGNWcIC7Cjm1P4d2arSujXZXR0athS20MtVdem1vS9iHD3TmRXbLaa55VUyjfUmZvLfKRfIAQ2zho/s1600/lego-cube-under-ultraviolet-lighting_w725_h544.jpg" height="480" width="640" /></a></div>
<br />
部落格:<a href="http://minstories.blogspot.tw/">松鼠爸爸講故事</a><br />
這是這星期弄出的部落格,裡面有許多小故事,目前有六十多則故事,計畫持續更新。<br />
雖然要在網路上找到故事,並不困難,但故事弄容有好有壞。<br />
我為了自己方便,將我自己喜歡的故事,整理蒐集在這個部落格裡。故事內容大多有改寫過,符合自己的需求。<br />
比方說伊索寓言的「<a href="http://minstories.blogspot.tw/2014/09/blog-post_47.html">牛和青蛙</a>」,讓無辜的青蛙家庭先是死掉小孩再死掉媽媽,實在太慘無人道了。安徒生童話有些很長,所以我就刪減節錄。<br />
還有一些故事(像是中國的一些經典),後面會感嘆和寓意,這些我也刪除。<br />
總之,盡量不讓故事太。也不蒐集太無聊的故事。阿凡提系列的故事,小時候讀覺得挺幽默有智慧的,但現在看看,卻覺得挺蠢,所以只收錄一小部分。<br />
所謂的不無聊,一般來說是內容有點寓意或者意思、情結有點轉折,或者是某種可以用到的典故(像<a href="http://minstories.blogspot.tw/2014/09/blog-post_81.html">伊卡洛斯</a>)。<br />
有一些故事其實是大故事的藍本/原始版本(像是<a href="http://minstories.blogspot.tw/2014/07/blog-post_8.html">雙夢記</a>是牧羊少年的奇幻之旅,還有冰雪奇緣源自安徒生的雪后)<br />
一些不適合小孩的故事,雖然很有趣,也暫時不放(像是天堂踩鴨子的故事,還在考慮怎麼改寫比較好)<br />
此外,將來也會多放一些原創的故事。<br />
非原創的故事,我儘量將我知道的來源寫下,像是瞎子摸象其實是佛經中的故事。<br />
相信很多人也會有講故事的需求,可能是給小孩聽,也可能是演講中可以運用。因為每個人的需求及口味不同,所以我喜歡的,不見得適合你。不過參考看看無妨,說不定會喜歡。<br />
<br />
<br />tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0tag:blogger.com,1999:blog-6532252579803357385.post-62814856964674038652014-09-11T09:53:00.000+08:002014-09-11T09:53:12.295+08:00真‧程式師用 One-Way Hash 來寫程式<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0o4WKlCzjLIN_siKe2ru5ZbkjdJ4qX6FqGW0ytBSLKTULKOfA8yOwC6vySyuB15RPTwnZfv2FSqRymy5ODSPXqjESIzuH3v9n5Md1FadGl8hzTjxWPQBZqqXuiba8ZyhOJENrKGYUBE4/s1600/2014-09-10+17:16:05+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0o4WKlCzjLIN_siKe2ru5ZbkjdJ4qX6FqGW0ytBSLKTULKOfA8yOwC6vySyuB15RPTwnZfv2FSqRymy5ODSPXqjESIzuH3v9n5Md1FadGl8hzTjxWPQBZqqXuiba8ZyhOJENrKGYUBE4/s1600/2014-09-10+17:16:05+%E7%9A%84%E8%9E%A2%E5%B9%95%E6%93%B7%E5%9C%96.png" height="442" width="640" /></a></div>
<br />
每隔一段時間,一定會看到關於真‧程式師要使用哪種編輯器的爭論,到最後一定會變成直接用 echo,直接用磁鐵改硬碟之類的。<br />
<blockquote class="tr_bq">
GNU nano is a text editor - a program often used to edit the source code of other programs. Emacs, Vim and ed are all progressively more "hard core" editors. cat is a Unix program that concatenates and outputs the contents of files. Things get steadily more ridiculous from here. Using a magnetised needle to flip bits on a hard drive requires nanometer precision and binary mastery, but in the early days of programming people did use needles sometimes to fix bugs on Punched cards. The use of a magnetized needle may also be a reference to the Apollo AGC guidance computer, whose instructions were physically written as patterns of wires looped around or through cylindrical magnets in order to record binary code. -- <a href="http://www.explainxkcd.com/wiki/index.php/378:_Real_Programmers">http://www.explainxkcd.com/wiki/index.php/378:_Real_Programmers</a></blockquote>
但對一個真‧程式師而言,直接寫 binary code 實在太容易了,即使是用磁鐵也一樣。真正的硬核程式師應該要用像是 sha2、 md5 這樣的東西來寫程式才有挑戰性。<br />
<br />
就像之前的 <a href="http://weijr-eng.blogspot.tw/2013/10/great-python-challenge.html">Great Python Challenge</a> 一樣,之前也提出了一個挑戰,挑戰真‧程式師用 one-way hash 來寫程式,比方:<br />
<blockquote class="tr_bq" style="background-color: black;">
cat 0.c| shasum -a 384 | xxd -p -r > a.out && chmod a+x a.out && ./a.out</blockquote>
會選擇 sha384 是因為他剛好夠長,可以塞進一個 ELF 執行檔(請參考 <a href="http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html">http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html</a>).<br />
<br />
當然也不能只挑戰別人,所以我自己也嘗試求解。最後的解如下面<br />
照下面的步驟就能測試 (Copy& Paste) :<br />
<blockquote class="tr_bq" style="background-color: black;">
mkdir test<br />
cd test<br />
wget https://raw.githubusercontent.com/tjwei/tjw_ipynb/master/0.c<br />
sh 0.c<br />
gcc 0.c && ./a.out<br />
cat 0.c | shasum -a 384 | xxd -p -r > a.out && chmod a+x a.out<br />
./a.out<br />
<div>
<br /></div>
</blockquote>
<br />
<b>過程</b><br />
<br />
第一版的解答很快就出來了 <a href="https://github.com/tjwei/tjw_ipynb/blob/master/a.c">https://github.com/tjwei/tjw_ipynb/blob/master/a.c</a><br />
基本上能執行,符合要求,但我還是希望至少能印出 "Hello World!"。<br />
第二個解答不久後也出來, <a href="https://raw.githubusercontent.com/tjwei/tjw_ipynb/aa733143a5a76f460122e93028f49693d53931e8/0.c">https://raw.githubusercontent.com/tjwei/tjw_ipynb/aa733143a5a76f460122e93028f49693d53931e8/0.c</a><br />
在有些環境下,的確符合要求,能印出 "Hello World!",程式碼看來也挺漂亮的,但不夠完整。<br />
最後我只好用更暴力的方式來搜尋解答,首先將程式碼縮短, comment 都去掉,只留下一個簡短的網址來取代原來的說明。 因為長度小於 112 byte,才能用一次 sha512 block 運算完成編碼。 這樣大概至少讓速度快個五倍。 在沒有超頻的 HD7970 ghz edition,可以用160 mhash/s, 的速度來搜尋。如果單純的 preimage 搜尋,甚至可以接近 180 mhash/s。而且還有一些加速的空間。<br />
總之,結果在 <a href="https://github.com/tjwei/tjw_ipynb/blob/master/0.c">https://github.com/tjwei/tjw_ipynb/blob/master/0.c</a><br />
中間工作的過程在<br />
<a href="http://nbviewer.ipython.org/github/tjwei/tjw_ipynb/blob/master/RealMan2.ipynb">http://nbviewer.ipython.org/github/tjwei/tjw_ipynb/blob/master/RealMan2.ipynb</a><br />
<br />
而既然這是一個挑戰,我的確接到一些回應,其中一個由 Kuo-Tung 提供的解答,也挺有趣的,可以在這裡看到 <a href="http://paste.plurk.com/show/1982392/">http://paste.plurk.com/show/1982392/</a>tjweihttp://www.blogger.com/profile/13263392105910479364noreply@blogger.com0