<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 第七章 到達哲學 > 原文:[Chapter 7 Getting to Philosophy](http://greenteapress.com/thinkdast/html/thinkdast008.html) > 譯者:[飛龍](https://github.com/wizardforcel) > 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) > 自豪地采用[谷歌翻譯](https://translate.google.cn/) 本章的目標是開發一個 Web 爬蟲,它測試了第 6.1 節中提到的“到達哲學”猜想。 ## 7.1 起步 在本書的倉庫中,你將找到一些幫助你起步的代碼: + `WikiNodeExample.java`包含前一章的代碼,展示了 DOM 樹中深度優先搜索(DFS)的遞歸和迭代實現。 + `WikiNodeIterable.java`包含`Iterable`類,用于遍歷 DOM 樹。我將在下一節中解釋這段代碼。 + `WikiFetcher.java`包含一個工具類,使用`jsoup`從維基百科下載頁面。為了幫助你遵守維基百科的服務條款,此類限制了你下載頁面的速度;如果你每秒請求許多頁,在下載下一頁之前會休眠一段時間。 + `WikiPhilosophy.java`包含你為此練習編寫的代碼的大綱。我們將在下面進行說明。 你還會發現 Ant 構建文件`build.xml`。如果你運行`ant WikiPhilosophy`,它將運行一些簡單的啟動代碼。 ## 7.2 可迭代對象和迭代器 在前一章中,我展示了迭代式深度優先搜索(DFS),并且認為與遞歸版本相比,迭代版本的優點在于,它更容易包裝在`Iterator`對象中。在本節中,我們將看到如何實現它。 如果你不熟悉`Iterator`和`Iterable`接口,你可以閱讀 <http://thinkdast.com/iterator> 和 <http://thinkdast.com/iterable>。 看看`WikiNodeIterable.java`的內容。外層的類`WikiNodeIterable`實現`Iterable<Node>`接口,所以我們可以在一個`for`循環中使用它: ```java Node root = ... Iterable<Node> iter = new WikiNodeIterable(root); for (Node node: iter) { visit(node); } ``` 其中`root`是我們想要遍歷的樹的根節點,并且`visit`是一個方法,當我們“訪問”`Node`時,做任何我們想要的事情。 `WikiNodeIterable`的實現遵循以下慣例: + 構造函數接受并存儲根`Node`的引用。 + `iterator`方法創建一個返回一個`Iterator`對象。 這是它的樣子: ```java public class WikiNodeIterable implements Iterable<Node> { private Node root; public WikiNodeIterable(Node root) { this.root = root; } @Override public Iterator<Node> iterator() { return new WikiNodeIterator(root); } } ``` 內層的類`WikiNodeIterator`,執行所有實際工作。 ```java private class WikiNodeIterator implements Iterator<Node> { Deque<Node> stack; public WikiNodeIterator(Node node) { stack = new ArrayDeque<Node>(); stack.push(root); } @Override public boolean hasNext() { return !stack.isEmpty(); } @Override public Node next() { if (stack.isEmpty()) { throw new NoSuchElementException(); } Node node = stack.pop(); List<Node> nodes = new ArrayList<Node>(node.childNodes()); Collections.reverse(nodes); for (Node child: nodes) { stack.push(child); } return node; } } ``` 該代碼與 DFS 的迭代版本幾乎相同,但現在分為三個方法: + 構造函數初始化棧(使用一個`ArrayDeque`實現)并將根節點壓入這個棧。 + `isEmpty`檢查棧是否為空。 + `next`從`Node`棧中彈出下一個節點,按相反的順序壓入子節點,并返回彈出的`Node`。如果有人在空`Iterator`上調用`next`,則會拋出異常。 可能不明顯的是,值得使用兩個類和五個方法,來重寫一個完美的方法。但是現在我們已經完成了,在需要`Iterable`的任何地方,我們可以使用`WikiNodeIterable`,這使得它的語法整潔,易于將迭代邏輯(DFS)與我們對節點的處理分開。 ## 7.3 `WikiFetcher` 編寫 Web 爬蟲時,很容易下載太多頁面,這可能會違反你要下載的服務器的服務條款。為了幫助你避免這種情況,我提供了一個`WikiFetcher`類,它可以做兩件事情: + 它封裝了我們在上一章中介紹的代碼,用于從維基百科下載頁面,解析 HTML 以及選擇內容文本。 + 它測量請求之間的時間,如果我們在請求之間沒有足夠的時間,它將休眠直到經過了合理的間隔。默認情況下,間隔為`1`秒。 這里是`WikiFetcher`的定義: ```java public class WikiFetcher { private long lastRequestTime = -1; private long minInterval = 1000; /** * Fetches and parses a URL string, * returning a list of paragraph elements. * * @param url * @return * @throws IOException */ public Elements fetchWikipedia(String url) throws IOException { sleepIfNeeded(); Connection conn = Jsoup.connect(url); Document doc = conn.get(); Element content = doc.getElementById("mw-content-text"); Elements paragraphs = content.select("p"); return paragraphs; } private void sleepIfNeeded() { if (lastRequestTime != -1) { long currentTime = System.currentTimeMillis(); long nextRequestTime = lastRequestTime + minInterval; if (currentTime < nextRequestTime) { try { Thread.sleep(nextRequestTime - currentTime); } catch (InterruptedException e) { System.err.println( "Warning: sleep interrupted in fetchWikipedia."); } } } lastRequestTime = System.currentTimeMillis(); } } ``` 唯一的公共方法是`fetchWikipedia`,接收`String`形式的 URL,并返回一個`Elements`集合,該集合包含的一個 DOM 元素表示內容文本中每個段落。這段代碼應該很熟悉了。 新的代碼是`sleepIfNeeded`,它檢查自上次請求以來的時間,如果經過的時間小于`minInterval`(毫秒),則休眠。 這就是`WikiFetcher`全部。這是一個演示如何使用它的例子: ```java WikiFetcher wf = new WikiFetcher(); for (String url: urlList) { Elements paragraphs = wf.fetchWikipedia(url); processParagraphs(paragraphs); } ``` 在這個例子中,我們假設`urlList`是一個`String`的集合 ,并且`processParagraphs`是一個方法,對`Elements`做一些事情,它由`fetchWikipedia`返回。 此示例展示了一些重要的東西:你應該創建一個`WikiFetcher`對象并使用它來處理所有請求。如果有多個`WikiFetcher`的實例,則它們不會確保請求之間的最小間隔。 注意:我的`WikiFetcher`實現很簡單,但是通過創建多個實例,人們很容易誤用它。你可以通過制作`WikiFetcher`“單例” 來避免這個問題,你可以閱讀 <http://thinkdast.com/singleton>。 ## 7.4 練習 5 在`WikiPhilosophy.java`中,你會發現一個簡單的`main`方法,展示了如何使用這些部分。從這個代碼開始,你的工作是寫一個爬蟲: 1. 獲取維基百科頁面的 URL,下載并分析。 1. 它應該遍歷所得到的 DOM 樹來找到第一個 有效的鏈接。我會在下面解釋“有效”的含義。 1. 如果頁面沒有鏈接,或者如果第一個鏈接是我們已經看到的頁面,程序應該指示失敗并退出。 1. 如果鏈接匹配維基百科頁面上的哲學網址,程序應該提示成功并退出。 1. 否則應該回到步驟`1`。 該程序應該為它訪問的 URL 構建`List`,并在結束時顯示結果(無論成功還是失敗)。 那么我們應該認為什么是“有效的”鏈接?你在這里有一些選擇 各種版本的“到達哲學”推測使用略有不同的規則,但這里有一些選擇: + 這個鏈接應該在頁面的內容文本中,而不是側欄或彈出框。 + 它不應該是斜體或括號。 + 你應該跳過外部鏈接,當前頁面的鏈接和紅色鏈接。 + 在某些版本中,如果文本以大寫字母開頭,則應跳過鏈接。 你不必遵循所有這些規則,但我們建議你至少處理括號,斜體以及當前頁面的鏈接。 如果你有足夠的信息來起步,請繼續。或者你可能想要閱讀這些提示: + 當你遍歷樹的時候,你將需要處理的兩種`Node`是`TextNode`和`Element`。如果你找到一個`Element`,你可能需要轉換它的類型,來訪問標簽和其他信息。 + 當你找到包含鏈接的`Element`時,通過向上跟蹤父節點鏈,可以檢查是否是斜體。如果父節點鏈中有一個`<i>`或`<em>`標簽,鏈接為斜體。 + 為了檢查鏈接是否在括號中,你必須在遍歷樹時掃描文本,并跟蹤開啟和閉合括號(理想情況下,你的解決方案應該能夠處理嵌套括號(像這樣))。 如果你從 Java 頁面開始,你應該在跟隨七個鏈接之后到達哲學,除非我運行代碼后發生了改變。 好的,這就是你所得到的所有幫助。現在全靠你了。玩的開心!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看