【问题标题】:turning a html structure into a Clojure Structure将 html 结构转换为 Clojure 结构
【发布时间】:2017-08-08 17:26:05
【问题描述】:

我有一个 html 页面,其中有一个结构,我想将其转换为 Clojure 数据结构。我对如何以惯用的方式处理这个问题感到困惑

这是我的结构:

<div class=“group”>
  <h2>title1<h2>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading1</h3>
    <a href=“path1” />
  </div>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading2</h3>
    <a href=“path2” />
  </div>
</div>
<div class=“group”>
  <h2>title2<h2>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading3</h3>
    <a href=“path3” />
  </div>
</div>

我想要的结构:

'(
[“Title1” “subhead1” “path1”]
[“Title1” “subhead2” “path2”]
[“Title2” “subhead3” “path3”]
[“Title3” “subhead4” “path4”]
[“Title3” “subhead5” “path5”]
[“Title3” “subhead6” “path6”]
)

标题的重复是故意的。

我读过David Nolan’s enlive tutorial。如果组和子组之间存在奇偶性,这提供了一个很好的解决方案,但在这种情况下它可以是随机的。

感谢您的建议。

【问题讨论】:

  • 您的 HTML 中有错别字吗? &lt;h2&gt;title1&lt;h2&gt;&lt;h2&gt;title2&lt;h2&gt; 似乎应该分别是 &lt;h2&gt;title1&lt;/h2&gt;&lt;h2&gt;title2&lt;/h2&gt;

标签: html clojure enlive


【解决方案1】:

您可以使用Hickory 进行解析,然后Clojure 有一些非常好的工具可以将解析后的HTML 转换为您想要的形式:

