Цитаты дня fortune-mod с bash.im
bash.im умер, статья оставлена на память
tl;dr: скачайте архив с кодом.
Метод⌗
Я встречался с решениями, которые парсят и загружают цитаты онлайн. Минусы такого подхода в том, что каждый раз при запуске fortune-mod хост обращается к сайту, да и пул цитат ограничен RSS-лентой.
Метод из данной статьи загружает сразу большое количество цитат (потенциально все), записывает их в текстовый файл в формате, понимаемом fortune-mod, после чего формирует специальный файл-указатель и сохраняет это всё в системе. При вызове
fortune bash-ru
цитаты читаются из локального файла, что гораздо быстрее, не зависит от наличия доступа в Интернет, да и готовые файлы можно распространить между своими системами (или подарить другу :).
Парсер⌗
Я взял за основу готовый код парсера, за авторством aruseni. После небольшой обработки получилось следующее:
#!/usr/bin/env python
# bash.im fortune parser
# original author: aruseni (https://github.com/aruseni)
import sys
import errno
import urllib2
import datetime
from bs4 import BeautifulSoup
class Parser:
user_agent = (
"Mozilla/5.0 "
"(X11; Ubuntu; Linux x86_64; rv:30.0) "
"Gecko/20100101 Firefox/30.0"
)
def __init__(self, start_page, end_page):
self.start_page = start_page
self.end_page = end_page
def get_url(self, page_number):
return "http://bash.im/index/%s" % page_number
def fetch_page(self, page_number):
req = urllib2.Request(
url=self.get_url(page_number),
headers={"User-Agent": self.user_agent}
)
f = urllib2.urlopen(req)
return f.read()
def parse_all_pages(self):
for page_number in xrange(self.start_page, self.end_page + 1):
self.parse_quotes(page_number)
def parse_quotes(self, page_number):
html = self.fetch_page(page_number)
soup = BeautifulSoup(html, "lxml")
quote_divs = soup.find_all("div", class_="quote")
for quote_div in quote_divs:
quote = {}
text_div = quote_div.find("div", class_="text")
# Skipping advertisement
if not text_div:
continue
# The quote text divs contain strings of text and
# <br> elements. Here all contents of a text div
# are joined with any elements replaced by \n.
quote["text"] = "".join(
map(
lambda x: x if isinstance(x, unicode) else "\n",
text_div.contents
)
)
quote["text"] = quote["text"].strip()
self.write_quote(quote)
def write_quote(self, quote):
line = quote.get("text")
quotes_file = open ("bash-ru","a")
quotes_file.write(line.encode('utf8') + "\n" + "%" + "\n")
quotes_file.close()
if __name__ == "__main__":
arguments = sys.argv
if (
len(arguments) == 3
and arguments[1].isdigit()
and arguments[2].isdigit()
):
start_page = int(arguments[1])
end_page = int(arguments[2])
if start_page > 0 and end_page >= start_page:
p = Parser(start_page, end_page)
p.parse_all_pages()
else:
sys.stderr.write("Please check the page numbers\n")
sys.exit(errno.EINVAL)
else:
sys.stderr.write("Invalid arguments\n")
sys.exit(errno.EINVAL)
Этот скрипт принимает в качестве аргументов номер первой страницы и номер последней страницы для обработки:
./parse.py 1 10
и формирует текстовый файл bash-ru в формате, поддерживаемом фортункой.
Makefile⌗
Для автоматизации дальнейшей обработки я написал Makefile:
# Fortunes processing
POSSIBLE += $(shell ls -1 | egrep -v '\.dat|Makefile|README|parse|requirements' | sed -e 's/$$/.dat/g')
all: ${POSSIBLE}
%.dat : %
@strfile $< $@
install: all
mv bash-ru bash-ru.dat /usr/share/games/fortunes
clean:
rm *.dat bash-ru
После отработки парсера, запускайте
make all
для формирования файла-указателя bash-ru.dat и
# make install
для установки файла фортунок в систему. Необходимы права суперпользователя!
После установки можно вызывать fortune bash-ru, прописывать эту команду в алиасы, смеяться (по желанию).