HTML извлечение ссылок MLA с помощью Enlive и Clojure
Моя цель - извлечь и проанализировать серию библиографических ссылок с веб-страницы для последующего ввода в базу данных. Все ссылки в формате MLA. Это должно быть общим решением для всех экземпляров библиографий в формате MLA, и оно должно работать не только на веб-странице, указанной ниже.
Вот мой код попытки, который не работает:
(use '[net.cgrand.enlive-html])
(def ^:dynamic *base-url* "https://www.impacttest.com/research/?Clinical-Research-Database-4")
(def ^:dynamic *ref-selector* [:div#content_1 :ul :li])
(defn fetch-url [url]
(html-resource (java.net.URL. url)))
(defn references []
(select (fetch-url *base-url*) *ref-selector*))
(def ^:dynamic *ref-regex* #"\s([A-Z]{1}[\w|\s]+)[,|\.]")
(def ^:dynamic *ref-modifier* `(remove :content))
(defmacro extract-re [node re modifier]
`(doseq [seqs (map :content (node))]
(re-find re (apply str (modifier seqs)))))
(extract-re references *ref-regex* *ref-modifier*)
(macroexpand-1 '(extract-re references *ref-regex* *ref-modifier*))
Я хотел бы макрос extract-re
создать doseq
который запускает регулярное выражение (re-find
) на всех оживляющих узлах. Есть две переменные, которые нужно изменить: одна - это само регулярное выражение, а другая - модификатор, который модифицирует оживляющий узел до его обработки. Без модификатора регулярное выражение будет соответствовать как авторам, так и некоторым заголовкам. Я пытался написать функцию, но не смог заставить ее работать в общем случае, поэтому я думаю, что макрос - это путь.
Что касается ссылок MLA, я думаю, что проще использовать модификатор на оживляющем узле, чем выполнять все операции извлечения с помощью регулярных выражений, хотя я могу ошибаться в этом. Я не могу думать о том, как сделать регулярное выражение, которое будет соответствовать только названию или только авторам.
Итак, как мне передать модификатор макросу и правильно ли он выполнить? Я не до конца понимаю детали цитирования макросов, так что я могу быть далек от того, как я написал макрос для начала, или даже если макрос необходим.
2 ответа
Ради иллюстрации...
Примечание: я буду префикс оживить с HTML
(require '[net.cgrand.enlive-html :as html])
Выход из (references)
представляет собой последовательность отдельных ссылочных элементов, таких как
(def data-sample
'{:tag :li, :attrs nil,
:content
("\n\t\t\t\t\t\t\t\t\t\t\t\t\t"
{:tag :strong, :attrs nil,
:content
("AMPAR peptide values *snip*.")}
" Dambinova SA, Shikuev, Weissman JD, Mullins, JD. "
{:tag :em, :attrs nil, :content ("Military Medicine.")}
" 2013, 178 (3):285-290.\t\t\t\t\t\t\t\t\t\t\t\t")})
Вы заметите, что заголовок статьи выделен жирным шрифтом, а журнал - курсивом, поэтому мы могли бы использовать селекторы, чтобы хотя бы извлечь их. Но поскольку изменения в форматировании используются для визуального разделения компонентов, они также обеспечивают разделение данных.
(defn trimmed-text-only [html-data]
(as-> html-data x
(html/select x [html/text-node])
(map clojure.string/trim x)
(remove empty? x)))
(trimmed-text-only data-sample)
;=>
("AMPAR peptide values *snip*."
"Dambinova SA, Shikuev, Weissman JD, Mullins, JD."
"Military Medicine."
"2013, 178 (3):285-290.")
Это уже делает компоненты очевидными, но обратите внимание, что каждый из них разделен периодом с периодами, не используемыми в компонентах. Таким образом, мы также можем полностью игнорировать форматирование и делить его на периоды с дополнительным преимуществом удаления этих периодов.
(defn extract-major-reference-components
[html-data]
(as-> html-data x
(trimmed-text-only x)
(apply str x)
(clojure.string/split x #"\.")
(zipmap [:title :authors :journal :issue-ref] x)))
(extract-major-reference-components data-sample)
;=>
{:title "AMPAR peptide values *snip*"
:authors "Dambinova SA, Shikuev, Weissman JD, Mullins, JD",
:journal "Military Medicine",
:issue-ref "2013, 178 (3):285-290"}
Теперь вы можете отобразить эту функцию извлечения на последовательность ссылок. С выходными картами вы можете выполнять дальнейшие преобразования с обновлением и регулярными выражениями, например, чтобы отделить отдельных авторов или год, номер выпуска и страницы от номера-ссылки.
Есть много проблем с этим кодом.
'(use [net.cgrand.enlive-html])
Это не вносит библиотеку, она создает буквальный список и ничего с этим не делает:
user> (class '(use [net.cgrand.enlive-html]))
clojure.lang.PersistentList
это фактически не-оп.
(def ^:dynamic *ref-modifier* `(remove :content))
Это создает список из двух элементов, а не "модификатор" любого вида.
(defmacro extract-re [node re modifier]
`(doseq [seqs (map :content (node))]
(re-find re (apply str (modifier seqs)))))
Здесь вы используете синтаксическую кавычку, но внутри нее никогда ничего не заключаете в кавычки. Макрос никак не использует свои аргументы.
Вы, кажется, хотите подать заявку modifier
как если бы это была функция (это даже не начинает происходить, см. приведенные выше проблемы с цитированием), но, как мы видим в реальном вызове, modifier
это список из двух элементов, который может вызвать ошибку при вызове.
В заключение, doseq
работает только для побочных эффектов и всегда возвращает ноль. Блок oseq не использует значение, сгенерированное re-find
, так что доза дала тело, по сути, бездействует.
Кроме того, я вижу сомнительную полезность в использовании динамических объявлений var для переменных, которые будут предоставлены в качестве явных аргументов функции.
Учитывая все эти проблемы, я думаю, что мы ближе к тому, что работает:
(use 'net.cgrand.enlive-html)
(def ^:dynamic *base-url*
"https://www.impacttest.com/research/?Clinical-Research-Database-4")
(def ^:dynamic *ref-selector* [:div#content_1 :ul :li])
(defn fetch-url [url]
(html-resource (java.net.URL. url)))
(defn references []
(select (fetch-url *base-url*) *ref-selector*))
(def ^:dynamic *ref-regex* #"\s([A-Z]{1}[\w|\s]+)[,|\.]")
(def ^:dynamic *ref-modifier* (partial remove :content))
(defn extract-re [node re modifier]
(doall
(for [sq (map :content (node))]
(re-find re (apply str (modifier sq))))))
и в действии:
user> (extract-re references *ref-regex* *ref-modifier*)
([" Dambinova SA," "Dambinova SA"] [" Zuckerman SL," "Zuckerman SL"] [" Conklin HM," "Conklin HM"] [" Covassin T," "Covassin T"] [" Maerlender A," "Maerlender A"] [" Fedor A," "Fedor A"] [" Resch J," "Resch J"] [" Elbin RJ," "Elbin RJ"] [" Rabinowitz AR," "Rabinowitz AR"] [" Kinnaman KA," "Kinnaman KA"] [" Tsushima WT," "Tsushima WT"] [" Amonette WE," "Amonette WE"] [" Lovell MR," "Lovell MR"] [" Schatz P," "Schatz P"] [" McGrath N," "McGrath N"] [" Kontos AP," "Kontos AP"] [" AB," "AB"] [" Meehan WP," "Meehan WP"] [" Rieger BP," "Rieger BP"] [" Solomon GS," "Solomon GS"] [" Sandel NK," "Sandel NK"] [" Schatz P," "Schatz P"] [" Schatz P," "Schatz P"] [" Lebrun CM," "Lebrun CM"] [" Brooks B," "Brooks B"] [" Meehan WP," "Meehan WP"] [" Fakhran S," "Fakhran S"] [" Cole WR," "Cole WR"] [" Tsushima M," "Tsushima M"] [" Zuckerman SL," "Zuckerman SL"] [" JK," "JK"] [" Covassin T," "Covassin T"] [" Moser RS," "Moser RS"] [" Mayers LB," "Mayers LB"] [" McAllister TW," "McAllister TW"] [" Meehan WP 3rd," "Meehan WP 3rd"] [" Neal MT," "Neal MT"] [" Lau BC," "Lau BC"] [" Kontos AP," "Kontos AP"] [" Gardner A," "Gardner A"] [" Elbin RJ," "Elbin RJ"] [" Wolf EG," "Wolf EG"] [" Reddy CC," "Reddy CC"] [" Moser RS," "Moser RS"] [" Guerriero RM," "Guerriero RM"] [" Deibert E," "Deibert E"] [" Wiebe DJ," "Wiebe DJ"] [" Baillargeon A," "Baillargeon A"] [" Erdal K." "Erdal K"] [" Maugans TA," "Maugans TA"] [" Iverson GL," "Iverson GL"] [" Ponsford J," "Ponsford J"] [" Schatz P," "Schatz P"] [" Mulligan I," "Mulligan I"] [" Echlin PS," "Echlin PS"] [" McLeod TC," "McLeod TC"] [" Zuckerman SL," "Zuckerman SL"] [" Kontos AP," "Kontos AP"] [" Zuckerman SL," "Zuckerman SL"] [" Schatz P," "Schatz P"] [" Kontos AP," "Kontos AP"] [" Covassin T," "Covassin T"] [" Covassin T," "Covassin T"] [" Duhaime AC," "Duhaime AC"] [" Echemendia RJ," "Echemendia RJ"] [" Ramanathan DM," "Ramanathan DM"] [" Meehan WP 3rd," "Meehan WP 3rd"] [" Krol AL," "Krol AL"] [" Turgeon C," "Turgeon C"] [" Randolph C." "Randolph C"] [" Barlow M," "Barlow M"] [" Schatz P," "Schatz P"] [" Moser RS," "Moser RS"] [" Broglio SP," "Broglio SP"] [" Thomas DG," "Thomas DG"] [" Allen BJ," "Allen BJ"] [" Solomon GS," "Solomon GS"] [" Ponsford J," "Ponsford J"] [" Johnson EW," "Johnson EW"] [" Randolph C," "Randolph C"] [" Elbin RJ," "Elbin RJ"] [" Broglio SP," "Broglio SP"] [" Kontos AP," "Kontos AP"] [" Lau BC," "Lau BC"] [" Lau BC," "Lau BC"] [" Hettich T," "Hettich T"] [" Elbin T," "Elbin T"] [" Maerlender A," "Maerlender A"] [" Kontos AP," "Kontos AP"] [" Talavage TM," "Talavage TM"] [" Meehan WP 3rd," "Meehan WP 3rd"] [" Lange RT," "Lange RT"] [" Covassin T," "Covassin T"] [" Schatz P." "Schatz P"] [" Lange RT," "Lange RT"] [" Pardini JE," "Pardini JE"] [" Echlin PS," "Echlin PS"] [" Schatz P," "Schatz P"] [" Echlin PS," "Echlin PS"] [" Keightley ML," "Keightley ML"] [" McGrath N." "McGrath N"] [" Covassin T," "Covassin T"] [" Pontifex MB," "Pontifex MB"] [" AB," "AB"] [" Casson IR," "Casson IR"] [" McCrory P," "McCrory P"] [" Covassin T," "Covassin T"] [" Bruce JM," "Bruce JM"] [" Covassin T," "Covassin T"] [" Lovell M." "Lovell M"] [" Lau B," "Lau B"] [" Nance ML," "Nance ML"] [" Peterson SE," "Peterson SE"] [" Lovell M." "Lovell M"] [" Broglio SP," "Broglio SP"] [" Broglio SP," "Broglio SP"] [" Colvin AC," "Colvin AC"] [" Reddy CC," "Reddy CC"] [" Solomon GS," "Solomon GS"] [" Covassin T," "Covassin T"] [" Majerske CW," "Majerske CW"] [" Lovell MR," "Lovell MR"] [" AB," "AB"] [" Tsushima WT," "Tsushima WT"] [" Miller JR," "Miller JR"] [" Slobounov S," "Slobounov S"] [" Mihalik JP," "Mihalik JP"] [" Covassin T," "Covassin T"] [" Lovell MR," "Lovell MR"] [" Stoller KP." "Stoller KP"] [" Broglio SP," "Broglio SP"] [" Moser RS," "Moser RS"] [" Iverson G." "Iverson G"] [" Fazio VC," "Fazio VC"] [" Swanik CB," "Swanik CB"] [" Broglio SP," "Broglio SP"] [" Covassin T," "Covassin T"] [" Broglio SP," "Broglio SP"] [" Chen JK," "Chen JK"] [" Van Kampen DA," "Van Kampen DA"] [" Broglio SP," "Broglio SP"] [" Pellman EJ," "Pellman EJ"] [" Pellman EJ," "Pellman EJ"] [" Schatz P," "Schatz P"] [" Biasca N," "Biasca N"] [" Collins M," "Collins M"] [" Lovell MR," "Lovell MR"] [" Lovell MR," "Lovell MR"] [" Iverson GL," "Iverson GL"] [" Cantu RC," "Cantu RC"] [" McClincy MP," "McClincy MP"] [" Schatz P," "Schatz P"] [" Iverson GL," "Iverson GL"] [" Van Kampen DA," "Van Kampen DA"] [" Lovell M," "Lovell M"] [" Mihalik JP," "Mihalik JP"] [" Moser RS," "Moser RS"] [" Broshek DK," "Broshek DK"] [" Grove R," "Grove R"] [" McCrea M," "McCrea M"] [" McCrory P," "McCrory P"] [" Iverson GL," "Iverson GL"] [" Lovell MR," "Lovell MR"] [" Bruce JM," "Bruce JM"] [" Pellman EJ," "Pellman EJ"] [" Iverson GL," "Iverson GL"] [" Lovell MR," "Lovell MR"] [" Kontos A," "Kontos A"] [" Collins MW," "Collins MW"] [" Iverson GL," "Iverson GL"] [" Lovell M," "Lovell M"] [" Field M," "Field M"] [" Covassin T," "Covassin T"] [" Iverson GL," "Iverson GL"] [" Lovell MR," "Lovell MR"] [" Collins MW," "Collins MW"] [" Lovell MR," "Lovell MR"] [" Collins MW," "Collins MW"] [" Collins MW," "Collins MW"] [" Collins MW," "Collins MW"] [" Maroon JC," "Maroon JC"] [" Lovell MR," "Lovell MR"] [" Lovell MR." "Lovell MR"] [" Aubry M," "Aubry M"] [" Grindel SH," "Grindel SH"] [" Collins MW," "Collins MW"] [" Lovell MR," "Lovell MR"] [" Collins MW," "Collins MW"] [" Lovell MR," "Lovell MR"])