(require '[hickory.core :as html])

(defn classifier [tag klass]
  (comp #{[:element tag klass]} (juxt :type :tag (comp :class :attrs))))

(def group? (classifier :div "“group”"))
(def subgroup? (classifier :div "“subgroup”"))
(def path? (classifier :a nil))
(defn identifier? [tag] (classifier tag nil))

(defn only [x]
  ;; https://stackoverflow.com/a/14792289/5044950
  {:pre [(seq x)
         (nil? (next x))]}
  (first x))

(defn identifier [tag element]
  (->> element :content (filter (identifier? tag)) only :content only))

(defn process [data]
  (for [group (filter group? (map html/as-hickory (html/parse-fragment data)))
        :let [title (identifier :h2 group)]
        subgroup (filter subgroup? (:content group))
        :let [subheading (identifier :h3 subgroup)]
        path (filter path? (:content subgroup))]
    [title subheading (:href (:attrs path))]))

例子:

(require '[clojure.pprint :as pprint])

(def data
"<div class=“group”>
  <h2>title1</h2>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading1</h3>
    <a href=“path1” />
  </div>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading2</h3>
    <a href=“path2” />
  </div>
</div>
<div class=“group”>
  <h2>title2</h2>
  <div class=“subgroup”>
    <p>unused</p>
    <h3>subheading3</h3>
    <a href=“path3” />
  </div>
</div>")

(pprint/pprint (process data))
;; (["title1" "subheading1" "“path1”"]
;;  ["title1" "subheading2" "“path2”"]
;;  ["title2" "subheading3" "“path3”"])

【讨论】:

  • 谢谢。这非常简洁。我一直在努力寻找正确的节点,并使用 enlive。我不知道山核桃。我会阅读它的文档 - 它看起来非常有用,并且是 clojure 库的好名字。
【解决方案2】:

解决方案可以分为两部分

  • 解析:使用clojure html parser 或任何其他解析器对其进行解析。
  • 自定义数据结构:修改解析后的html,如果需要,可以使用clojure.walk

【讨论】:

    【解决方案3】:

    您可以使用the tupelo.forest library 解决此问题。这是一个带注释的单元测试,显示了该方法。您可以找到更多信息 in the API docs 以及 the unit teststhe example demos。其他文档即将发布。

    (dotest
      (with-forest (new-forest)
        (let [html-str        "<div class=“group”>
                                  <h2>title1</h2>
                                  <div class=“subgroup”>
                                    <p>unused</p>
                                    <h3>subheading1</h3>
                                    <a href=“path1” />
                                  </div>
                                  <div class=“subgroup”>
                                    <p>unused</p>
                                    <h3>subheading2</h3>
                                    <a href=“path2” />
                                  </div>
                                </div>
                                <div class=“group”>
                                  <h2>title2</h2>
                                  <div class=“subgroup”>
                                    <p>unused</p>
                                    <h3>subheading3</h3>
                                    <a href=“path3” />
                                  </div>
                                </div>"
    
              enlive-tree     (->> html-str
                                java.io.StringReader.
                                en-html/html-resource
                                first)
              root-hid        (add-tree-enlive enlive-tree)
              tree-1          (hid->hiccup root-hid)
    
              ; Removing whitespace nodes is optional; just done to keep things neat
              blank-leaf-hid? (fn fn-blank-leaf-hid? ; whitespace pred fn
                                [hid]
                                (let [node (hid->node hid)]
                                  (and (contains-key? node ::tf/value)
                                    (ts/whitespace? (grab ::tf/value node)))))
              blank-leaf-hids (keep-if blank-leaf-hid? (all-leaf-hids)) ; find whitespace nodes
              >>              (apply remove-hid blank-leaf-hids) ; delete whitespace nodes found
              tree-2          (hid->hiccup root-hid)
              >>              (is= tree-2 [:html
                                           [:body
                                            [:div {:class "“group”"}
                                             [:h2 "title1"]
                                             [:div {:class "“subgroup”"}
                                              [:p "unused"]
                                              [:h3 "subheading1"]
                                              [:a {:href "“path1”"}]]
                                             [:div {:class "“subgroup”"}
                                              [:p "unused"]
                                              [:h3 "subheading2"]
                                              [:a {:href "“path2”"}]]]
                                            [:div {:class "“group”"}
                                             [:h2 "title2"]
                                             [:div {:class "“subgroup”"}
                                              [:p "unused"]
                                              [:h3 "subheading3"]
                                              [:a {:href "“path3”"}]]]]])
    
              ; find consectutive nested [:div :h2] pairs at any depth in the tree
              div-h2-paths    (find-paths root-hid [:** :div :h2])
              >>              (is= (format-paths div-h2-paths)
                                [[{:tag :html}
                                  [{:tag :body}
                                   [{:class "“group”", :tag :div}
                                    [{:tag :h2, :tupelo.forest/value "title1"}]]]]
                                 [{:tag :html}
                                  [{:tag :body}
                                   [{:class "“group”", :tag :div}
                                    [{:tag :h2, :tupelo.forest/value "title2"}]]]]])
    
              ; find the hid for each top-level :div (i.e. "group"); the next-to-last (-2) hid in each vector
              div-hids        (mapv #(idx % -2) div-h2-paths)
              ; for each of div-hids, find and collect nested :h3 values
              dif-h3-paths    (vec
                                (lazy-gen
                                  (doseq [div-hid div-hids]
                                    (let [h2-value  (find-leaf-value div-hid [:div :h2])
                                          h3-paths  (find-paths div-hid [:** :h3])
                                          h3-values (it-> h3-paths (mapv last it) (mapv hid->value it))]
                                      (doseq [h3-value h3-values]
                                        (yield [h2-value h3-value]))))))
              ]
          (is= dif-h3-paths
            [["title1" "subheading1"]
             ["title1" "subheading2"]
             ["title2" "subheading3"]])
    
          )))
    

    【讨论】:

      猜你喜欢
      • 2011-12-21
      • 2011-05-17
      • 2012-03-05
      • 2020-09-12
      • 2019-05-17
      • 2014-12-31
      • 2021-12-10
      • 1970-01-01
      • 2017-09-27
      相关资源
      最近更新 更多