整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          爬蟲學(xué)習(xí)路線大綱

          天分享下基礎(chǔ)爬蟲或者小規(guī)模爬蟲,應(yīng)該掌握哪些技能、需要如何學(xué)起!

          【文末有獲取方式】

          【文末有獲取方式】

          Python 基礎(chǔ)

          • 環(huán)境搭建
            • Python安裝
            • 開發(fā)工具 - PyCharm,VS Code
          • 變量
            • 定義變量
            • 命名規(guī)則
            • 基本數(shù)據(jù)類型
          • 基本數(shù)據(jù)結(jié)構(gòu)
            • 字符串
            • 列表
            • 字典
            • 集合
            • 元組
          • 流程控制
            • 條件
            • 循環(huán)
          • 函數(shù)
            • 調(diào)用函數(shù)
            • 定義函數(shù)
            • 函數(shù)參數(shù)
            • lambda 函數(shù)
          • 面向?qū)ο缶幊?/li>
            • 類和對象
            • 訪問限制
            • 裝飾器

          數(shù)據(jù)采集與解析

          • HTTP 基本原理
            • URI 和 URL 統(tǒng)一資源定位符
            • HTTP&HTTPS 請求與響應(yīng)
            • HTML 組成原理
          • WEB 基本原理
            • JavaScript&HTML&CSS
            • 節(jié)點樹與節(jié)點
            • Web 加載原理
            • 靜動態(tài) Web 頁面
          • Socket 庫
            • 基于 socket 協(xié)議的爬蟲
          • Requests 庫
            • requests 庫的使用
            • cookie 與 session
            • 模擬登錄
            • 請求頭模擬
            • IP 代理
          • 正則表達(dá)式
            • re 模塊的使用
            • 基本字符串、數(shù)字等匹配規(guī)則
            • 貪婪與非貪婪匹配
          • Xpath
            • 執(zhí)行原理
            • 節(jié)點操作
            • 元素操作

          、前言

          jsoup 是一款Java 的HTML解析器,我們使用它可直接解析URL地址、HTML文本內(nèi)容。除此之外,它提供了一套非常友好的API,使得我們可以比較便利的通過DOM,CSS等操作類型,獲取對應(yīng)網(wǎng)頁元素屬性及數(shù)據(jù)操作。

          jsoup官網(wǎng):https://jsoup.org/

          二、核心功能

          2.1、功能描述

          ① 從一個URL,文件或字符串中解析HTML

          ② 使用DOM或CSS選擇器來查找、取出數(shù)據(jù)使用DOM或CSS選擇器來查找、取出數(shù)據(jù)

          ③ 可操作HTML元素、屬性、文本可操作HTML元素、屬性、文本

          Tips:jsoup是基于MIT協(xié)議發(fā)布的,可放心使用于商業(yè)項目

          2.2、Maven地址

                  <dependency>
                      <groupId>org.jsoup</groupId>
                      <artifactId>jsoup</artifactId>
                      <version>1.11.3</version>
                  </dependency>

          三、核心API

          6個包提供用于開發(fā)jsoup應(yīng)用程序的類和接口。

          org.jsoup

          org.jsoup.examples

          org.jsoup.helper

          org.jsoup.nodes

          org.jsoup.parser
          org.jsoup.safety

          org.jsoup.salect

          主要類:

          Jsoup 類提供了連接,清理和解析HTML文檔的方法

          Document 獲取HTML文檔

          Element 獲取、操作HTML節(jié)點

          四、實例代碼

          4.1、三種加載HTML的方法

          
              public static void main(String[] args) {
                  /* 從URL加載HTML */
                  Document document = Jsoup.connect("http://www.baidu.com").get();
                  String title = document.title();
                  /* 獲取html中的標(biāo)題 */
                  System.out.println("title :"+title);
          
                  /* 從字符串加載HTML */
                  String html = "<html><head><title>First parse</title></head>"
                          + "<body><p>Parsed HTML into a doc.</p></body></html>";
                  Document doc = Jsoup.parse(html);
                  title = doc.title();
                  System.out.println("title :"+title);
          
                  /* 從文件加載HTML */
                  doc = Jsoup.parse(new File("F:\\jsoup\\html\\index.html"),"utf-8");
                  title = doc.title();
                  System.out.println("title :"+title);
              }
          

          4.2、獲取html中的head、body、url等信息

          
              public static void main(String[] args) {
                  Document document = Jsoup.connect("http://www.baidu.com").get();
                  String title = document.title();
          
                  System.out.println("title :"+title);
                  /* 獲取html中的head */
                  System.out.println(document.head());
                  /* 獲取html中的body */
          
                  /* 獲取HTML頁面中的所有鏈接 */
                  Elements links = document.select("a[href]");
                  for (Element link : links){
                      System.out.println("link : "+ link.attr("href"));
                      System.out.println("text :"+ link.text());
                  }
              }
           

          4.3、獲取URL的元信息

          
              public static void main(String[] args) {
                  Document document = Jsoup.connect("https://passport.lagou.com").get();
          
                  System.out.println(document.head());
                  /* 獲取URL的元信息 */
                  String description = document.select("meta[name=description]").get(0).attr("content");
                  System.out.println("Meta description : " + description);
          
                  String keywords = document.select("meta[name=keywords]").first().attr("content");
                  System.out.println("Meta keyword : " + keywords);
              }
           

          4.4、根據(jù)class名稱獲取表單

          
              public static void main(String[] args) {
                  Document document = Jsoup.connect("https://baidu.com").get();
                  /* 獲取拉勾網(wǎng)登入頁面的body */
                  /* System.out.println(document.body()); */
                  /* 根據(jù)class名稱獲取表單 */
                  Elements formElement = document.getElementsByClass("form_body");
                  System.out.println(formElement.html());
                  /* 獲取URL的元信息 */
                  for (Element inputElement : formElement) {
                      String placeholder = inputElement.getElementsByTag("input").attr("placeholder");
                      System.out.println(placeholder);
                  }
              }
           

          4.5、提取并打印表單參數(shù)

          
              public static void main(String[] args) {
                  Document document = Jsoup.parse(new File("F:\\jsoup\\html\\login.html"),"utf-8");
                  Element loginform = document.getElementById("registerform");
          
                  Elements inputElements = loginform.getElementsByTag("input");
                  for (Element inputElement : inputElements) {
                      String key = inputElement.attr("name");
                      String value = inputElement.attr("value");
                      System.out.println("Param name: "+key+" -- Param value: "+value);
                  }
              }
           

          4.6、設(shè)置元素的html內(nèi)容

          
              public static void main(String[] args) {
                  Document document = Jsoup.parse(new File("F:\\jsoup\\html\\index.html"),"utf-8");
                  System.out.println(document.body());
                  System.out.println("*************");
                  Element div = document.select("div").first();
                  div.html("<p>Hello</p>");
                  div.prepend("<p>Fiest</p>");
                  div.append("<p>Last</p>");
                  System.out.println(document.body());
                  System.out.println(div.text());
          
                  /* 對元素包裹一個外部HTML內(nèi)容 */
                  div.wrap("<div id=\"div2\"></div>");
                  System.out.println(document.body());
              }
           

          4.7、設(shè)置元素的文本內(nèi)容

          javascript 是一門單線程的語言,在同一個時間只能做完成一件任務(wù),如果有多個任務(wù),就必須排隊,前面一個任務(wù)完成,再去執(zhí)行后面的任務(wù)。作為瀏覽器端的腳本語言,javascript 的主要功能是用來和用戶交互以及操作 dom。假設(shè) javascript 不是單線程語言,在一個線程里我們給某個 dom 節(jié)點增加內(nèi)容的時候,另一個線程同時正在刪除這個 dom 節(jié)點的內(nèi)容,則會造成混亂。

          由于 js 單線程的設(shè)計,假設(shè) js 程序的執(zhí)行都是同步。如果執(zhí)行一些耗時較長的程序,例如 ajax 請求,在請求開始至請求響應(yīng)的這段時間內(nèi),當(dāng)前的工作線程一直是空閑狀態(tài), ajax 請求后面的 js 代碼只能等待請求結(jié)束后執(zhí)行,因此會導(dǎo)致 js 阻塞的問題。

          javascript 單線程指的是瀏覽器中負(fù)責(zé)解釋和執(zhí)行 javascript 代碼的只有一個線程,即為 js 引擎線程,但是瀏覽器的渲染進(jìn)程是提供多個線程的,如下:

          1. js 引擎線程
          2. 事件觸發(fā)線程
          3. 定時器觸發(fā)線程
          4. 異步 http 請求線程
          5. GUI 渲染線程

          一、異步 & 同步

          為解決上述類似上述 js 阻塞的問題,js 引入了同步和異步的概念。

          1、什么是同步?

          “同步”就是后一個任務(wù)等待前一個任務(wù)結(jié)束后再去執(zhí)行。

          2、什么是異步?

          “異步”與同步不同,每一個異步任務(wù)都有一個或多個回調(diào)函數(shù)。webapi 會在其相應(yīng)的時機(jī)里將回調(diào)函數(shù)添加進(jìn)入消息隊列中,不直接執(zhí)行,然后再去執(zhí)行后面的任務(wù)。直至當(dāng)前同步任務(wù)執(zhí)行完畢后,再把消息隊列中的消息添加進(jìn)入執(zhí)行棧進(jìn)行執(zhí)行。

          異步任務(wù)在瀏覽器中一般是以下:

          1. 網(wǎng)絡(luò)請求
          2. 計時器
          3. DOM 監(jiān)聽事件
          4. ...

          二、什么是執(zhí)行棧(stack)、堆(heap)、事件隊列(task queue)?

          1、執(zhí)行棧

          “棧”是一種數(shù)據(jù)結(jié)構(gòu),是一種線性表。特點為 LIFO,即先進(jìn)后出 (last in, first out)。

          利用數(shù)組的 push 和 shift 可以實現(xiàn)壓棧和出棧的操作。

          在代碼運(yùn)行的過程中,函數(shù)的調(diào)用會形成一個由若干幀組成的棧。

          function foo(b) {
            let a = 10;
            return a + b + 11;
          }
          
          function bar(x) {
            let y = 3;
            return foo(x * y);
          }
          
          console.log(bar(7))

          上面代碼最終會在控制臺打印42,下面梳理一下它的執(zhí)行順序。

          1. console.log 函數(shù)作為第一幀壓入棧中。
          2. 調(diào)用 bar,第二幀被壓入棧中。幀中包含著 bar 的變量對象。
          3. bar 調(diào)用 foo,foo 做一位第三幀被壓入棧中,幀中包含著 foo 的變量對象。
          4. foo 執(zhí)行完畢然后返回。被彈出棧。
          5. bar 執(zhí)行完畢然后返回,被彈出棧。
          6. log 函數(shù)接收到 bar 的返回值。執(zhí)行完畢后,出棧。此時棧已空。

          2、堆

          對象被分配在堆中,堆是一個用來表示一大塊(通常是非結(jié)構(gòu)化的)內(nèi)存區(qū)域的計算機(jī)術(shù)語。

          堆和棧的區(qū)別

          首先,stack 是有結(jié)構(gòu)的,每個區(qū)塊按照一定次序存放,可以明確知道每個區(qū)塊的大小;heap 是沒有結(jié)構(gòu)的,數(shù)據(jù)可以任意存放。因此,

          stack 的尋址速度要快于 heap。

          其次,每個線程分配一個 stack,每個進(jìn)程分配一個 heap,也就是說,stack 是線程獨占的,heap 是線程共用的。

          此外,stack 創(chuàng)建的時候,大小是確定的,數(shù)據(jù)從超過這個大小,就發(fā)生 stack overflow 錯誤,而 heap 的大小是不確定的,

          需要的話可以不斷增加。

          public void Method1()
          {
              int i=4;
          
              int y=2;
          
              class1 cls1 = new class1();
          }

          上面代碼這三個變量和一個對象實例在內(nèi)存中的存放方式如下。

          從上圖可以看到,i、y和cls1都存放在stack,因為它們占用內(nèi)存空間都是確定的,而且本身也屬于局部變量。但是,cls1指向的對象實例存放在heap,因為它的大小不確定。作為一條規(guī)則可以記住,所有的對象都存放在heap。

          接下來的問題是,當(dāng)Method1方法運(yùn)行結(jié)束,會發(fā)生什么事?

          回答是整個stack被清空,i、y和cls1這三個變量消失,因為它們是局部變量,區(qū)塊一旦運(yùn)行結(jié)束,就沒必要再存在了。而heap之中的那個對象實例繼續(xù)存在,直到系統(tǒng)的垃圾清理機(jī)制(garbage collector)將這塊內(nèi)存回收。因此,一般來說,內(nèi)存泄漏都發(fā)生在heap,即某些內(nèi)存空間不再被使用了,卻因為種種原因,沒有被系統(tǒng)回收。

          3、事件隊列和事件循環(huán)

          隊列是一種數(shù)據(jù)結(jié)構(gòu),也是一種特殊的線性表。特點為 FIFO,即先進(jìn)先出(first in, first out)

          利用數(shù)組的 push 和 pop 可實現(xiàn)入隊和出隊的操作。

          事件循環(huán)和事件隊列的維護(hù)是由事件觸發(fā)線程控制的。

          事件觸發(fā)線程線程同樣是由瀏覽器渲染引擎提供的,它會維護(hù)一個事件隊列。

          js 引擎遇到上文所列的異步任務(wù)后,會交個相應(yīng)的線程去維護(hù)異步任務(wù),等待某個時機(jī),然后由事件觸發(fā)線程將異步任務(wù)對應(yīng)的回調(diào)函數(shù)加入到事件隊列中,事件隊列中的函數(shù)等待被執(zhí)行。

          js 引擎在執(zhí)行過程中,遇到同步任務(wù),會將任務(wù)直接壓入執(zhí)行棧中執(zhí)行,當(dāng)執(zhí)行棧為空(即 js 引擎線程空閑), 事件觸發(fā)線程 會從事件隊列中取出一個任務(wù)(即異步任務(wù)的回調(diào)函數(shù))放入執(zhí)行在棧中執(zhí)行。

          執(zhí)行完了之后,執(zhí)行棧再次為空,事件觸發(fā)線程會重復(fù)上一步的操作,再從事件隊列中取出一個消息,這種機(jī)制就被稱為 事件循環(huán) (Event Loop)機(jī)制。

          為了更好地理解Event Loop,請看下圖(轉(zhuǎn)引自Philip Roberts的演講《Help, I'm stuck in an event-loop》)。

          例子代碼:

          console.log('script start')
          
          setTimeout(() => {
            console.log('timer 1 over')
          }, 1000)
          
          setTimeout(() => {
            console.log('timer 2 over')
          }, 0)
          
          console.log('script end')
          
          // script start
          // script end
          // timer 2 over
          // timer 1 over

          模擬 js 引擎對其執(zhí)行過程:

          第一輪事件循環(huán):

          1. console.log 為同步任務(wù),入棧,打印“script start”。出棧。
          2. setTimeout 為異步任務(wù),入棧,交給定時器觸發(fā)線程處理(在1秒后加入將回調(diào)加入事件隊列)。出棧。
          3. setTimeout 為異步任務(wù),入棧,交給定時器觸發(fā)線程處理(在4ms之內(nèi)將回調(diào)加入事件隊列)。出棧。
          4. console.log 為同步任務(wù),入棧,打印"script end"。出棧。

          此時,執(zhí)行棧為空,js 引擎線程空閑。便從事件隊列中讀取任務(wù),此時隊列如下:

          第二輪事件循環(huán)

          1. js 引擎線程從事件隊列中讀取 cb2 加入執(zhí)行棧并執(zhí)行,打印”time 2 over“。出棧。

          第三輪事件循環(huán)

          1. js 引擎從事件隊列中讀取 cb1 加入執(zhí)行棧中并執(zhí)行,打印”time 1 over“ 。出棧。

          注意點:

          上面,timer 2 的延時為 0ms,HTML5標(biāo)準(zhǔn)規(guī)定 setTimeout 第二個參數(shù)不得小于4(不同瀏覽器最小值會不一樣),不足會自動增加,所以 "timer 2 over" 還是會在 "script end" 之后。

          就算延時為0ms,只是 time 2 的回調(diào)函數(shù)會立即加入事件隊列而已,回調(diào)的執(zhí)行還是得等到執(zhí)行棧為空時執(zhí)行。

          四、宏任務(wù) & 微任務(wù)

          在 ES6 新增 Promise 處理異步后,js 執(zhí)行引擎的處理過程又發(fā)生了新的變化。

          看代碼:

          console.log('script start')
          
          setTimeout(function() {
              console.log('timer over')
          }, 0)
          
          Promise.resolve().then(function() {
              console.log('promise1')
          }).then(function() {
              console.log('promise2')
          })
          
          console.log('script end')
          
          // script start
          // script end
          // promise1
          // promise2
          // timer over

          這里又新增了兩個新的概念, macrotask (宏任務(wù))和 microtask (微任務(wù))。

          所有的任務(wù)都劃分到宏任務(wù)和微任務(wù)下:

          • macrotask : script 主代碼塊、setTimeout、setInterval、requestAnimationFrame、node 中的setimmediate 等。
          • microtask : Promise.then catch finally、MutationObserver、node 中的process.nextTick 等。

          js 引擎首先執(zhí)行主代碼塊。

          執(zhí)行棧每次執(zhí)行的代碼就是一個宏任務(wù),包括任務(wù)隊列(宏任務(wù)隊列)中的。執(zhí)行棧中的任務(wù)執(zhí)行完畢后,js 引擎會從宏任務(wù)隊列中去添加任務(wù)到執(zhí)行棧中,即同樣是事件循環(huán)的機(jī)制。

          當(dāng)在執(zhí)行宏任務(wù)遇到微任務(wù) Promise.then 時,會創(chuàng)建一個微任務(wù),并加入到微任務(wù)隊列中的隊尾。

          微任務(wù)是在宏任務(wù)執(zhí)行的時候創(chuàng)建的,而在下一個宏任務(wù)執(zhí)行之前,瀏覽器會對頁面重新渲染(task >> render >> task(任務(wù)隊列中讀取))。 同時,在上一個宏任務(wù)執(zhí)行完成后,頁面渲染之前,會執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)。

          所以上述代碼的執(zhí)行過程就可以解釋了。

          js 引擎執(zhí)行 promise.then 時,promise1、promise2 被認(rèn)為是兩個微任務(wù)按照代碼的先后順序被加入到微任務(wù)隊列中,script end執(zhí)行后,空。

          此時當(dāng)前宏任務(wù)(script 主代碼塊)執(zhí)行完畢,并不從當(dāng)前宏任務(wù)隊列中讀取任務(wù)。而是立馬清空當(dāng)前宏任務(wù)所產(chǎn)生的微任務(wù)隊列。將兩個微任務(wù)依次放入執(zhí)行棧中執(zhí)行。執(zhí)行完畢,打印 promise1、promise2。棧空。 此時,第一輪事件循環(huán)結(jié)束。

          緊接著,再去讀取宏任務(wù)隊列中的任務(wù),time over 被打印。棧空。

          因此,宏任務(wù)和微任務(wù)的執(zhí)行機(jī)制如下:

          1. 執(zhí)行一個宏任務(wù)(棧中沒有就從宏任務(wù)隊列中獲取)
          2. 執(zhí)行過程中遇到微任務(wù),就將它添加到微任務(wù)的任務(wù)隊列中
          3. 宏任務(wù)執(zhí)行完畢,立即執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)(依次執(zhí)行)
          4. 當(dāng)前所有微任務(wù)執(zhí)行完畢后,開始檢查渲染,GUI 線程接管渲染
          5. 渲染完畢后,JS 引擎繼續(xù)開始下一個宏任務(wù),從宏任務(wù)隊列中獲取

          async & await

          因為,async 和 await 本質(zhì)上還是基于 Promise 的封裝,而 Promise 是屬于微任務(wù)的一種。所以使用 await 關(guān)鍵字與 Promise.then 效果類似:

          setTimeout(_ => console.log(4))
          
          async function main() {
            console.log(1)
            await Promise.resolve()
            console.log(3)
          }
          
          main()
          
          console.log(2)
          // 1
          // 2
          // 3
          // 4

          async 函數(shù)在 await 之前的代碼都是同步執(zhí)行的, 可以理解為 await 之前的代碼都屬于 new Promise 時傳入的代碼,await 之后的所有代碼都是 Promise.then 中的回調(diào),即在微任務(wù)隊列中。

          五、總結(jié)

          1. js 單線程實際上解釋執(zhí)行 js 代碼的只有一個線程,但是瀏覽器的渲染是多線程的。
          2. 異步和同步的概念與區(qū)別,異步任務(wù)有哪些。
          3. 棧、堆、隊列的特點和使用場景。
          4. 事件隊列以及事件循環(huán)機(jī)制。
          5. es6 下,宏任務(wù)與微任務(wù)的執(zhí)行過程。

          參考:

          • JavaScript 異步與事件循環(huán)
          • 并發(fā)模型與事件循環(huán)
          • 微任務(wù)、宏任務(wù)與Event-Loop
          • JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop
          • JS事件循環(huán)
          • [譯] 深入理解 JavaScript 事件循環(huán)(二)— task and microtask
          • Help, I'm stuck in an event-loop

          原文作者:大芒果哇

          原文地址:https://www.cnblogs.com/shenggao/p/13799566.html


          主站蜘蛛池模板: 熟女少妇丰满一区二区| 亚洲av无码一区二区乱子伦as | 国产无套精品一区二区| 无码人妻精品一区二区三区9厂| 亚洲天堂一区在线| 日本高清不卡一区| 中文字幕日韩一区二区三区不| 一区二区三区在线播放| 精品综合一区二区三区| 亚洲AV成人精品日韩一区18p | 亚洲人成网站18禁止一区| 亚洲av午夜精品一区二区三区| 一区二区三区免费看| 大屁股熟女一区二区三区| 中文字幕一精品亚洲无线一区| 国精品无码一区二区三区左线| 清纯唯美经典一区二区| 无码精品人妻一区二区三区人妻斩| 无码人妻品一区二区三区精99| 日韩人妻精品一区二区三区视频| 少妇无码一区二区二三区| 国产91大片精品一区在线观看 | 亚洲AV无码国产精品永久一区| 极品尤物一区二区三区| 日本一区免费电影| 无码精品人妻一区二区三区影院 | 无码精品人妻一区二区三区AV| www一区二区三区| 日韩精品一区二区三区四区 | 精品国产一区AV天美传媒| 人妻互换精品一区二区| 精品一区二区三区水蜜桃| 国模精品一区二区三区| 在线视频一区二区三区| 国产主播福利精品一区二区| 中文字幕一区二区视频| 精品一区二区三区免费毛片爱 | 精品女同一区二区三区免费播放| 亚洲片一区二区三区| 精品国产一区二区三区无码| 国产麻豆媒一区一区二区三区|