Парсинг XML в Scala на примере комментариев Disqus

8 декабря 2014

Примем за рабочую теорию, что вы знаете, что такое Disqus. На сайты эта штука, как правило, устанавливается путем размещения небольшого JS-кода. Есть также плагины для различных CMS, но по моему опыту, они тормозные и пользоваться ими нельзя. В итоге складывается нехорошая ситуация — комментарии на сайте есть, а поисковыми системами они индексируются либо очень плохо (Google), либо вообще никак (всеми остальными ПС).

Я решил попробовать разобраться в этой проблеме. А что, если воспользоваться возможностью Disqus экспортировать все комментарии в XML, пропарсить этот XML и сгенерировать куски HTML кода для дальнейшего их заинклуживания на стороне сервера внутри <div id="disqus_thread">? Сказано — сделано:

#!/usr/bin/env scala

import scala.xml._
import java.nio.file._
import java.nio.charset._
import java.io._

if(args.size < 3) {
  println("Usage: comments2html <infile> <outdir> <domain>")
  sys.exit(1)
}

val Array(inputFile, outputDir, domainName, _*) = args

val domain = s"http://$domainName/"

val xml = XML.loadFile(inputFile)
val threadsXml = xml \\ "thread"
val postsXml = xml \\ "post"

val dsqid = "@{http://disqus.com/disqus-internals}id"

val threadsMap =
  threadsXml.map{
    n => ((n \ dsqid).text.toLong, (n \ "link").text)
  }.filter{
    case (_,u) => u.startsWith(domain) && u.indexOf("?") < 0
  }.toMap

val postsMap =
  postsXml.map{
    n => ( (n \ "thread" \ dsqid).text.toLong,
           (n \ "createdAt").text,
           (n \ "author" \ "name").text,
           (n \ "message").text,
           n
         )
  }.filter{
    case t =>
      (! (t._5 \ "isDeleted").text.toBoolean ) &&
        (! (t._5 \ "isSpam").text.toBoolean )
  }.groupBy(_._1).filter{
    case (k,v) => threadsMap.get(k).isDefined
  }.map{
    case (k,v) =>
      ( threadsMap(k),
        v.map(t => (t._2, t._3, t._4) ).toArray.sortBy(_._1)
      )
  }

new File(outputDir).mkdirs

postsMap.map{ case(k,v) =>
  val outputFileName = outputDir + "/" +
                       k.replace(domain, "").replace("/",".html")
  println(s"Writing $outputFileName")
  val fContentStr = {
      "<ul>\n" +
        v.map(t => s"<li><em>${t._2}:</em> ${skipHtml(t._3)}</li>")
         .mkString("\n") +
      "\n</ul>\n"
    }
  val fContent = fContentStr.getBytes(StandardCharsets.UTF_8)
  Files.write( Paths.get(outputFileName), fContent )
}

def skipHtml(s: String): String = {
  val t1 = """<[^>]+>""".r.replaceAllIn(s, " ")
  """\s+""".r.replaceAllIn(t1, " ")
}

Как видите, парсить XML на Scala — одно удовольствие. При сильном желании приведенный скрипт можно доработать таким образом, чтобы он сам производил экспорт комментариев, затем ходил за сжатым xml файлом по POP3 (экспорт производится на электронную почту), генерировал HTML-файлы и сам заливал их на сервер с помощью какого-нибудь scp. Также не повредит научить его как-то трогать sitemap.xml и чистить кэш CMS. Но как по мне, достаточно синкать комментарии руками, скажем, раз в месяц.

Следует отметить небольшую вероятность того, что ПС примет все это колдунство с отображением комментариев, а потом их перерисовкой JS кодом Disqus’а за клоакинг. Однако я сомневаюсь, что такое когда-нибудь с кем-нибудь случится. В конце концов, плагины Disqus’а для различных CMS работают по такому же принципу.

Нет единого мнения касательно того, стоит ли показывать комментарии ПС. Как мне кажется, это зависит от конкретного сайта. В блогах так делать особого смысла нет. А вот если вы ведете линкоблог или пишете подкаст, UGC может представлять собой большую ценность.

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.