2017-10-20 15 views
0

Ich versuche, einen Algorithmus zu erstellen, um Web-Crawler rekursiv und auf eine funktionale Weise zu tun. Ich weiß, wie man es mit for-Schleifen, var-Variablen und Akkumulieren auf es macht. Aber ich kämpfe, es rekursiv zu tun.Algorithmus für Web-Crawler in Scala

Einige Fragen zu meinem Code: 1. Warum gibt def loop zurück Any? 2. Es gibt eine URL im Format http://..../example.zip, in der getLinksPage eine Ausnahme auslöst, None zurückgibt und die Schleife unterbricht. Wie kann ich damit umgehen? 3. Wie kann ich diesen Code mit einem Scala-Framework-Test testen?

def getLinksPage(urlToCrawl: String): Option[List[String]] = { 
    try { 
     val conn = Jsoup.connect(urlToCrawl) 
     val doc = conn.get() 
     val elements = doc.select("a[href]") 
     val elementsSc = elements.asScala 
     val links = elementsSc.map(_.attr("abs:href")).toSeq 
     val linksURL = links.map(new URL(_)) 

     val tartgetURL = (new URL(urlToCrawl)).getHost 
     val linksLocalURL = linksURL.filter(_.getHost == tartgetURL).map(_.toString).toList 
     Some(linksLocalURL) 
    } 
    catch { 
     case e: Exception => None 
    } 
    } 

    def loop(l:Option[List[String]], acc: List[String]): Any = l match { 
    case Some(Nil) => acc 
    case Some(hd::tl) => if (!acc.contains(hd)) loop(getLinksPage(hd),hd::acc) 
         else loop(Option(tl), acc) 
    case None => acc 

    } 

loop(getLinksPage(mainURL), List(mainURL)) 

Antwort

0
  1. Sie haben ausdrücklich die Rückgabetyp-Satz zu jedem. Aktualisieren Sie es auf List[String]
  2. Reduzieren Sie den Umfang Ihrer Ausnahmebehandlung, um nur den Code einzuschließen, der eine Ausnahme auslösen kann. Die Verwendung einessollte dies erleichtern. Stellen Sie aus Gründen der Einfachheit auch nur eine List anstelle einer Option[List] mit List[String].empty zurück.
  3. Zwei mögliche Optionen: Mischen Sie Ihre conn Instanz über eine Eigenschaft, mit der Sie den Wert überschreiben oder Ihre Funktion ändern können, um eine implizite zu nehmen, die Ihre Unit-Tests dann spotten können.

bearbeiten

Hier ist eine spitball Probe, wie Sie Ihre getLinksPage und loop Funktionen als unabhängige Einheiten testen können ScalaTest verwenden. Haftungsausschluss: Die Syntax ist möglicherweise nicht 100%; bei Bedarf anpassen.

case class Crawler() { 
    def getConnection(url: String) = Jsoup.connect(url) 

    def getLinksPage(urlToCrawl: String): Option[List[String]] = { 
    val conn = getConnection(urlToCrawl) 

    ... 
    } 
} 

class CrawerSpec extends WordSpec with MockFactory { 

    trait LinksFixture { 

    val connection = mock[Connection] 
    val getConnection = mockFunction[String, Connection] 

    lazy val crawler = new Crawler() { 
     override def getConnection(url: String) = LinksFixture.this.getConnection(url) 
    } 
    } 

    trait LoopFixture { 

    val getLinksPage = mock[String, Option[List[String]]] 

    lazy val crawler = new Crawler() { 
     override def getLinksPage(url: String) = LoopFixture.this.getLinksPage(url) 
    } 
    } 

    "getLinksPage" should { 

    "return the links" in new LinksFixture { 

     val url = "http://bad-wolf" 

     getConnection expects(url) returning connection 
     // add other expects on connection 

     crawler.getLinksPage(url) shouldBe expected // define expected 
    } 
    } 

    "loop" should { 

    "loop over the links" in new LoopFixture { 

     getLinksPage expects(*) onCall { 
     _ match { 
      case "a" => Some(List("b","c")) 
      case "b" => Some(List("d")) 
      case _ => None 
     } 
     } 
     // add any other expects 

     crawler.loop(Some(List("a")), List.empty[String]) shouldBe // define expected 
    } 
    } 
} 
+0

Danke. Was Punkt 3 betrifft, wie kann ich einen Web-Server mit rekursiven http-Links vortäuschen, um den Web-Crawler zu testen? Welcher Rahmen (ScalaMock ...)? – rodbs

+0

Aktualisierte Antwort mit Beispieleinheitstest. Nicht garantiert syntaktisch korrekt. :) –

+0

Ich verstehe nicht, warum Sie diese Eigenschaften verwenden. Und es funktioniert nicht für mich. Ich replizierte Ihren Code mit mir in Edit 2 und es funktioniert nicht – rodbs