2017-10-20 128 views
0

我想創建一個算法來以遞歸方式和以功能方式進行網絡爬蟲。 我知道如何使用for循環,var變量並累積它。 但我很努力地遞歸做它。在Scala中的網絡爬蟲算法

關於我的代碼的一些問題: 1.爲什麼def loop返回Any? 2.有一些URL形式爲http://..../example.zip,其中getLinksPage引發異常,返回None並中斷循環。我該如何處理它? 3.我如何用一些Scala框架測試來測試這段代碼?

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)) 

回答

0
  1. 您明確設定的返回類型爲Any。將其更新爲List[String]
  2. 將異常處理的範圍縮小爲僅包含可能拋出異常的代碼。使用for comprehension應該有助於這一點。同樣爲了簡單起見,考慮僅使用List[String].empty來返回List而不是Option[List]
  3. 兩種潛在的選擇:通過一個特性混合你的conn實例,這個特性可以讓你覆蓋這個值,或者改變你的函數來獲取你的單元測試可以模擬的隱式conn

編輯

下面是如何測試你的getLinksPageloop功能使用ScalaTest獨立單位spitball樣品。免責聲明:語法可能不是100%;根據需要調整。

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

謝謝。關於第3點,我如何使用遞歸http鏈接在本地模擬Web服務器來測試Web爬蟲?哪個框架(ScalaMock ...)? – rodbs

+0

使用示例單元測試更新答案。不保證句法正確。 :) –

+0

我不明白你爲什麼使用這些特質。它不適合我。我在編輯2中複製了我的代碼並且它不起作用 – rodbs