整合營銷服務商

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

          免費咨詢熱線:

          使用Python構建Markdown到HTML的轉換管道

          markdown中寫下你的文章,并使用Python將它們轉換成HTML-作者Florian Dahlitz,于2020年5月18日(15分鐘)

          介紹

          幾個月前,我想開通自己的博客,而不是使用像Medium這樣的網站。這是一個非常基礎的博客,所有的文章都是HTML形式的。然而,有一天,我突然產生了自己編寫Markdown到HTML生成器的想法,最終這將允許我用markdown來編寫文章。此外,為它添加諸如估計閱讀時間之類的擴展特性會更容易。長話短說,我實現了自己的markdown到HTML生成器,我真的很喜歡它!

          在本系列文章中,我想向您展示如何構建自己的markdown到HTML生成器。該系列由三部分組成:

          • 第一部分(本文)介紹了整個管線的實現。

          • 第二部分通過一個模塊擴展了實現的管線,該模塊用于計算給定文章的預計閱讀時間。

          • 第三部分演示如何使用管線生成自己的RSS摘要。

          這三部分中使用的代碼都可以在GitHub上找到。

          備注:我的文章中markdown到HTML生成器的想法基于Anthony Shaw文章中的實現。

          項目構建

          為了遵循本文的內容,您需要安裝幾個軟件包。我們把它們放進requirements.txt文件。

          Markdown是一個包,它允許您將markdown代碼轉換為HTML。之后我們用Flask產生靜態文件。

          但在安裝之前,請創建一個虛擬環境,以避免Python安裝出現問題:

          激活后,您可以使用pip安裝requirements.txt中的依賴。

          很好!讓我們創建幾個目錄來更好地組織代碼。首先,我們創建一個app目錄。此目錄包含我們提供博客服務的Flask應用程序。所有后續目錄都將在app目錄內創建。其次,我們創建一個名為posts的目錄。此目錄包含要轉換為HTML文件的markdown文件。接下來,我們創建一個templates目錄,其中包含稍后使用Flask展示的模板。在templates目錄中,我們再創建兩個目錄:

          posts包含生成的HTML文件,這些文件與應用程序根目錄中posts目錄中的文件相對應。

          shared包含在多個文件中使用的HTML文件。

          此外,我們還創建了一個名為services的目錄。該目錄將包含我們在Flask應用程序中使用的模塊,或者為它生成某些東西。最后,創建一個名為static的目錄帶有兩個子目錄images和css。自定義CSS文件和文章的縮略圖將存儲在此處。

          您的最終項目結構應如下所示:

          令人驚嘆!我們完成了一般的項目設置。我們來看看Flask的設置。

          Flask設置

          路由

          我們在上一節安裝了Flask。但是,我們仍然需要一個Python文件來定義用戶可以訪問的端點。在app目錄中創建main.py并將以下內容復制到其中。

          該文件定義了一個具有兩個端點的基礎版Flask應用程序。用戶可以使用/route訪問第一個端點返回索引頁,其中列出了所有文章。

          第二個端點是更通用的端點。它接受post的名稱并返回相應的HTML文件。

          接下來,我們通過向app目錄中添加一個__init__.py,將其轉換為一個Python包。此文件為空。如果您使用UNIX計算機,則可以從項目的根目錄運行以下命令:

          模板

          現在,我們創建兩個模板文件index.html以及layout.html,都存儲在templates/shared目錄中。這個layout.html模板將用于單個博客條目,而index.html模板用于生成索引頁,從中我們可以訪問每個帖子。讓我們從index.html模板開始。

          它是一個基本的HTML文件,其中有兩個元標記、一個標題和兩個樣式表。注意,我們使用一個遠程樣式表和一個本地樣式表。遠程樣式表用于啟用Bootstrap[1]類。第二個是自定義樣式。我們晚點再定義它們。

          HTML文件的主體包含一個容器,其中包含Jinja2[2]邏輯,用于為每個post生成Bootstrap卡片[3]。您是否注意到我們不直接基于變量名訪問這些值,而是需要將[0]添加到其中?這是因為文章中解析的元數據是列表。實際上,每個元數據元素都是由單一元素組成的列表。我們稍后再看。到目前為止,還不錯。讓我們看看layout.html模板。

          如你所見,它比前一個短一點,簡單一點。文件頭與index.html文件很相似,除了我們有不同的標題。當然,我們可以共用一個模板,但是我不想讓事情變得更復雜。

          body中的容器僅定義一個h1標記。然后,我們提供給模板的內容被插入并呈現。

          樣式

          正如上一節所承諾的,我們將查看自定義CSS文件style.css. 我們在static/css中找到該文件,并根據需要自定義頁面。下面是我們將用于基礎示例的內容:

          我不喜歡Bootstrap中blockquotes的默認外觀,所以我們在左側添加了一點間距和邊框。此外,blockquote段落底部的頁邊空白將被刪除。不刪除的話看起來很不自然。

          最后但并非最不重要的是,左右兩邊的填充被刪除。由于兩邊都有額外的填充,縮略圖沒有正確對齊,所以在這里刪除它們。

          到現在為止,一直都還不錯。我們完成了關于Flask的所有工作。讓我們開始寫一些帖子吧!

          寫文章

          正如標題所承諾的,你可以用markdown寫文章-是的!在寫文章的時候,除了保證正確的markdown格式外,沒有其他需要注意的事情。

          在完成本文之后,我們需要在文章中添加一些元數據。此元數據添加在文章之前,并由三個破折號分隔開來---。下面是一個示例文章(post1.md)的摘錄:

          注意:您可以在GitHub庫的app/posts/post1.md中找到完整的示例文章。

          在我們的例子中,元數據由標題、副標題、類別、發布日期和index.html中卡片對應縮略圖的路徑組成.

          我們在HTML文件中使用了元數據,你還記得嗎?元數據規范必須是有效的YAML。示例形式是鍵后面跟著一個冒號和值。最后,冒號后面的值是列表中的第一個也是唯一的元素。這就是我們通過模板中的索引運算符訪問這些值的原因。

          假設我們寫完了文章。在我們可以開始轉換之前,還有一件事要做:我們需要為我們的帖子生成縮略圖!為了讓事情更簡單,只需從你的電腦或網絡上隨機選取一張圖片,命名它為placeholder.jpg并把它放到static/images目錄中。GitHub存儲庫中兩篇文章的元數據包含一個代表圖像的鍵值對,值是placeholder.jpg。

          注意:在GitHub存儲庫中,您可以找到我提到的兩篇示例文章。

          markdown到HTML轉換器

          最后,我們可以開始實現markdown to HTML轉換器。因此,我們使用我們在開始時安裝的第三方包Markdown。我們先創建一個新模塊,轉換服務將在其中運行。因此,我們在service目錄中創建了converter.py。我們一步一步看完整個腳本。您可以在GitHub存儲庫中一次查看整個腳本。

          首先,我們導入所需的所有內容并創建幾個常量:

          ROOT指向我們項目的根。因此,它是包含app的目錄。

          POSTS_DIR是以markdown編寫的文章的路徑。

          TEMPLATE_DIR分別指向對應的templates目錄。

          BLOG_TEMPLATE_文件存儲layout.html的路徑。

          INDEX_TEMPLATE_FILE是index.html

          BASE_URL是我們項目的默認地址,例如。https://florian-dahlitz.de.默認值(如果不是通過環境變量DOMAIN提供的話)是http://0.0.0.0:5000。

          接下來,我們創建一個名為generate_entries的新函數。這是我們定義的唯一一個轉換文章的函數。

          在函數中,我們首先獲取POSTS_DIR目錄中所有markdown文件的路徑。pathlib的awesome glob函數幫助我們實現它。

          此外,我們定義了Markdown包需要使用的擴展。默認情況下,本文中使用的所有擴展都隨它的安裝一起提供。

          注意:您可以在文檔[4]中找到有關擴展的更多信息。

          此外,我們實例化了一個新的文件加載程序,并創建了一個在轉換項目時使用的環境。隨后,將創建一個名為all_posts的空列表。此列表將包含我們處理后的所有帖子。現在,我們進入for循環并遍歷POSTS_DIR中找到的所有文章。

          我們啟動for循環,并打印當前正在處理的post的路徑。如果有什么東西出問題了,這尤其有用。然后我們就知道,哪個文章的轉換失敗了。

          接下來,我們在默認url之后增加一部分。假設我們有一篇標題為“面向初學者的Python”的文章。我們將文章存儲在一個名為python-for-beginners.md,的文件中,因此生成的url將是http://0.0.0.0:5000/posts/python-for-beginners。

          變量url_html存儲的字符串與url相同,只是我們在末尾添加了.html。我們使用此變量定義另一個稱為target_file.的變量。變量指向存儲相應HTML文件的位置。

          最后,我們定義了一個變量md,它表示markdown.Markdown的實例,用于將markdown代碼轉換為HTML。您可能會問自己,為什么我們沒有在for循環之前實例化這個實例,而是在內部實例化。當然,對于我們這里的小例子來說,這沒有什么區別(只是執行時間稍微短一點)。但是,如果使用諸如腳注之類的擴展來使用腳注,則需要為每個帖子實例化一個新實例,因為腳注添加后就不會從此實例中刪除。因此,如果您的第一篇文章使用了一些腳注,那么即使您沒有明確定義它們,所有其他文章也將具有相同的腳注。

          讓我們轉到for循環中的第一個with代碼塊。

          實際上,with代碼塊打開當前post并將其內容讀入變量content。之后調用_md.convert將以markdown方式寫入的內容轉換為HTML。隨后,env環境根據提供的模板BLOG_TEMPLATE_FILE(即layout.html如果你還記得的話)渲染生成的HTML。

          第二個with 代碼塊用于將第一個with 代碼塊中創建的文檔寫入目標文件。

          以下三行代碼從元數據中獲取發布日期(被發布的日期),將其轉換為正確的格式(RFC 2822),并將其分配回文章的元數據。此外,生成的post_dict被添加到all_posts列表中。

          我們現在出了for循環,因此,我們遍歷了posts目錄中找到的所有posts并對其進行了處理。讓我們看看generate_entries函數中剩下的三行代碼。

          我們按日期倒序對文章進行排序,所以首先顯示最新的文章。隨后,我們將文章寫到模板目錄一個新創建的index.html文件中。別把index.html錯認為templates/shared目錄中的那個。templates/shared目錄中的是模板,這個是我們要使用Flask服務的生成的。

          最后我們在函數generate_entries之后添加以下if語句。

          這意味著如果我們通過命令行執行文件,它將調用generate_entries函數。

          太棒了,我們完成了converter.py腳本!讓我們從項目的根目錄運行以下命令來嘗試:

          您應該看到一些正在轉換的文件的路徑。假設您編寫了兩篇文章或使用了GitHub存儲庫中的兩篇文章,那么您應該在templates目錄中找到三個新創建的文件。首先是index.html,它直接位于templates目錄中,其次是templates/posts目錄中的兩個HTML文件,它們對應于markdown文件。

          最后啟動Flask應用程序并轉到http://0.0.0.0:5000。

          總結

          太棒了,你完成了這個系列的第一部分!在本文中,您已經學習了如何利用Markdown包創建自己的Markdown to HTML生成器。您實現了整個管線,它是高度可擴展的,您將在接下來的文章中看到這一點。

          希望你喜歡這篇文章。一定要和你的朋友和同事分享。如果你還沒有,考慮在Twitter上關注我@DahlitzF或者訂閱我的通知,這樣你就不會錯過任何即將發表的文章。保持好奇心,不斷編碼!

          參考文獻

          Bootstrap (http://getbootstrap.com/)

          Primer on Jinja Templating (https://realpython.com/primer-on-jinja-templating/)

          Bootstrap Card (https://getbootstrap.com/docs/4.4/components/card/)

          Python-Markdown Extensions (https://python-markdown.github.io/extensions/)

          Tweet

          英文原文:https://florian-dahlitz.de/blog/build-a-markdown-to-html-conversion-pipeline-using-python
          譯者:阿布銩


          本文接著上文(Golang GinWeb框架6-綁定請求字符串/URI/請求頭/復選框/表單類型)繼續探索GinWeb框架


          靜態文件服務

          package main
          ?
          import (
            "github.com/gin-gonic/gin"
            "log"
            "net/http"
            "os"
          )
          ?
          func main() {
            router := gin.Default()
            cwd, _ := os.Getwd()  //獲取當前文件目錄
            log.Printf("當前項目路徑:%s", cwd)
            router.Static("/static", cwd) //提供靜態文件服務器, 第一個參數為相對路徑,第二個參數為根路徑, 這個路徑一般放置css,js,fonts等靜態文件,前端html中采用/static/js/xxx或/static/css/xxx等相對路徑的方式引用
            router.StaticFS("/more_static", http.Dir("./")) //將本地文件樹結構映射到前端, 通過瀏覽器可以訪問本地文件系統, 模擬訪問:http://localhost:8080/more_static
            router.StaticFile("/logo.png", "./resources/logo.png")  //StaticFile提供單靜態單文件服務, 模擬訪問:http://localhost:8080/log.png
          ?
            // Listen and serve on 0.0.0.0:8080
            router.Run(":8080")
          }


          返回文件數據

          package main
          ?
          import (
            "github.com/gin-contrib/cors"
            "github.com/gin-gonic/gin"
            "net/http"
          )
          ?
          func main() {
            router := gin.Default()
            router.Use(cors.Default())
          ?
            router.GET("/local/file", func(c *gin.Context) {
              c.File("./main.go")
            })
          ?
          ?
            // A FileSystem implements access to a collection of named files.
            // The elements in a file path are separated by slash ('/', U+002F)
            // characters, regardless of host operating system convention.
            // FileSystem接口, 要求實現文件的訪問的方法, 提供文件訪問服務根路徑的HTTP處理器
            var fs http.FileSystem = http.Dir("./")  //將本地目錄作為文件服務根路徑
            router.GET("/fs/file", func(c *gin.Context) {
              c.FileFromFS("main.go", fs)  //將文件服務系統下的文件數據返回
            })
            router.Run(":8080")
          }
          /*
          模擬訪問文件數據:
          curl http://localhost:8080/local/file
          ?
          模擬訪問文件系統下的文件數據:
          curl http://localhost:8080/fs/file
          */


          用文件讀出器提供文件數據服務

          package main
          ?
          import (
            "github.com/gin-gonic/gin"
            "net/http"
          )
          ?
          func main() {
            router := gin.Default()
            router.GET("/someDataFromReader", func(c *gin.Context) {
              response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
              if err != nil || response.StatusCode != http.StatusOK {  //請求鏈接中的文件出現錯誤時, 直接返回服務不可用
                c.Status(http.StatusServiceUnavailable)
                return
              }
          ?
              reader := response.Body  //用響應體內容構造一個文件讀出器
              defer reader.Close()
              contentLength := response.ContentLength
              contentType := response.Header.Get("Content-Type")
          ?
              extraHeaders := map[string]string{
                "Content-Disposition": `attachment; filename="gopher.png"`,
              }
              // DataFromReader writes the specified reader into the body stream and updates the HTTP code.
              // func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {}
              // DataFromReader方法將指定的讀出器reader中的內容, 寫入http響應體流中, 并更新響應碼, 響應頭信息等
              c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
            })
            router.Run(":8080")
          }
          /*
          模擬訪問:
          curl http://localhost:8080/someDataFromReader
          */


          HTML渲染


          使用LoadHTMLGlob()方法或LoadHTMLFiles()方法

          package main
          ?
          import (
            "github.com/gin-gonic/gin"
            "net/http"
          )
          ?
          func main() {
            router := gin.Default()
            //LoadHTMLGlob方法以glob模式加載匹配的HTML文件, 并與HTML渲染器結合
            router.LoadHTMLGlob("templates/*")
            //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
            router.GET("/index", func(c *gin.Context) {
              //HTML方法設置響應碼, 模板文件名, 渲染替換模板中的值, 設置響應內容類型Content-Type "text/html"
              c.HTML(http.StatusOK, "index.tmpl", gin.H{
                "title": "Main website",
              })
            })
            router.Run(":8080")
          }
          /*
          模擬測試:
          curl http://localhost:8080/index
          */

          增加模板文件, templates/index.tmpl

          <html>
            <h1>
              {{ .title }}
            </h1>
          </html>

          使用不同文件夾下的相同文件名的模板文件

          func main() {
            router := gin.Default()
            router.LoadHTMLGlob("templates/**/*")
            router.GET("/posts/index", func(c *gin.Context) {
              c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
                "title": "Posts",
              })
            })
            router.GET("/users/index", func(c *gin.Context) {
              c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
                "title": "Users",
              })
            })
            router.Run(":8080")
          }

          posts目錄下添加模板文件, templates/posts/index.tmpl

          {{ define "posts/index.tmpl" }}
          <html><h1>
            {{ .title }}
          ?</h1>
          <p>Using posts/index.tmpl</p>
          </html>
          {{ end }}

          users目錄下添加模板文件, templates/users/index.tmpl

          {{ define "users/index.tmpl" }}
          <html><h1>
            {{ .title }}
          </h1>
          <p>Using users/index.tmpl</p>
          </html>
          {{ end }}

          自定義模板渲染器


          你也可以使用你自定義的HTML模板渲染器, 需要自定義模板文件file1, file2等

          package main
          ?
          import (
          ?  "github.com/gin-gonic/gin"
            "html/template"
            "net/http"
          )
          ?
          func main() {
            router := gin.Default()
            //template.ParseFiles(文件1,文件2...)創建一個模板對象, 然后解析一組模板,使用文件名作為模板的名字
            // Must方法將模板和錯誤進行包裹, 返回模板的內存地址 一般用于變量初始化,比如:var t = template.Must(template.New("name").Parse("html"))
            html := template.Must(template.ParseFiles("file1", "file2"))
            router.SetHTMLTemplate(html) //關聯模板和HTML渲染器
          ?
            router.GET("/index", func(c *gin.Context) {
              //HTML方法設置響應碼, 模板文件名, 渲染替換模板中的值, 設置響應內容類型Content-Type "text/html"
              c.HTML(http.StatusOK, "file1", gin.H{
                "title": "Main website",
              })
            })
            router.Run(":8080")
          }


          自定義分隔符


          你可以自定義分隔符, 模板中默認的分隔符是{{ }}, 我們也可以修改, 比如下面增加一對中括號

            r := gin.Default()
            r.Delims("{[{", "}]}")
            r.LoadHTMLGlob("/path/to/templates")


          自定義模板方法


          詳見 示例代碼.

          模板中與后端都定義好模板方法, 模板渲染時執行該方法, 類似過濾器方法, 比如時間格式化操作

          package main
          ?
          import (
            "fmt"
            "html/template"
            "net/http"
            "time"
          ?
            "github.com/gin-gonic/gin"
          ?)
          ?
          func formatAsDate(t time.Time) string {
            year, month, day := t.Date()  //Date方法返回年,月,日
            return fmt.Sprintf("%d%02d/%02d", year, month, day)  //格式化時間
          }
          ?
          func main() {
            router := gin.Default()
            router.Delims("{[{", "}]}") //自定義模板中的左右分隔符
            //SetFuncMap方法用給定的template.FuncMap設置到Gin引擎上, 后面模板渲染時會調用同名方法
            //FuncMap是一個map,鍵名關聯方法名, 鍵值關聯方法, 每個方法必須返回一個值, 或者返回兩個值,其中第二個是error類型
            router.SetFuncMap(template.FuncMap{
              "formatAsDate": formatAsDate,
            })
            router.LoadHTMLFiles("./testdata/template/raw.tmpl") //加載單個模板文件并與HTML渲染器關聯
          ?
            router.GET("/raw", func(c *gin.Context) {
              c.HTML(http.StatusOK, "raw.tmpl", gin.H{
                "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
              })
            })
          ?
            router.Run(":8080")
          }
          ?
          /*
          模擬測試:
          curl http://localhost:8080/raw
          */

          定義模板文件: raw.tmpl

          Date: {[{.now | formatAsDate}]}

          時間格式化結果:

          Date: 2017/07/01


          多個模板


          Gin默認只使用一個html.Template模板引擎, 也可以參考多模板渲染器使用類似Go1.6的塊級模板block template功能.

          模板相關詳情請參考官方template包


          參考文檔


          Gin官方倉庫:https://github.com/gin-gonic/gin



          END已結束

          歡迎大家留言, 訂閱, 交流哦!


          往期回顧


          Golang GinWeb框架6-XML/JSON/YAML/ProtoBuf等渲染

          Golang GinWeb框架5-綁定請求字符串/URI/請求頭/復選框/表單類型

          Golang GinWeb框架4-請求參數綁定和驗證

          Golang GinWeb框架3-自定義日志格式和輸出方式/啟禁日志顏色

          Golang GinWeb框架2-文件上傳/程序panic崩潰后自定義處理方式

          Golang GinWeb框架-快速入門/參數解析

          Golang與亞馬遜對象存儲服務AmazonS3快速入門

          Golang+Vue實現Websocket全雙工通信入門

          GolangWeb編程之控制器方法HandlerFunc與中間件Middleware

          Golang連接MySQL執行查詢并解析-告別結構體

          Golang的一種發布訂閱模式實現

          Golang 并發數據沖突檢測器(Data Race Detector)與并發安全

          Golang"驅動"MongoDB-快速入門("快碼加鞭")

          互式文檔是一種創建Shiny apps的新途徑。交互式文檔是一種包含Shiny控件與輸出的 R Markdown文件, 你可以在 markdown中寫報告,并且作為app來啟動它。

          本文主要闡述如何使用R Markdown寫報告。

          與本文配套的文獻 Introduction to interactive documents, 將向你展示如何通過將R Markdown 報告轉變成為帶有Shiny組件的交互式文檔。

          R Markdown

          R Markdown是通過R語言制作動態文檔的文件格式。R Markdown文檔在markdown中完成,其中包含嵌入的R代碼,如下圖:

          ---
          title: R Markdown
          output: html_document
          ---
          
          This is an R Markdown document. Markdown is a simple formatting syntax which allows you to author HTML, PDF, and MS Word documents. For more details on how to use R Markdown, see <http://rmarkdown.rstudio.com>.
          
          When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
          
          ```{r}
          summary(cars)
          ```
          
          You can also embed plots:
          
          ```{r, echo=FALSE}
          plot(cars)
          ```
          
          Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot.

          R Markdown文檔編輯需要 rmarkdown包,rmarkdown安裝需要RStudio編輯器環境,但是你可以以github途徑來下載rmarkdown,并安裝。

          devtools:install_github("rmarkdown", "rstudio")

          R Markdown是資源代碼豐富并高可用的文件,你可以將通過一下兩種方式改變R Markdown文件格式。

          1. knit

          主站蜘蛛池模板: 香蕉免费看一区二区三区| 一区二区三区四区电影视频在线观看| 日韩精品无码视频一区二区蜜桃| 国产一区中文字幕| 精品三级AV无码一区| 亚洲熟妇av一区二区三区| 色狠狠色噜噜Av天堂一区| 一区二区三区杨幂在线观看| 香蕉一区二区三区观| 在线成人综合色一区| 国产主播福利一区二区| 国模无码一区二区三区| 人妻互换精品一区二区| 一区二区三区在线观看中文字幕 | 亚洲熟女www一区二区三区| 国产免费一区二区三区不卡| 亚洲AV无码一区东京热久久| 蜜臀AV一区二区| 夜色阁亚洲一区二区三区| 99久久精品国产免看国产一区| 国产一区二区内射最近更新| 久久福利一区二区| 日韩一区二区三区电影在线观看| 国产午夜精品一区二区| 国产福利91精品一区二区| 日本在线视频一区二区| 国产精品 一区 在线| 亚洲一区在线观看视频| 琪琪see色原网一区二区| 亚洲av午夜福利精品一区| 国产剧情一区二区| 久久福利一区二区| 成人国产精品一区二区网站公司| 武侠古典一区二区三区中文| 亚洲av午夜福利精品一区| 国产一区二区三区播放心情潘金莲| 久久亚洲一区二区| 日本丰满少妇一区二区三区| 精品亚洲A∨无码一区二区三区| 无码人妻一区二区三区av| 国产精品视频一区二区噜噜|