【问题标题】:Extracting consecutive html fragments with enlive使用enlive提取连续的html片段
【发布时间】:2014-10-28 10:33:43
【问题描述】:

我需要抓取具有以下形式的 html:

<div id='content'>
    <h3>Headline1</h3>
    <div>Text1</div>
    <div>Text2</div>
    <div>Text3</div>
    <h3>Headline2</h3>
    <div>Text4</div>
    <div>Text5</div>
    <h3>Headline3</h3>
    <div>Text6</div>
    <div>... and so on ...</div>
</div>

我需要将标题标签之间的内容作为单独的块获取。所以从一个标题到下一个标题。不幸的是,没有所需范围的容器标签。

我尝试了片段选择器{[:h3] [:h3]},但不知何故,这只返回所有h3标签,它们之间没有标签: (({:tag :h3, :attrs nil, :content ("Headline1")}) ({:tag :h3, :attrs nil, :content ("Headline2")}) ({:tag :h3, :attrs nil, :content ("Headline3")}))

有效的是{[[:h3 (html/nth-of-type 1)]] [[:h3 (html/nth-of-type 2)]]}。这给了我第一个和第二个 h3-tag 之间的所有 html。但是,这并不能通过一个选择器为我提供所有所需的块。

enlive 完全可以做到这一点,还是我应该求助于正则表达式?

谢谢!

【问题讨论】:

  • 这个答案有帮助吗?:stackoverflow.com/questions/17157780/range-selectors-in-enlive PS 你不能使用正则表达式来解析 HTML:stackoverflow.com/questions/1732348/… 如果你尝试,你可能会唤醒古老的 ;-)
  • 不幸的是,它似乎没有帮助。当我用 {[:h3] [:h3]} 选择时,我只得到 h3 标签,而不是它们之间的节点。
  • 如果您添加一个 html sn-p 和您的选择器,我们也许可以编写一个匹配的。如果你包括你现在得到的输出,奖励积分。
  • 嗨,我已经用 sn-p 和更详细的描述编辑了帖子。谢谢!

标签: clojure enlive


【解决方案1】:

选择 div.content 中的所有内容,然后根据标签对其进行分区。

这里有一个更一般的概念,通过识别哪些是分隔符,哪些不是,将一系列事物分成段:

(defn separate*
  "Produces a sequence of (parent child*)*, coll must start with a parent"
  [child? coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [run (cons (first s)
                     (take-while child? (next s)))]
       (cons run (separate* child? (drop (count run) s)))))))

partition-by 非常相似,但总是在父级上拆分:

(partition-by keyword? [:foo 1 2 3 :bar :baz 4 5])
;; => ((:foo) (1 2 3) (:bar :baz) (4 5))

(separate* (compliment keyword?) [:foo 1 2 3 :bar :baz 4 5])
;; => ((:foo 1 2 3) (:bar) (:baz 4 5))

如果要在没有前导标题时处理:

(defn separate
  [parent? coll]
  (when-let [s (seq coll)]
    (if (parent? (first coll))
      (separate* (complement parent?) coll)
      (let [child? (complement parent?)
            run (take-while child? s)]
        (cons (cons nil run)
              (separate* child? (drop (count run) s)))))))

(separate keyword? [1 2 :foo 3 4])
;; => ((nil 1 2) (:foo 3 4))

回到手头的问题:

(def x [{:tag :h3 :content "1"}
        {:tag :div :content "A"}
        {:tag :div :content "B"}
        {:tag :h3 :content "2"}
        {:tag :div :content "C"}
        {:tag :div :content "D"}])

(def sections (separate #(= :h3 (:tag %)) x))
=> (({:content "1", :tag :h3}
     {:content "A", :tag :div
     {:content "B", :tag :div})
    ({:content "2", :tag :h3}
     {:content "C", :tag :div}
     {:content "D", :tag :div}))

如果我们不想保留 h3 标题的内容:

(map rest sections)
=> (({:content "A", :tag :div} {:content "B", :tag :div})
    ({:content "C", :tag :div} {:content "D", :tag :div}))

【讨论】:

  • 看起来棒极了。我会尽快测试它...谢谢!
猜你喜欢
  • 2023-03-09
  • 1970-01-01
  • 2014-02-18
  • 1970-01-01
  • 2013-01-04
  • 2012-03-08
  • 2013-06-27
  • 2014-12-02
  • 2013-05-15
相关资源
最近更新 更多