Есть в рунете замечательный сайт -
www.stihi.ru, на котором помимо графоманов довольно много и интересных, хороших авторов. Если какой-то автор вам особенно понравился, то все его стихи выкачать и аккуратно сложить по папочкам (в соответствии с классификацией автора) можно с помощью такого кода на Common Lisp:
(defun load-poem (url dir &key verbose)
(html:with-parse-html (page url :encoding "cp1251")
(let* ((index (xpath:find-single-node page "/html/body/index"))
(title (xpath:find-string index "h1")))
(when verbose
(format t "Load ~A (~A)~%" url title))
(with-open-file (out (make-pathname :directory (pathname-directory dir)
:name title
:type "txt")
:direction :output :if-exists :supersede)
(iter (for br in (xpath:find-list index "div[@class='text']/br"))
(write-line (string-trim #(#\Newline)
(xtree:text-content (xtree:prev-sibling br)))
out))))))
(defun load-all-poems (url target &key verbose (recursive t))
(ensure-directories-exist target)
(html:with-parse-html (page url :encoding "cp1251")
(iter (for node in (xpath:find-list page "//ul/li/a"))
(load-poem (puri:merge-uris (puri:parse-uri (xtree:attribute-value node "href"))
url)
target
:verbose verbose))
(when recursive
(iter (for node in (xpath:find-list page "//div[@id='bookheader']/a"))
(let ((title (xtree:text-content node)))
(when verbose
(format t "~%Load book ~A~%" title))
(load-from-stihiru (puri:merge-uris (puri:parse-uri (xtree:attribute-value node "href"))
url)
(merge-pathnames (make-pathname :directory (list :relative title))
target)
:recursive nil
:verbose verbose))))))
Выделение содержательной части из html-страниц весьма тривиально и реализуется за счёт использования языка запросов XPath. Теперь скачать, например, все стихи моей жены можно таким вызовом:
(load-all-poems #U"http://www.stihi.ru/avtor/mari_mishon"
#P"/var/kubart/poems/"
При написании данного кода я обнаружил, что libxml2 не может правильно определить кодировку страниц стихиры, так что пришлось несколько доработать
cl-libxml2, добавив возможность явно указывать кодировку html-страниц.
Я так понимаю, ты автор cl-libxml2? А что побудило к написанию собственного биндинга вместо использования pure-lisp cxml/chtml? Я так понимаю, какие-то их недостатки?
ОтветитьУдалитьЯ лично использовал chtml для массового парсинга кучи html-ок (задача стояла выдрать из них тексты для последующей обработки), и в целом с моей задачей он вполне справлялся. При работе, правда, обнаружилась пара мелких багов, проявляющихся довольно редко (один - при парсинге документов, содержащих очень много (тысячи) entity references, второй - если документ кончался внутри тега - типа <br в конце документа). Баги были успешно пофикшены и патчи отправлены (и приняты).
> Я так понимаю, ты автор cl-libxml2?
ОтветитьУдалитьДа.
> А что побудило к написанию собственного биндинга
Во-первых, мне была нужна поддержка XSLT, которой на момент создания биндинга ещё не было. Впрочем, она и сейчас не в самом лучшем состоянии с точки зрения производительности.
Во-вторых, для меня работа с XML была очень даже критической и мне нужна была библиотека, которая гарантированно покроет все мои нужды. Я не мог полагаться на cxml, но знал что libxml2 сможет обеспечить меня любым необходимым сервисом.
В-третьих, CXML размазана по целому ряду библиотек, ей определённо не хватает консолидации. И многим компонентам не хватает практической обкатки, интерфейс той же Plexippus XPath не совсем удобен.
archimag, а что с cl-libxml2 на гуглокоде?
ОтветитьУдалитьasdf пишет-
sbcl gzip: stdin: not in gzip format
@pscat
ОтветитьУдалитьВсё нормально. asdf такого писать не может, только asdf-install. Но asdf-install никогда нормально не работало. А там не gzip, а bz2. Используй quicklisp.
Где можно найти изменения, которые вы внесли в cl-libxml2?
ОтветитьУдалить@orivej
ОтветитьУдалитьНу, дык, https://github.com/archimag/cl-libxml2
Спасибо, не заметил почему-то, что 0.3.4 вышла раньше.
ОтветитьУдалитьЯ новичок в Common Lisp, я правильно пониманию, что для включения имён из iterate в пространство имён этого кода необходимо сделать:
(require 'cl-libxml2)
(defpackage stihiru
(:use cl)
(:use iterate)
(:export load-all-poems))
(in-package :stihiru)
и затем вызывать #'stihiru:load-all-poems, или есть менее изолирующий, но более короткий способ? (Но не (in-package :iterate), потому что так можно включить только один пакет.)
Да, функция load-from-stihiru оказывается не определена.
Нашёл ответ, это use-package. Я замечаю, что раньше имена пакетов писали, начиная с ', затем с :, теперь у вас и в других местах встречаю '#:. С чем связан выбор последнего?
ОтветитьУдалить@orivej
ОтветитьУдалитьЛучше такие вопросы на lisper.ru задавать :) Здесь я не всегда имею возможности ответить, тем более, если вопрос не по теме поста )
'symbol - просто символ
:symbol - keyworld, символ из пакета keyword
'#:symbol - внепакетный символ, не замусоривает пакеты ненужными символами
А как вам такая разработка?
ОтветитьУдалитьhttp://www.alex-y28.narod.ru/pilesos.html
ОтветитьУдалитьзабыл линк