Blaugust 2026 Feeds - 以及我是如何用 Elisp 製作的 Light _ □ ✕
  • Posts
  • About
  • Debug
Post

Blaugust 2026 Feeds - 以及我是如何用 Elisp 製作的

Published
2026-08-02 15:40 UTC (precision: second)
Source feed
Taxodium [exclude this blog]
Link
https://taxodium.ink/blaugust-2026-feeds.html
Length
9905 runes
Extracted text
Blaugust 2026 Feeds 以及我是如何用 Elisp 製作的 生成了一份 Blaugust 2026 Feeds (更新於 2026-08-04,137 / 148),如果你需要可以點擊下載,然後导入到你的 RSS 閱讀器中。如果你想 參加 Blaugust,現在就是最好的時間! 這份 OMPL 文件裡沒有完全包擴 Blaugust 2026 – Meet the Mentors & Participant List 中的博客,因為有的博客無法訪問,有的在頁面上找不到訂閱連結,所以缺失了几個博客。如果在連結裡找不到你的博客,歡迎邮件聯系我,我很樂意更新進去。連結我會定期更新(可能每隔 7 天),你可以看連結後的更新日期了解是否更新過。 我主要是通過博客主頁中的 <link> 去查找博客的訂閱連結,一般可以在主頁的 <head> 裡找到: <head> <!-- RSS --> <link rel="alternate" type="application/rss+xml" title="your blog name" href="https://example.org/feed.xml" /> <!-- Atom --> <link rel="alternate" type="application/atom+xml" title="your blog name" href="https://example.org/atom.xml" /> </head> 但我發現有的博客并沒有添加,我建議有空可以加上去,這有助於 RSS 閱讀工具 (和我) 發現你博客的訂閱連結。 如果頁面上有 <link> ,可以打開頁面的 Web Developer Tools,执行這段 JS 找到頁面下所有的訂閱連結。 Array.from(document.querySelectorAll("link[rel=alternate][type*=application]")) .map(n => console.log(n.href)) 有的博客沒有設置 <link> ,我會在頁面中查找有沒有 RSS 訂閱連結;如果還是沒有,我也會嘗試在博客域名後面加上 /feed.xml 、 /rss.xml 看看能不能找到。大多時候我都能找到,但有的博客不提供我就沒辦法了,如果你的博客也是這樣的,我請求你加上 <link> 以及在頁面上提供明顯的訂閱連結。 另外 owls 也製作了一份 OPML 訂閱文件 ⸺ The Blaugust 2026 Post Firehose,他也分享了他是如何製作的,可以去看看。 截止目前 Blaugust 的參加者大約有 90 多個,如果一個個博客點開去找訂閱連結會花不少時間,所以我是用一段 Elisp 脚本去抓取的。為什麼是 Elisp?因為我不熟悉 Elisp,想著可以順便學習一下如何通過 Elisp 抓取 HTML 頁面信息。 大體的流程是這樣的: 拉取 Blaugust 2026 – Meet the Mentors & Participant List 的 HTML,解析所有參加者的域名和博客名字。 遍歷所有參加者的域名,获取主頁的 HTML,從中查找 <link ref="alternative" type="application/rss+xml"> 和 <link ref="alternative" type="application/atom+xml"> ,获取到訂閱 URL。(有的博客沒提供,或者有的做了爬虫防护,可能會获取不到;另外也可能會获取到多個連結,除了文章的,還有評論的;有的是 /feed/ ,還要拼接上域名。) 生成一份文件,包含所有訂閱數據,我會檢查一遍,处理找不到訂閱連結的博客。 用文件數據按照 OPML 格式生成 OPML 文件。 如果你也用 Emacs,你或許會對實現的 Elisp 代碼感興趣。 Elisp 代碼 ;;; init-blaugust.el --- Fetch Blaugust Participant -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (require 'plz) (require 'dom) (require 'rx) (require 'cl-lib) (defun spike-leung/blaugust--fetch-parse-dom (url then else) "Fetch URL and parse html to dom. THEN and ELSE are callback pass to plz, see `plz' for more info." (plz 'get url :as (lambda () (libxml-parse-html-region (point-min) (point-max))) :then then :else else)) (defun spike-leung/blaugust--get-feeds-from-link (url callback) "Get RSS or Atom feed links from URL. Call CALLBACK with a list of feeds." (spike-leung/blaugust--fetch-parse-dom url (lambda (dom) (let* ( ;; find all feed <link> tag (feed-link-tags (and dom (seq-filter (lambda (elt) (and (equal "alternate" (dom-attr elt 'rel)) (member (dom-attr elt 'type) '("application/rss+xml" "application/atom+xml")))) (dom-by-tag dom 'link)))) ;; get feed url from <link> (feeds (if feed-link-tags (mapcar (lambda (link-tag) (let ((href (dom-attr link-tag 'href))) (if (string-match-p (rx bos (or "https://" "http://")) href) href (url-expand-file-name href url)))) feed-link-tags) nil))) (funcall callback feeds))) ;; ignore error (lambda (_err) (message "Request failed: %S" _err) (funcall callback nil)))) (defun spike-leung/blaugust--save-blogroll (blogroll output-file start-time &optional old-blogroll) "Save BLOGROLL to OUTPUT-FILE. Calc duration with START-TIME. If OLD-BLOGROLL not nil, merge BLOGROLL with EXSITING-BLOGROLL." (let* ((elapsed (float-time (time-subtract (current-time) start-time))) (merged-blogroll (if old-blogroll (spike-leung/blaugust--merge-blogroll blogroll old-blogroll) blogroll)) (sorted-blogroll (sort (copy-sequence merged-blogroll) (lambda (a b) (string< (downcase (plist-get a :title)) (downcase (plist-get b :title))))))) (message "Collect finish (%.1fs). Write result to %s..." elapsed output-file) (make-directory (file-name-directory output-file) t) (with-temp-file output-file (insert (pp-to-string sorted-blogroll))) (message "Wrote blogroll to %s (%.1fs)" output-file elapsed))) (defun spike-leung/blaugust--merge-blogroll (new old) "Merge curated OLD blogroll into NEW by :domain. If a NEW entry has no :feeds but OLD has :feeds for the same domain, reuse OLD's :feeds. Also keep OLD entries not present in NEW." (let ((old-table (make-hash-table :test #'equal)) result) ;; init table (dolist (o old) (puthash (plist-get o :domain) o old-table)) (dolist (n new) ;; if n's feeds is nil while o's feeds not, merge (let* ((key (plist-get n :domain)) (old-entry (gethash key old-table)) (old-feeds (and old-entry (plist-get old-entry :feeds))) (new-feeds (plist-get n :feeds))) (when (and old-feeds (null new-feeds)) (message "Reusing curated feeds for %s" key) (setq n (plist-put (copy-sequence n) :feeds old-feeds))) (push n result) (remhash key old-table))) ;; Any old entries whose domain did not appear in new are pushed into result too. (maphash (lambda (_key o) (push o result)) old-table) ;; Reverse result because push builds it backwards. (nreverse result))) (defun spike-leung/blaugust--collect-feed-for-participant (participant callback) "Collect feeds for PARTICIPANT. Call CALLBACK with PARTICIPANT augmented by :feeds." (spike-leung/blaugust--get-feeds-from-link (plist-get participant :domain) (lambda (feeds) (funcall callback (plist-put participant :feeds feeds))))) (defun spike-leung/blaugust--collect-participant-links (participant-list-url callback) "Collect participant entries from PARTICIPANT-LIST-URL. Call CALLBACK with a list of plists (:title :link)." (spike-leung/blaugust--fetch-parse-dom participant-list-url (lambda (dom) (let ((participant-a-tags (and dom (mapcan (lambda (wp-block-list) (dom-by-tag wp-block-list 'a) ) (cdr (dom-by-class dom "wp-block-list")))))) (funcall callback (mapcar (lambda (atag) (let ((title (dom-text atag)) (domain (dom-attr atag 'href))) `(:title ,title :domain ,domain))) participant-a-tags)))) (lambda (_err) (funcall callback nil)))) (defun spike-leung/blaugust--collect-feeds (participant-list-url output-file) "Collect feeds from PARTICIPANT-LIST-URL. Save data to OUTPUT-FILE." (interactive "sParticipant List URL: \nFOutput File:") (let ((start-time (current-time)) (existing-blogroll (and (file-exists-p output-file) (ignore-errors (spike-leung/blaugust--read-blogroll output-file))))) (message "Start collecting Blaugust feeds...") (condition-case err (spike-leung/blaugust--collect-participant-links participant-list-url (lambda (participants) (let ((total (length participants)) (completed 0) (blogroll nil)) (if (zerop total) (message "No blogroll found.") (message "Collecting feeds from %d blogs..." total) (dolist (participant participants) (spike-leung/blaugust--collect-feed-for-participant participant (lambda (participant-with-feeds) (push participant-with-feeds blogroll) (cl-incf completed) (message "Collected %d/%d: %s" completed total (plist-get participant :title)) (when (= completed total) (spike-leung/blaugust--save-blogroll blogroll output-file start-time existing-blogroll))))))))) (error (message "Failed to fetch participant page: %s" participant-list-url) nil)))) (defun spike-leung/blaugust--read-blogroll (input-file) "Read INPUT-FILE as blogroll data." (seq-filter (lambda (feed) (not (null (plist-get feed :feeds)))) (with-temp-buffer (insert-file-contents input-file) (read (current-buffer))))) (defun spike-leung/blaugust--export-opml (title input-file output-file) "Export feeds from INPUT-FILE plist to OPML-formatted OUTPUT-FILE. TITLE is the title in opml file. Use `spike-leung/blaugust--collect-feeds' to generate INPUT-FILE." (declare (completion elfeed--mode-p)) (interactive "sTitle in OPML file: \nFInput file: \nFOutput OPML file: ") (let* ((feeds (spike-leung/blaugust--read-blogroll input-file))) (with-temp-file output-file (let ((standard-output (current-buffer))) (princ "<?xml version=\"1.0\"?>\n") (xml-print `((opml ((version . "1.0")) (head () (title () ,title)) (body () ,@(cl-loop for feed in feeds for url = (car (plist-get feed :feeds)) for domain = (plist-get feed :domain) for title = (or (plist-get feed :title) "") collect `(outline ((xmlUrl . ,url) (htmlUrl . ,domain) (title . ,title) (text . ,title)))))))))))) (defun spike-leung/blaugust--generate-elfeed-feeds (input-file) "Generate elfeed feeds from blaugust feeds. INPUT-FILE is the blaugust feeds generated by `spike-leung/blaugust--collect-feeds'." (interactive "FInput file:") (let* ((feeds (spike-leung/blaugust--read-blogroll input-file)) (existing-feed-urls (mapcar (lambda (feed) (cond ((stringp feed) feed) ((listp feed) (car feed)) (t nil))) elfeed-feeds)) (blaugust-elfeed-feeds (mapcar (lambda (feed) `( ;; feed url ,(car (plist-get feed :feeds)) ;;title :title ,(plist-get feed :title) ;; set no-update t as default ;; :no-update t ;; tags blaugust2026) ) feeds)) (existing-feeds (seq-filter (lambda (feed) (member (car feed) existing-feed-urls)) blaugust-elfeed-feeds)) (new-feeds (mapcar (lambda (feed) ;; add tag for new feeds (append feed '(new-feeds))) (seq-filter (lambda (feed) (not (member (car feed) existing-feed-urls))) blaugust-elfeed-feeds)))) (with-current-buffer (get-buffer-create "*blaugust-elfeed-feeds*") (erase-buffer) (insert ";; Already exist\n") (insert (pp-to-string existing-feeds)) (insert "\n;; New\n") (insert (pp-to-string new-feeds)) (lisp-mode) (display-buffer (current-buffer))))) (provide 'init-blaugust) ;;; init-blaugust.el ends here Elisp 代碼主要用到了: alphapapa/plz.el,用來發起請求,plz 提供了回調方法,也提供了多種返回體格式,讓处理請求更容易。 dom.el 是 Emacs 內置的,提供了一些处理 DOM 的方法: libxml-parse-html-region 解析 HTML dom-by-class 等方法查找 DOM dom-attr 等获取 DOM 信息 使用時: 用 spike-leung/blaugust--collect-feeds ,輸入 Blaugust 2026 – Meet the Mentors & Participant List 的連結和一個文件名,就會從連結拉取所有的 participant 的信息保存到文件中。 檢查文件中的信息 (可以通過 consult-line 和 embark-export 過濾出 :feeds nil 的部分),补全沒拉取到的博客信息。沒拉取到的博客,會顯示成 :feeds nil 。 用 spike-leung/blaugust--export-opml ,基於上面生成的文件生成 OPML 文件。 如果你用 elfeed,還可以調用 spike-leung/blaugust--generate-elfeed-feeds ,這會和己有的 elfeed-feeds 比對,顯示一個临時 buffer,列出哪些己經在 elfeed-feeds 裡、哪些是新的,數據格式和 elfeed-feeds 是一樣的,可以方便地添加到 elfeed 里 (默認會加上 blaugust2026 的 tag,對原來 elfeed-feeds 中沒有的還會加上 new-feeds 的 tag,便於過濾)。 感谢你的阅读! 欢迎 邮件 跟我分享你的想法 :) 你也可以 訂閱 我的博客,保持更新 :P 祝好,素未某面的读者。 純文本版本 原始 org 文件
Stored topics

Every stored assignment is listed, including rows below the current cutoff (0.75), so a missing tag can be explained.

Concept Code Score Meter Margin Origin Path
science and technology science-and-technology 0.97 3.53 direct science and technology
arts, culture, entertainment and media arts-culture-entertainment-and-media 0.92 2.39 direct arts, culture, entertainment and media
economy, business and finance economy-business-and-finance 0.84 1.66 ↑derived from 20000306 economy, business and finance
products and services products-and-services 0.84 1.66 ↑derived from 20000306 economy, business and finance > products and services
media and entertainment industry media-and-entertainment-industry 0.84 1.66 ↑derived from 20000306 economy, business and finance > products and services > media and entertainment industry
books and publishing books-and-publishing 0.84 1.66 direct economy, business and finance > products and services > media and entertainment industry > books and publishing
computing and information technology computing-and-information-technology 0.80 1.37 ↑derived from local:open-source economy, business and finance > products and services > computing and information technology
software and applications software-and-applications 0.80 1.37 ↑derived from local:open-source economy, business and finance > products and services > computing and information technology > software and applications
Open source open-source 0.80 1.37 direct economy, business and finance > products and services > computing and information technology > software and applications > Open source
Software development software-development 0.75 1.08 direct economy, business and finance > products and services > computing and information technology > software and applications > Software development
arts and entertainment arts-and-entertainment 0.55 0.22 ↑derived from 20000013 arts, culture, entertainment and media > arts and entertainment
literature literature 0.55 0.22 direct arts, culture, entertainment and media > arts and entertainment > literature
health health 0.52 0.10 ↑derived from 20000465 health
health treatment and procedure health-treatment-and-procedure 0.52 0.10 ↑derived from 20000465 health > health treatment and procedure
diet diet 0.52 0.10 direct health > health treatment and procedure > diet
technology and engineering technology-and-engineering 0.42 -0.33 ↑derived from 20001298 science and technology > technology and engineering
information technology and computer science information-technology-and-computer 0.42 -0.33 ↑derived from 20001298 science and technology > technology and engineering > information technology and computer science
artificial intelligence artificial-intelligence 0.42 -0.33 direct science and technology > technology and engineering > information technology and computer science > artificial intelligence
film industry film-industry 0.39 -0.46 direct economy, business and finance > products and services > media and entertainment industry > film industry
education education 0.36 -0.58 direct education
streaming service streaming-service 0.29 -0.92 direct economy, business and finance > products and services > media and entertainment industry > streaming service
lifestyle and leisure lifestyle-and-leisure 0.25 -1.09 direct lifestyle and leisure
society society 0.25 -1.10 ↑derived from 20001300 society
fundamental rights fundamental-rights 0.25 -1.10 ↑derived from 20001300 society > fundamental rights
privacy privacy 0.25 -1.10 direct society > fundamental rights > privacy
Information security information-security 0.21 -1.32 direct science and technology > technology and engineering > information technology and computer science > Information security
culture culture 0.14 -1.77 direct arts, culture, entertainment and media > culture
Self-hosting self-hosting 0.14 -1.84 direct economy, business and finance > products and services > computing and information technology > Self-hosting
animation animation 0.13 -1.91 direct arts, culture, entertainment and media > arts and entertainment > animation
Cryptography cryptography 0.13 -1.93 direct science and technology > technology and engineering > information technology and computer science > Cryptography
sport sport 0.11 -2.10 ↑derived from 20001183 sport
competition discipline competition-discipline 0.11 -2.10 ↑derived from 20001183 sport > competition discipline
eSports esports 0.11 -2.10 direct sport > competition discipline > eSports
lifestyle lifestyle 0.09 -2.33 ↑derived from 20001257 lifestyle and leisure > lifestyle
house and home house-and-home 0.09 -2.33 ↑derived from 20001257 lifestyle and leisure > lifestyle > house and home
gardening gardening 0.09 -2.33 direct lifestyle and leisure > lifestyle > house and home > gardening
music music 0.07 -2.59 direct arts, culture, entertainment and media > arts and entertainment > music
human interest human-interest 0.07 -2.62 ↑derived from 20000498 human interest
award and prize award-and-prize 0.07 -2.62 direct human interest > award and prize
leisure leisure 0.05 -2.94 ↑derived from local:tabletop-gaming lifestyle and leisure > leisure
game game 0.05 -2.94 ↑derived from local:tabletop-gaming lifestyle and leisure > leisure > game
Tabletop gaming tabletop-gaming 0.05 -2.94 direct lifestyle and leisure > leisure > game > Tabletop gaming
religion religion 0.05 -3.05 direct religion
disease and condition disease-and-condition 0.04 -3.06 ↑derived from 20000458 health > disease and condition
mental health and disorder mental-health-and-disorder 0.04 -3.06 direct health > disease and condition > mental health and disorder
podcast podcast 0.04 -3.12 direct economy, business and finance > products and services > media and entertainment industry > podcast
natural science natural-science 0.04 -3.12 ↑derived from 20000719 science and technology > natural science
biology biology 0.04 -3.12 direct science and technology > natural science > biology
mass media mass-media 0.04 -3.26 ↑derived from 20000051 arts, culture, entertainment and media > mass media
television television 0.04 -3.26 direct arts, culture, entertainment and media > mass media > television
environment environment 0.03 -3.47 direct environment
video game video-game 0.03 -3.57 ↑derived from local:mmorpg lifestyle and leisure > leisure > game > video game
MMORPG mmorpg 0.03 -3.57 direct lifestyle and leisure > leisure > game > video game > MMORPG
labour labour 0.02 -3.72 direct labour
consumer goods consumer-goods 0.02 -3.89 ↑derived from 20001160 economy, business and finance > products and services > consumer goods
handicrafts handicrafts 0.02 -3.89 direct economy, business and finance > products and services > consumer goods > handicrafts
scientific research scientific-research 0.02 -3.99 ↑derived from 20000739 science and technology > scientific research
scientific exploration scientific-exploration 0.02 -3.99 ↑derived from 20000739 science and technology > scientific research > scientific exploration
space exploration space-exploration 0.02 -3.99 direct science and technology > scientific research > scientific exploration > space exploration
social sciences social-sciences 0.02 -4.14 ↑derived from 20000747 science and technology > social sciences
history history 0.02 -4.14 direct science and technology > social sciences > history
board game board-game 0.01 -4.30 direct lifestyle and leisure > leisure > game > board game
crime, law and justice crime-law-and-justice 0.01 -4.33 ↑derived from 20000086 crime, law and justice
crime crime 0.01 -4.33 ↑derived from 20000086 crime, law and justice > crime
cyber crime cyber-crime 0.01 -4.33 direct crime, law and justice > crime > cyber crime
family family 0.01 -4.36 direct society > family
visual arts visual-arts 0.01 -4.39 ↑derived from 20000036 arts, culture, entertainment and media > arts and entertainment > visual arts
photography photography 0.01 -4.39 direct arts, culture, entertainment and media > arts and entertainment > visual arts > photography
politics and government politics-and-government 0.01 -4.44 direct politics and government
travel and tourism travel-and-tourism 0.01 -4.66 direct lifestyle and leisure > leisure > travel and tourism
exercise and fitness exercise-and-fitness 0.01 -4.66 direct lifestyle and leisure > wellness > exercise and fitness
wellness wellness 0.01 -4.66 ↑derived from 20001239 lifestyle and leisure > wellness
social media social-media 0.01 -4.81 ↑derived from local:blogging arts, culture, entertainment and media > mass media > social media
Blogging blogging 0.01 -4.81 direct arts, culture, entertainment and media > mass media > social media > Blogging
hobby hobby 0.01 -4.85 ↑derived from 20000568 lifestyle and leisure > leisure > hobby
food and drink enthusiasm food-and-drink-enthusiasm 0.01 -4.85 direct lifestyle and leisure > leisure > hobby > food and drink enthusiasm
public health public-health 0.01 -5.07 direct health > public health
card game card-game 0.01 -5.14 direct lifestyle and leisure > leisure > game > card game
climate change climate-change 0.01 -5.17 direct environment > climate change
weather weather 0.00 -5.34 ↑derived from 20001128 weather
weather forecast weather-forecast 0.00 -5.34 direct weather > weather forecast
conflict, war and peace conflict-war-and-peace 0.00 -6.17 direct conflict, war and peace
disaster, accident and emergency incident disaster-accident-and-emergency-incident 0.00 -6.73 direct disaster, accident and emergency incident

← Back to posts

feedtagger 0.1.0-dev model: modernbert-base-zeroshot-v2.0/int8@all-s256 660 posts 0 unclassified min score 0.75