整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          寫個網頁更簡單了!讓AI根據手繪原型生成HTML - 教程+代碼

          小新 編譯自 Insight Data Blog

          量子位 出品 | 公眾號 QbitAI

          寫個網頁能有多麻煩?在大多數公司里,這項工作分為三步:

          1. 產品經理完成用戶調研任務后,列出一系列技術要求;

          2. 設計師根據這些要求來設計低保真原型,逐漸修改得到高保真原型和UI設計圖;

          3. 工程師將這些設計圖實現為代碼,最終變成用戶使用的產品。

          這么多環節,任何地方出一點問題,都會拉長開發周期。因此,不少公司,比如Airbnb已經開始用機器學習來提高這個過程的效率。

          Airbnb內部的AI工具,從圖紙到代碼一步到位

          看起來很美好,但Airbnb還沒公開該模型中端到端訓練的細節,以及手工設計的圖像特征對該模型的貢獻度。這是該公司特有的閉源解決方案專利,可能不會進行公開。

          好在,一個叫Ashwin Kumar的程序員創建了一個開源版本,讓開發者/設計師的工作變得更簡單。

          以下內容翻譯自他的博客:

          理想上,這個模型可以根據網站設計的簡單手繪原型,很快地生成一個可用的HTML網站:

          SketchCode模型利用手繪線框圖來生成HTML網站

          事實上,上面例子就是利用訓練好的模型在測試集上生成的一個實際網站,代碼請訪問:https://github.com/ashnkumar/sketch-code。

          從圖像標注中獲取靈感

          目前要解決的問題屬于一種更廣泛的任務,叫做程序綜合(program synthesis),即自動生成工作源代碼。盡管很多程序綜合研究通過自然語言規范或執行追蹤法來生成代碼,但在當前任務中,我會充分利用源圖像,即給出的手繪線框圖來展開工作。

          在機器學習中有一個十分熱門的研究領域,稱為圖像標注(image caption),目的是構建一種把圖像和文本連接在一起的模型,特別是用于生成源圖像內容的描述。

          圖像標注模型生成源圖像的文本描述

          我從一篇pix2code論文和另一個應用這種方法的相關項目中獲得靈感,決定把我的任務按照圖像標注方式來實現,把繪制的網站線框圖作為輸入圖像,并將其相應的HTML代碼作為其輸出內容。

          注:上段提到的兩個參考項目分別是

          pix2code論文:https://arxiv.org/abs/1705.07962

          floydhub教程:https://blog.floydhub.com/turning-design-mockups-into-code-with-deep-learning/?source=techstories.org

          獲取合適的數據集

          確定圖像標注方法后,理想中使用的訓練數據集會包含成千上萬對手繪線框圖和對應的HTML輸出代碼。但是,目前還沒有我想要的相關數據集,我只好為這個任務來創建數據集。

          最開始,我嘗試了pix2code論文給出的開源數據集,該數據集由1750張綜合生成網站的截圖及其相應源代碼組成。

          pix2code數據集中的生成網站圖片和源代碼

          這是一個很好的數據集,有幾個有趣的地方:

          • 該數據集中的每個生成網站都包含幾個簡單的輔助程序元素,如按鈕、文本框和DIV對象。盡管這意味著這個模型受限于將這些少數元素作為它的輸出內容,但是這些元素可通過選擇生成網絡來修改和擴展。這種方法應該很容易地推廣到更大的元素詞匯表。

          • 每個樣本的源代碼都是由領域專用語言(DSL)的令牌組成,這是該論文作者為該任務所創建的。每個令牌對應于HTML和CSS的一個片段,且加入編譯器把DSL轉換為運行的HTML代碼。

          彩色網站圖像變手繪圖

          為了修改我的任務數據集,我要讓網站圖像看起來像手工繪制出的。我嘗試使用Python中的OpenCV庫和PIL庫等工具對每張圖像進行修改,包括灰度轉換和輪廓檢測。

          最終,我決定直接修改原始網站的CSS樣式表,通過執行以下操作:

          1. 更改頁面上元素的邊框半徑來平滑按鈕和DIV對象的邊緣;

          2. 模仿繪制的草圖來調整邊框的粗細,并添加陰影;

          3. 將原有字體更改為類似手寫的字體;

          最終實現的流程中還增加了一個步驟,通過添加傾斜、移動和旋轉來實現圖像增強,來模擬實際繪制草圖中的變化。

          使用圖像標注模型架構

          現在,我已經處理好數據集,接下來是構建模型。

          我利用了圖像標注中使用的模型架構,該架構由三個主要部分組成:

          1. 一種使用卷積神經網絡(CNN)的計算機視覺模型,從源圖像提取圖像特征;

          2. 一種包含門控單元GRU的語言模型,對源代碼令牌序列進行編碼;

          3. 一個解碼器模型,也屬于GRU單元,把前兩個步驟的輸出作為輸入,并預測序列中的下一個令牌。

          以令牌序列為輸入來訓練模型

          為了訓練模型,我將源代碼拆分為令牌序列。模型的輸入為單個部分序列及它的源圖像,其標簽是文本中的下一個令牌。該模型使用交叉熵函數作為損失函數,將模型的下個預測令牌與實際的下個令牌進行比較。

          在模型從頭開始生成代碼的過程中,該推理方式稍有不同。圖像仍然通過CNN網絡進行處理,但文本處理開始時僅采用一個啟動序列。在每個步驟中,模型對序列中輸出的下個預測令牌將會添加到當前輸入序列,并作為新的輸入序列送到模型中;重復此操作直到模型的預測令牌為,或該過程達到每個文本中令牌數目的預定義值。

          當模型生成一組預測令牌后,編譯器就會將DSL令牌轉換為HTML代碼,這些HTML代碼可以在任何瀏覽器中運行。

          用BLEU分數評估模型

          我決定使用BLEU分數來評估模型。這是機器翻譯任務中常用的一種度量標準,通過在給定相同輸入的情況下,衡量機器生成的文本與人類可能產生內容的近似程度。

          實際上,BLEU通過比較生成文本和參考文本的N元序列,以創建修改后的準確版本。它非常適用于這個項目,因為它會影響生成HTML代碼中的實際元素,以及它們之間的相互關系。

          最棒的是,我還可以通過檢查生成的網站來比較當前的實際BLEU分數。

          觀察BLEU分數

          當BLEU分數為1.0時,則說明給定源圖像后該模型能在正確位置設置合適的元素,而較低的BLEU分數這說明模型預測了錯誤元素或是把它們放在相對不合適的位置。我們最終模型在評估數據集上的BLEU分數為0.76。

          福利:定制網頁風格

          后來,我還想到,由于該模型只生成當前頁面的框架,即文本的令牌,因此我可以在編譯過程中添加一個定制的CSS層,并立刻得到不同風格的生成網站。

          一個手繪圖生成多種風格的網頁

          把風格定制和模型生成兩個過程分開,在使用模型時帶來了很多好處:

          1.如果想要將SketchCode模型應用到自己公司的產品中,前端工程師可以直接使用該模型,只需更改一個CSS文件來匹配該公司的網頁設計風格;

          2. 該模型內置的可擴展性,即通過單一源圖像,模型可以迅速編譯出多種不同的預定義風格,因此用戶可以設想出多種可能的網站風格,并在瀏覽器中瀏覽這些生成網頁。

          總結和展望

          受到圖像標注研究的啟發,SketchCode模型能夠在幾秒鐘內將手繪網站線框圖轉換為可用的HTML網站。

          但是,該模型還存在一些問題,這也是我接下來可能的工作方向:

          1. 由于這個模型只使用了16個元素進行訓練,所以它不能預測這些數據以外的令牌。下一步方向可能是使用更多元素來生成更多的網站樣本,包括網站圖片,下拉菜單和窗體,可參考啟動程序組件(https://getbootstrap.com/docs/4.0/components/buttons/)來獲得思路;

          2. 在實際網站構建中,存在很多變化。創建一個能更好反映這種變化的訓練集,是提高生成效果的一種好方法,可以通過獲取更多網站的HTML/CSS代碼以及內容截圖來提高;

          3. 手繪圖紙也存在很多CSS修改技巧無法捕捉到的變化。解決這個問題的一種好方法是使用生成對抗網絡GAN來創建更逼真的繪制網站圖像。

          相關地址

          代碼:https://github.com/ashnkumar/sketch-code

          原文:https://blog.insightdatascience.com/automated-front-end-development-using-deep-learning-3169dd086e82

          — 完 —

          誠摯招聘

          量子位正在招募編輯/記者,工作地點在北京中關村。期待有才氣、有熱情的同學加入我們!相關細節,請在量子位公眾號(QbitAI)對話界面,回復“招聘”兩個字。

          量子位 QbitAI · 頭條號簽約作者

          ?'?' ? 追蹤AI技術和產品新動態

          建Web游戲



          今天小編教大家如何用Python編程語言創建Web游戲,如果你能完成,你就可以算是一個能力相當不錯的Python初學者了。雖然還需要多讀一些書,多寫一些程序,不過你已經具備進一步學習的功底了。接下來的學習就只是時間、動力及資源的問題了。

          在這個習題中,我們不會去創建一個完整的游戲,相反,我們會為習題42中的游戲創建一個“引擎”(engine),讓這個游戲能夠在瀏覽器中運行起來。這會涉及重構習題42中的游戲,混合習題47中的結構,添加自動測試代碼,最后創建一個可以運行這個游戲的Web引擎。

          這是一個很龐大的習題。預計你要花一周到一個月才能完成。最好的方法是一點一點來,每晚完成一點,在進行下一步之前確認上一步已經正確完成。

          重構習題43中的游戲

          你已經在兩個習題中修改了gothonweb項目,這個習題中會再修改一次。你學習的這種修改的技術叫做“重構”,或者用我喜歡的講法來說,叫“修理”。重構是一個編程術語,它指的是清理舊代碼或者為舊代碼添加新功能的過程。你其實已經做過這樣的事情了,只不過不知道這個術語而已。重構是軟件開發中經歷的最習以為常的事情。

          在這個習題中你要做的是將習題47中的可以測試的房間地圖和習題43中的游戲這兩樣東西合并到一起,創建一個新的游戲結構。游戲的內容不會發生變化,只不過我們會通過“重構”讓它有一個更好的結構而已。

          第一步是將ex47/game.py的內容復制到gothonweb/map.py中,然后將tests/ex47_tests.py的內容復制到tests/map_tests.py中,然后再次運行nosetests,確認它們還能正常工作。

          注意

          從現在開始,我不會再展示運行測試的輸出了,我假設你會回去運行這些測試,而且知道什么樣的輸出是正確的。

          將習題47的代碼復制完畢后,就該開始重構它,讓它包含習題43中的地圖。我一開始會把基本結構為你準備好,然后你需要去完成map.py和map_tests.py里邊的內容。

          首先要做的是用Room這個類來構建地圖的基本結構。

          map.py

          1  class Room(object):
          2  
          3      def __init__(self, name, description):
          4          self.name = name
          5          self.description = description
          6          self.paths = []
          7  
          8      def go(self, direction):
          9           return self.paths.get(direction, None)
          10  
          11      def add_paths(self, paths):
          12           self.paths.update(paths)
          13  
          14  
          15  central_corridor = Room("Central Corridor",
          16  """
          17  The Gothons of Planet Percal #25 have invaded your ship and destroyed
          18  your entire crew.  You are the last surviving member and your last
          19  mission is to get the neutron destruct bomb from the Weapons Armory,
          20  put it in the bridge, and blow the ship up after getting into an 
          21  escape pod.
          22  
          23  You're running down the central corridor to the Weapons Armory when
          24  a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume
          25  flowing around his hate filled body.  He's blocking the door to the
          26  Armory and about to pull a weapon to blast you.
          27  """)
          28  
          29  
          30  laser_weapon_armory = Room("Laser Weapon Armory",
          31  """
          32  Lucky for you they made you learn Gothon insults in the academy.
          33  You tell the one Gothon joke you know:
          34  Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr.
          35  The Gothon stops, tries not to laugh, then busts out laughing and can't move.
          36  While he's laughing you run up and shoot him square in the head
          37  putting him down, then jump through the Weapon Armory door.
          38  
          39  You do a dive roll into the Weapon Armory, crouch and scan the room
          40  for more Gothons that might be hiding.  It's dead quiet, too quiet.
          41  You stand up and run to the far side of the room and find the
          42  neutron bomb in its container.  There's a keypad lock on the box
          43  and you need the code to get the bomb out.  If you get the code
          44  wrong 10 times then the lock closes forever and you can't
          45  get the bomb.  The code is 3 digits.
          46  """)
          47  
          48  
          49  the_bridge = Room("The Bridge",
          50  """
          51  The container clicks open and the seal breaks, letting gas out.
          52  You grab the neutron bomb and run as fast as you can to the
          53  bridge where you must place it in the right spot.
          54  
          55  You burst onto the Bridge with the netron destruct bomb
          56  under your arm and surprise 5 Gothons who are trying to
          57  take control of the ship.  Each of them has an even uglier
          58  clown costume than the last.  They haven't pulled their
          59  weapons out yet, as they see the active bomb under your
          60  arm and don't want to set it off.
          61  """)
          62  
          63  
          64  escape_pod = Room("Escape Pod",
          65  """
          66  You point your blaster at the bomb under your arm
          67  and the Gothons put their hands up and start to sweat.
          68  You inch backward to the door, open it, and then carefully
          69  place the bomb on the floor, pointing your blaster at it.
          70  You then jump back through the door, punch the close button
          71  and blast the lock so the Gothons can't get out.
          72  Now that the bomb is placed you run to the escape pod to
          73  get off this tin can.
          74  
          75  You rush through the ship desperately trying to make it to
          76  the escape pod before the whole ship explodes.  It seems like
          77  hardly any Gothons are on the ship, so your run is clear of
          78  interference.  You get to the chamber with the escape pods, and
          79  now need to pick one to take.  Some of them could be damaged
          80  but you don't have time to look.  There's 5 pods, which one
          81  do you take?
          82  """)
          83  
          84  
          85  the_end_winner = Room("The End",
          86  """
          87  You jump into pod 2 and hit the eject button.
          88  The pod easily slides out into space heading to
          89  the planet below.  As it flies to the planet, you look
          90  back and see your ship implode then explode like a
          91  bright star, taking out the Gothon ship at the same
          92  time.  You won!
          93  """)
          94  
          95  
          96  the_end_loser = Room("The End",
          97  """
          98  You jump into a random pod and hit the eject button.
          99  The pod escapes out into the void of space, then
          100  implodes as the hull ruptures, crushing your body
          101  into jam jelly.
          102  """
          103  )
          104  
          105  escape_pod.add_paths({
          106      '2': the_end_winner,
          107      '*': the_end_loser
          108  })
          109  
          110  generic_death = Room("death", "You died.")
          111  
          112  the_bridge.add_paths({
          113      'throw the bomb': generic_death,
          114      'slowly place the bomb': escape_pod
          115  })
          116  
          117  laser_weapon_armory.add_paths({
          118      '0132': the_bridge,
          119      '*': generic_death
          120  })
          121  
          122  central_corridor.add_paths({
          123      'shoot!': generic_death,
          124      'dodge!': generic_death,
          125      'tell a joke': laser_weapon_armory
          126  })
          127  
          128  START = central_corridor

          你會發現Room類和地圖有一些問題。

          1.我們必須把以前放在if-else結構中的房間描述做成每個房間的一部分。這樣房間的次序就不會被打亂了,這對我們的游戲是一件好事。這是你后面要修改的東西。

          2.原版游戲中我們使用了專門的代碼來生成一些內容,如炸彈的激活鍵碼、艦艙的選擇等,這次我們做游戲時就先使用默認值好了,不過后面的附加練習里,我會要求你把這些功能再加到游戲中。

          3.我為游戲中所有錯誤決策的失敗結尾寫了一個generic_death,你需要去補全這個函數。你需要把原版游戲中所有的場景結局都加進去,并確保代碼能正確運行。

          4.我添加了一種新的轉換模式,以"*"為標記,用來在游戲引擎中實現“捕獲所有操作”的功能。

          等把上面的代碼基本寫好以后,接下來就是你必須繼續寫的自動測試tests/map_test.py了。

          map_tests.py

          1  from nose.tools import *
          2  from gothonweb.map import *
          3  
          4  def test_room():
          5       gold = Room("GoldRoom", 
          6                      """This room has gold in it you can grab. There's a
          7                      door to the north.""")
          8       assert_equal(gold.name, "GoldRoom")
          9       assert_equal(gold.paths, {})
          10  
          11  def test_room_paths():
          12      center = Room("Center", "Test room in the center.")
          13      north = Room("North", "Test room in the north.")
          14      south = Room("South", "Test room in the south.")
          15  
          16      center.add_paths({'north': north, 'south': south})
          17      assert_equal(center.go('north'), north)
          18      assert_equal(center.go('south'), south)
          19  
          20  def test_map():
          21      start = Room("Start", "You can go west and down a hole.")
          22      west = Room("Trees", "There are trees here, you can go east.")
          23      down = Room("Dungeon", "It's dark down here, you can go up.")
          24  
          25      start.add_paths({'west': west, 'down': down})
          26      west.add_paths({'east': start})
          27      down.add_paths({'up': start})
          28  
          29      assert_equal(start.go('west'), west)
          30      assert_equal(start.go('west').go('east'), start)
          31      assert_equal(start.go('down').go('up'), start)
          32  
          33  def test_gothon_game_map():
          34      assert_equal(START.go('shoot!'), generic_death)
          35      assert_equal(START.go('dodge!'), generic_death)
          36  
          37      room = START.go('tell a joke')
          38      assert_equal(room, laser_weapon_armory)

          你在這個習題中的任務是完成地圖,并且讓自動測試可以完整地檢查整個地圖。這包括將所有的generic_death對象修正為游戲中實際的失敗結尾。讓你的代碼成功運行起來,并讓你的測試越全面越好。后面我們會對地圖做一些修改,到時候這些測試將用來確保修改后的代碼還可以正常工作。

          會話和用戶跟蹤

          在Web應用程序運行的某個位置,你需要追蹤一些信息,并將這些信息和用戶的瀏覽器關聯起來。在HTTP協議的框架中,Web環境是“無狀態”的,這意味著你的每一次請求和你的其他請求都是相互獨立的。如果你請求了頁面A,輸入了一些數據,然后點了一個頁面B的鏈接,那你發送給頁面A的數據就全部消失了。

          解決這個問題的方法是為Web應用程序建立一個很小的數據存儲,給每個瀏覽器進程賦予一個獨一無二的數字,用來跟蹤瀏覽器所做的事情。這個存儲通常用數據庫或者存儲在磁盤上的文件來實現。在lpthw.web這個小框架中實現這樣的功能是很容易的,下面就是一個這樣的例子。

          session.sample.py

          1  import web
          2  
          3  web.config.debug = False
          4  
          5  urls = (
          6        "/count", "count",
          7        "/reset", "reset"
          8  )
          9  app = web.application(urls, locals())
          10  store = web.session.DiskStore('sessions')
          11  session = web.session.Session(app, store, initializer=['count': 0])
          12  
          13  class count:
          14       def GET(self):
          15            session.count += 1
          16            return str(session.count)
          17  
          18  class reset:
          19       def GET(self):
          20            session.kill()
          21            return ""
          22  
          23  if __name__ == "__main__":
          24      app.run()

          為了實現這個功能,需要創建一個sessions/文件夾作為程序的會話存儲位置,創建好以后運行這個程序,然后檢查/count頁面,刷新一下這個頁面,看計數會不會累加上去。關掉瀏覽器后,程序就會“忘掉”之前的位置,這也是我們的游戲所需的功能。有一種方法可以讓瀏覽器永遠記住一些信息,不過這會讓測試和開發變得更難。如果你回到/reset頁面,然后再訪問/count頁面,你可以看到你的計數器被重置了,因為你已經關掉了這個會話。

          你需要花點時間弄懂這段代碼,注意會話開始時count的值是如何設為0的,另外再看看sessions/下面的文件,看能不能打開。下面是我打開一個Python會話并解碼的過程:

          >>> import pickle
          >>> import base64
          >>> base64.b64decode(open("sessions/XXXXX").read())
          "(dp1\nS'count'\np2\nI1\nsS'ip'\np3\nV127.0.0.1\np4\nsS'session_id'\np5\nS'XXXX'\np6\ns."
          >>>
          >>> x = base64.b64decode(open("sessions/XXXXX").read())
          >>>
          >>> pickle.loads(x)
          {'count': 1, 'ip': u'127.0.0.1', 'session_id': 'XXXXX'}

          所以,會話其實就是使用pickle和base64這些庫寫到磁盤上的字典。存儲和管理會話的方法很多,大概和Python的Web框架那么多,所以了解它們的工作原理并不是很重要。當然如果你需要調試或者清空會話,知道點兒原理還是有用的。

          創建引擎

          你應該已經寫好了游戲地圖和它的單元測試代碼。現在要你制作一個簡單的游戲引擎,用來讓游戲中的各個房間運轉起來,從玩家收集輸入,并且記住玩家所在的位置。我們將用到你剛學過的會話來制作一個簡單的引擎,讓它可以:

          1.為新用戶啟動新的游戲;

          2.將房間展示給用戶;

          3.接收用戶的輸入;

          4.在游戲中處理用戶的輸入;

          5.顯示游戲的結果,繼續游戲,直到玩家角色死亡為止。

          為了創建這個引擎,你需要將bin/app.py搬過來,創建一個功能完備的、基于會話的游戲引擎。這里的難點是,我會先使用基本的HTML文件創建一個非常簡單的版本,接下來將由你完成它?;镜囊媸窍旅孢@個樣子的:

          app.py

          1  import web
          2  from gothonweb import map
          3  
          4  urls = (
          5      '/game', 'GameEngine',
          6      '/', 'Index',
          7  )
          8  
          9  app = web.application(urls, globals())
          10  
          11  # little hack so that debug mode works with sessions
          12  if web.config.get('_session') is None:
          13        store = web.session.DiskStore('sessions')
          14        session = web.session.Session(app, store,
          15                                            initializer=['room': None])
          16      web.config._session = session
          17  else:
          18       session = web.config._session
          19  
          20  render = web.template.render('templates/', base="layout")
          21  
          22  
          23  class Index(object):
          24       def GET(self):
          25          # this is used to "setup" the session with starting values
          26          session.room = map.START
          27          web.seeother("/game")
          28  
          29  
          30  class GameEngine(object):
          31  
          32      def GET(self):
          33           if session.room:
          34                return render.show_room(room=session.room)
          35           else:
          36              # why is there here? do you need it?
          37              return render.you_died()
          38  
          39      def POST(self):
          40           form = web.input(action=None)
          41  
          42          # there is a bug here, can you fix it?
          43          if session.room and form.action:
          44               session.room = session.room.go(form.action)
          45  
          46          web.seeother("/game")
          47  
          48  if __name__ == "__main__":
          49      app.run()

          在這個腳本里你可以看到更多的新東西,不過了不起的事情是,整個基于網頁的游戲引擎只要一個小文件就可以做到了。這段腳本里最有技術含量的就是將會話帶回來的那幾行,這對于調試模式下的代碼重載是必需的,否則每次刷新網頁,會話就會消失,游戲也不會再繼續了。

          在運行bin/app.py之前,你需要修改PYTHONPATH環境變量。不知道什么是環境變量?要運行一個最基本的Python程序,你就得學會環境變量,用Python的人就喜歡這樣:

          在終端輸入下面的內容:

          export PYTHONPATH=$PYTHONPATH:.

          如果用的是Windows,那就在PowerShell中輸入以下內容:

          $env:PYTHONPATH = "$env:PYTHONPATH;."

          你只要針對每一個shell會話輸入一次就可以了,不過如果你運行Python代碼時看到了導入錯誤,那就需要去執行一下上面的命令,或者是因為你上次執行的有錯才導致導入錯誤的。

          接下來需要刪掉templates/hello_form.html和templates/index.html,然后重新創建上面代碼中提到的兩個模板。下面是一個非常簡單的templates/show_room.html,供你參考。

          show_room.html

          $def with (room)
          
          <h1> $room.name </h1>
          
          <pre>
          $room.description
          </pre>
          
          $if room.name == "death":
              <p><a href="/">Play Again?</a></p>
          $else:
              <p>
              <form action="/game" method="POST">
                  - <input type="text" name="action"> <input type="SUBMIT">
              </form>
              </p>

          這就用來顯示游戲中的房間的模板。接下來,你需要在用戶跑到地圖的邊界時,用一個模板告訴用戶,他的角色的死亡信息,也就是templates/you_died.html這個模板。

          you_died.html

          <h1>You Died!</h1>
          
          <p>Looks like you bit the dust.</p>
          <p><a href="/">Play Again</a></p>

          準備好這些文件就可以做下面的事情了。

          1.再次運行測試代碼tests/app_tests.py,這樣就可以測試這個游戲。由于會話的存在,你可能頂多只能實現幾次點擊,不過你應該可以做出一些基本的測試來。

          2.刪除sessions/*下的文件,再重新運行一遍游戲,確認游戲是從一開始運行的。

          3. 運行python bin/app.py腳本,試玩一下你的游戲。

          你需要和往常一樣刷新和修正你的游戲,慢慢修改游戲的HTML文件和引擎,直到實現游戲需要的所有功能為止。

          期末考試

          你有沒有覺得我一下子給了你超多的信息呢?那就對了,我想要你在學習技能的同時有一些可以用來鼓搗的東西。為了完成這個習題,我將給你最后一套需要你自己完成的練習。你會注意到,到目前為止你寫的游戲并不是很好,這只是你的第一版代碼而已,你現在的任務就是讓游戲更加完善,實現下面的這些功能。

          1.修正代碼中所有我提到和沒提到的bug,如果你發現了新bug,你可以告訴我。

          2.改進所有的自動測試,以便可以測試更多的內容,直到你可以不用瀏覽器就能測到所有的內容為止。

          3.讓HTML頁面看上去更美觀一些。

          4.研究一下網頁登錄系統,為這個程序創建一個登錄界面,這樣人們就可以登錄這個游戲,并且可以保存游戲高分。

          5.完成游戲地圖,盡可能地把游戲做大,功能做全。

          6.給用戶一個“幫助系統”,讓他們可以查詢每個房間里可以執行哪些命令。

          7.為游戲添加新功能,想到什么功能就添加什么功能。

          8.創建多個地圖,讓用戶可以選擇他們想要玩的一張地圖來進行游戲。你的bin/app.py應該可以運行提供給它的任意地圖,這樣你的引擎就可以支持多個不同的游戲。

          9.最后,使用在習題48和習題49中學到的東西創建一個更好的輸入處理器。你手頭已經有了大部分必要的代碼,只需要改進語法,讓它和你的輸入表單以及游戲引擎掛鉤即可。

          祝你好運!

          常見問題回答

          我在游戲中用了會話(`session)`,不能用nosetests測試。

          你需要閱讀并了解帶reloader的會話:http://webpy.org/cookbook/session_with_reloader。

          我看到了ImportError。

          錯誤路徑,錯誤Python版本,PYTHONPATH沒設置對,漏了__init__.py文件,拼寫錯誤,都檢查一下吧。

          有天老板找我到辦公室跟我說要做一個商城,商城賣出去東西就有傭金可以拿。我聽著就頭大。老板打開電腦給我看了網站:你看一下這個網站,照著它的流程就可以擁有一個商城了。我靠過去一看,大概了解一下:原來是利用第三方工具就可以構建一個導購網站,只要消費者在網站領取優惠券就會自動跳轉到某bao的購買頁面,購買成功后就可以有傭金了。我看了一下覺得可以,只要不讓我敲代碼一切好說。于是我照著流程構建了一個網站,然后勾選了很多零食進行推廣,然后我就發現了一個問題:我只勾選了一些零食啊,商城怎么還有其它類型的商品?我思索了一下就明白了,這網站還是挺流氓的,還摻雜著其他人的推廣鏈接,我一想這樣不行,轉化率肯定低啊。果不其然,試用了一天就只有5個單子,因為公司的網站還是挺有流量的,所以這轉化率不可能這么低。老板看了一下,覺得沒什么用讓我把商城入口給關了,我只好照做,但是我心里對這流氓網站不服啊,于是我打算自己做一個導購網站。(最后還是要敲代碼(。?_?)/~~~)

          想法

          1. 在推廣平臺上下載一個商品清單的excel文檔,文檔的內容包含:商品的名稱、商品的主圖鏈接、商品分類、商品價格、商品推廣鏈接.....。
          2. 利用python讀取excel,獲得分類、商品信息的json文件。
          3. 創建一個html頁面讀取json文件,把分類和商品顯示出來,利用html中的錨點定位,點擊就會滾動到對應的分類商品,就可以選擇心儀的商品下單,從而達到推廣商品的作用了。

          行動

          1. 安裝xlrd

          cmd窗口: pip install xlrd
          

          2.創建index.py,導入模塊

          import xlrd
          

          3.打開Excel文件讀取數據

          wb= xlrd.open_workbook('文件路徑')
          

          4.獲取表格

          sheet1 = wb.sheet_by_index(0) #這里的excel文檔內只有一個表格,0代表第一個
          

          5.獲取表格的行數

          rows = sheet1.nrows
          

          6.獲取表格中的類目

          商品一級類目

          住宅家具

          影音電器

          影音電器

          美容護膚

          廚房電器

          運動服/休閑服裝

          餐飲具

          category0 = sheet1.col_values(4) #獲取列內容(類目),這里excel文檔的第四列是類目
          del category0[0] #刪除列表中的 "商品一級類目"
          category = sorted(set(category0),key=category0.index) #類目列表->去除重復
          

          7.整理數據

          [ 
           [
           分類名,
           [商品信息]
           ],
           [
           分類名,
           [商品信息]
           ]
          ]
          data = []
          for i,v in enumerate(category):
           data.append([v,[]])
          for i,v in enumerate(data):
           for x in range(rows):
           if v[0] == sheet1.cell(x,4).value:
           data[i][1].append(sheet1.row_values(x))
          

          8.導出json文件

          jsonData = json.dumps(data, ensure_ascii=False)
          with open('results.json', 'w',encoding="utf-8") as f:
           f.write(jsonData)
          

          9.運行index.py,獲得json文件

          cmd窗口:python index.py
          

          10.創建html頁面,并引用json文件

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
           <script>//引用jquery
           $(function(){
          function color16(){//十六進制顏色隨機
           var r = Math.floor(Math.random()*256);
           var g = Math.floor(Math.random()*256);
           var b = Math.floor(Math.random()*256);
           var color = '#'+r.toString(16)+g.toString(16)+b.toString(16);
           return color;
           }
          var navo = '';//類目導航
          var info = '';//商品內容
          $.get('./results.json', function(data) {//請求json文件
           
           $.each(data, function(index, val) {
           navo+="<a href='#"+val[0]+"''>"+val[0]+"</a> "
           });//把json文件中的類目數組遍歷出來,并用錨定位
           $.each(data, function(index, val) {
           var div_title = "<div id='"+val[0]+"' style='float:left;'>";
           var div_content = "";
           $.each(val[1], function(index, val) {
           div_content+="<div style='background:"+color16()+"' onclick=\"location.href=\'"+val[21]+"\'\" class='pro_img'>"+val[1]+"<span class='money'>¥"+val[6]+"</span><\/div>"
           });
           var div_footer ="</div><br>";
           info+=div_title+div_content+div_footer
           });
          $('#nav').html(navo);//把導航顯示出來
          $('#content').html(info);//把商品顯示出來
           },'json');
           })
           
           </script>
           <style>
           #content{
           margin-top: 10px
           }
           .money{
           position: absolute;
           left: 0;
           bottom: 0;
           height: 30px;
           line-height: 30px;
           color: #e22a40;
           font-weight: 700
           }
           .pro_img{
           position: relative;
           float: left;
           width: 220px;
           height: 220px;
           line-height: 220px;
           text-align: center;
           border: 1px solid #eee;
           cursor: pointer;
           font-size: 30px;
           white-space:normal; 
           overflow:hidden; /*超過部分不顯示*/
                text-overflow:ellipsis; /*超過部分用點點表示*/
                white-space:nowrap;*//*不換行
           }
           </style>
          </head>
          <body>
          <div id="nav"></div>
          <div id="content"></div>
          </body>
          </html>
          

          效果

          https://fjxasdf.github.io/daogou (github比較卡)

          遺留問題

          1. 導購頁面沒有樣式,不夠美觀。
          2. 沒有顯示商品圖片,由于excel文檔中有1萬條商品信息,把一萬張圖片顯示出來太卡了。

          主站蜘蛛池模板: 日韩人妻无码一区二区三区 | 日韩一区二区三区在线观看| 一区二区三区在线观看| A国产一区二区免费入口| 精品一区二区三区在线视频观看| 四虎精品亚洲一区二区三区| 色婷婷亚洲一区二区三区 | 中文字幕在线观看一区二区| 精品无码综合一区| 国产精品亚洲一区二区三区在线| 成人国内精品久久久久一区| 一本岛一区在线观看不卡| 中文字幕久久久久一区| 亚洲av日韩综合一区在线观看| 亚洲午夜一区二区电影院| 波多野结衣在线观看一区| 亚洲无人区一区二区三区| 亚洲色偷偷偷网站色偷一区| 无码人妻一区二区三区免费 | 精品综合一区二区三区| 亲子乱AV视频一区二区| 久久婷婷久久一区二区三区| 亚洲第一区精品日韩在线播放| 国产一区二区内射最近更新| 亚洲一区二区中文| 色欲AV无码一区二区三区| 真实国产乱子伦精品一区二区三区| 国模精品一区二区三区| 性色AV一区二区三区| 日本一区二区三区在线视频观看免费| 无码人妻精品一区二区蜜桃AV| 国产午夜精品一区理论片飘花| 亚洲香蕉久久一区二区| 国产免费伦精品一区二区三区| 亚洲天堂一区二区三区四区| 国产成人久久精品麻豆一区| 精品一区二区三区视频在线观看| 中文字幕在线播放一区| 日本香蕉一区二区三区| 日韩成人无码一区二区三区 | 亚洲一区欧洲一区|