我不会对此过于兴奋,但周末我一直在用 Nokogiri 做其他事情。它有点令人上瘾。我想趁 Nokogiri 还在我脑海里的时候看看嵌入代码。
我对此感兴趣是因为我想看到 Discourse 被新闻和博客网站更广泛地使用。如果那样的话,我可以想象新的网站所有者会对当前的嵌入功能感到沮丧。这里有一个改进它的想法:
向 EmbeddableHost 模型添加两个新的可选属性:
target_selector:包含要嵌入的内容的外部 CSS 选择器
exclude_selectors:要从 target_selector 选择的内容中排除的 CSS 选择器列表。
应该在管理员/嵌入页面的每个 Embeddable Host 行上添加一个“配置”按钮。单击该按钮会打开一个类似于“电子邮件/预览摘要”页面的页面。
配置主机页面将有一个表单,用于输入主机的 target_selector 和 exclude_selectors 设置,以及一个 URL 字段,允许使用提供的 URL 值针对特定网页进行测试。测试基本上只是使用提供的 target_selector 和 exclude_selectors 值运行 TopicEmbed.parse_html,然后显示结果。
parse_html 代码的更改很容易测试。这是一种可能的方法。请注意,此代码仅为概念验证:
编辑到 topic_embed.rb (discourse/app/models/topic_embed.rb at main · discourse/discourse · GitHub)
###########################################################################
# `target_selector` 和 `exclude_selectors` 最好从域的 `EmbeddableHost` 记录中找到
# 这些特定的设置用于测试 boingboing.net
target_selector = 'article'
exclude_selectors = ['.article-header, .share-comments-container', '.boing-single-post-rev-content', '.next-post-list-container', '.boing-end-of-article-container-on-single-post-pages']
if defined?(target_selector) && target_selector.present?
read_doc = article_content(html, target_selector, exclude_selectors)
else
# 如果主机未设置 `target_selector`,则回退到 Readability
read_doc = Readability::Document.new(html, opts)
end
###########################################################################
为了在不创建新类的情况下进行测试,这里是将一个基本的 article_content 方法添加到 TopicEmbed 类中:
def self.article_content(html, target_selector, exclude_selectors = [])
doc = Nokogiri::HTML(html)
# 删除注释和脚本标签
doc.xpath('//comment()').each { |i| i.remove }
doc.css("script, style").each { |i| i.remove }
# 获取 target_selector 的 NodeSet
# 如果返回的集合为空,则可能回退到使用 Readability
selected_nodes = doc.css(target_selector)
# 排除节点
unless exclude_selectors.empty?
selected_nodes.css(*exclude_selectors).each do |node|
node.remove
end
end
# 处理图像大小,可能需要改进
selected_nodes.css('img').each do |img|
img.remove_attribute('width')
img.remove_attribute('height')
end
# 仅为好玩,如果 iframe 的源是允许的,则允许它们
# 使用 `[data-sanitized="true"]` 防止 iframe 在 remove_empty_nodes 步骤中被剥离
allowed_iframe_sources = SiteSetting.allowed_iframes.split('|')
selected_nodes.css('iframe').each do |iframe|
allowed = allowed_iframe_sources.any? do |allowed_source|
iframe['src'].start_with?(allowed_source)
end
if allowed
iframe['data-sanitized'] = 'true'
iframe['width'] = '690'
iframe['height'] = '388'
else
iframe.remove
end
end
# 删除空的 'p' 和 'div' 节点
selected_nodes.css('p', 'div').each do |node|
node.remove if node.content.strip.empty? && !node.at_css('iframe[data-sanitized="true"]')
end
# 将节点转换为字符串并返回一个带有 `content` 方法的对象
content = selected_nodes.to_s
OpenStruct.new(content: content)
end
我很有信心,只需在多个域上进行一些调整就可以使其正常工作。到目前为止,我从 BBS 获得的结果一直很好。
目标是提出一个网站所有者可以轻松理解和自行配置的东西。通过这种方法,target_selector 越具体,配置 exclude_selectors 就越容易。例如,对于 WordPress 网站,如果选择 .entry-content 作为 target_selector,则无需进一步配置。如果网站所有者想获得比基本 .entry-content HTML 更多的内容,他们可以在配置主机页面上找出如何做到这一点。
唯一真正的问题是对于 HTML 非常不一致的主机。这种情况可以通过将 Ruby Readability 保留为备用方案来处理。