Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产日韩欧美亚洲青青草原,亚洲免费在线看,国产日韩精品一区二区

          整合營銷服務(wù)商

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

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

          如何用JAVA 寫一個簡單爬蟲

          了編寫一個Java爬蟲,你需要了解以下幾個步驟:

          1. 首先,你需要確定你要抓取的網(wǎng)站??梢詮臑g覽器中復(fù)制網(wǎng)站的URL并粘貼到你的Java代碼中。
          2. 接下來,你需要使用Java的網(wǎng)絡(luò)編程API連接到該網(wǎng)站。你可以使用URLConnection或HttpClient等庫。
          3. 一旦你建立了一個連接,你就可以開始讀取網(wǎng)頁內(nèi)容。你可以使用Java的IO庫讀取網(wǎng)頁。
          4. 在讀取網(wǎng)頁內(nèi)容之后,你需要解析網(wǎng)頁以提取所需的信息。這可以使用Java的解析器,如Jsoup或XML解析器。
          5. 最后,你需要存儲或使用所提取的信息。你可以將信息保存到數(shù)據(jù)庫中,將其輸出到文件中,或?qū)⑵溆糜谄渌猛尽?/li>

          下面是一個基本的Java爬蟲代碼示例,它使用Jsoup解析器和URLConnection庫連接到目標(biāo)網(wǎng)站并提取標(biāo)題和鏈接信息:

          import java.io.IOException;
          import java.net.URL;
          import java.net.URLConnection;
          import java.util.Scanner;
          
          import org.jsoup.Jsoup;
          import org.jsoup.nodes.Document;
          import org.jsoup.nodes.Element;
          import org.jsoup.select.Elements;
          
          public class SimpleWebCrawler {
            
            public static void main(String[] args) {
              String url = "https://www.example.com/";
              try {
                URLConnection conn = new URL(url).openConnection();
                conn.addRequestProperty("User-Agent", "Mozilla/5.0");
                Scanner scanner = new Scanner(conn.getInputStream());
                String html = scanner.useDelimiter("\\Z").next();
                scanner.close();
                Document doc = Jsoup.parse(html);
                Elements links = doc.select("a[href]");
                for (Element link : links) {
                  System.out.println(link.attr("href") + " - " + link.text());
                }
              } catch (IOException e) {
                e.printStackTrace();
              }
            }
          }
          

          Jsoup

          Jsoup是一款用于解析HTML和XML文檔的Java庫。它提供了類似于jQuery的語法來操作文檔,使得解析和處理文檔變得非常簡單。

          以下是Jsoup解析器的一些常用功能:

          1. 解析HTML文檔:使用Jsoup可以輕松解析HTML文檔,并且可以處理各種標(biāo)簽、屬性、文本內(nèi)容等。
          2. 獲取元素:可以使用類似于jQuery的選擇器語法來獲取HTML文檔中的元素,例如獲取所有的鏈接、圖片等。
          3. 修改元素:可以使用Jsoup修改HTML文檔中的元素,例如修改元素的屬性、添加或刪除元素等。
          4. 過濾HTML文檔:可以使用Jsoup過濾HTML文檔中的不必要的元素,例如過濾掉廣告、統(tǒng)計代碼等。
          5. 處理字符編碼:可以使用Jsoup來處理HTML文檔中的字符編碼,例如將文檔中的ISO-8859-1編碼轉(zhuǎn)換為UTF-8編碼等。
          6. 支持HTTPS:Jsoup還支持使用HTTPS協(xié)議獲取HTML文檔,可以使用它來爬取一些需要登錄才能訪問的網(wǎng)站。

          總之,Jsoup是一款非常實用的HTML和XML解析器,可以幫助Java開發(fā)者快速、簡單地解析和處理HTML文檔,使得爬蟲開發(fā)變得更加容易。

          Jsoup的使用

          使用Jsoup解析器需要先將其添加到項目的依賴中。可以通過Maven或者Gradle來添加依賴。

          例如,使用Maven添加Jsoup的依賴:

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

          添加依賴之后,就可以在Java代碼中使用Jsoup了。以下是使用Jsoup解析器獲取HTML文檔中所有鏈接的示例代碼:

          import org.jsoup.Jsoup;
          import org.jsoup.nodes.Document;
          import org.jsoup.nodes.Element;
          import org.jsoup.select.Elements;
          
          public class JsoupExample {
              public static void main(String[] args) {
                  String html = "<html><head><title>Jsoup Example</title></head>"
                          + "<body><p>Jsoup is a Java library for working with real-world HTML.</p>"
                          + "<a href=\"http://example.com\">Example</a></body></html>";
          
                  Document doc = Jsoup.parse(html); // 將HTML字符串解析為文檔對象
          
                  Elements links = doc.select("a"); // 獲取所有的鏈接元素
          
                  for (Element link : links) {
                      String href = link.attr("href"); // 獲取鏈接的URL地址
                      String text = link.text(); // 獲取鏈接的文本內(nèi)容
                      System.out.println(href + ": " + text);
                  }
              }
          }
          

          以上代碼使用Jsoup將HTML字符串解析為文檔對象,然后使用選擇器語法獲取所有的鏈接元素,并輸出它們的URL地址和文本內(nèi)容。

          除此之外,Jsoup還有很多其他的功能,例如修改元素、過濾HTML文檔等等,可以根據(jù)具體需求靈活運(yùn)用。

          Jsoup 解析器的常見功能和代碼片段示例

          1.獲取網(wǎng)頁的 Title:

          Document doc = Jsoup.connect("http://example.com/").get();
          String title = doc.title();

          2.獲取指定標(biāo)簽的文本內(nèi)容:

          Element element = doc.select("div.content").first();
          String text = element.text();

          3.獲取指定屬性的值:

          Element element = doc.select("img").first();
          String src = element.attr("src");

          4.過濾 HTML 標(biāo)簽:

          String html = "<p>這是一段 <b>加粗</b> 的文本。</p>";
          String text = Jsoup.parse(html).text();

          5.修改 HTML 內(nèi)容:

          Element element = doc.select("div.content").first();
          element.append("<p>這是新增的文本內(nèi)容。</p>");

          6.提取網(wǎng)頁中的鏈接:

          Elements links = doc.select("a[href]");
          for (Element link : links) {
              String href = link.attr("href");
              System.out.println(href);
          }

          7.提取網(wǎng)頁中的圖片:

          Elements imgs = doc.select("img[src~=(?i)\\.(png|jpe?g|gif)]");
          for (Element img : imgs) {
              String src = img.attr("src");
              System.out.println(src);
          }

          這些只是 Jsoup 解析器的常見用法之一。Jsoup 還有更多的功能,如解析 XML、處理表單、處理 Cookie 等,大家可以自己去了解!

          有不足之處大家也可以在評論區(qū)指出!

          在當(dāng)前數(shù)據(jù)爆發(fā)的時代,數(shù)據(jù)分析行業(yè)勢頭強(qiáng)勁,越來越多的人涉足數(shù)據(jù)分析領(lǐng)域。進(jìn)入領(lǐng)域最想要的就是獲取大量的數(shù)據(jù)來為自己的分析提供支持,但是如何獲取互聯(lián)網(wǎng)中的有效信息?這就促進(jìn)了“爬蟲”技術(shù)的飛速發(fā)展。

          網(wǎng)絡(luò)爬蟲(又被稱為網(wǎng)頁蜘蛛,網(wǎng)絡(luò)機(jī)器人,在FOAF社區(qū)中間,更經(jīng)常的稱為網(wǎng)頁追逐者),是一種按照一定的規(guī)則,自動地抓取萬維網(wǎng)信息的程序或者腳本。

          傳統(tǒng)爬蟲從一個或若干初始網(wǎng)頁的URL開始,獲得初始網(wǎng)頁上的URL,在抓取網(wǎng)頁的過程中,不斷從當(dāng)前頁面上抽取新的URL放入隊列,直到滿足系統(tǒng)的一定停止條件。聚焦爬蟲的工作流程較為復(fù)雜,需要根據(jù)一定的網(wǎng)頁分析算法過濾與主題無關(guān)的鏈接,保留有用的鏈接并將其放入等待抓取的URL隊列。然后,它將根據(jù)一定的搜索策略從隊列中選擇下一步要抓取的網(wǎng)頁URL,并重復(fù)上述過程,直到達(dá)到系統(tǒng)的某一條件時停止。

          另外,所有被爬蟲抓取的網(wǎng)頁將會被系統(tǒng)存貯,進(jìn)行一定的分析、過濾,并建立索引,以便之后的查詢和檢索;對于聚焦爬蟲來說,這一過程所得到的分析結(jié)果還可能對以后的抓取過程給出反饋和指導(dǎo)。

          筆者是爬蟲初學(xué)者,通過這篇綜述來記錄一下自己的心得體會。

          以下為文章主要內(nèi)容:

          1. 初見爬蟲

          使用Python中的Requests第三方庫。在Requests的7個主要方法中,最常使用的就是get()方法,通過該方法構(gòu)造一個向服務(wù)器請求資源的Request對象,結(jié)果返回一個包含服務(wù)器資源的額Response對象。通過Response對象則可以獲取請求的返回狀態(tài)、HTTP響應(yīng)的字符串即URL對應(yīng)的頁面內(nèi)容、頁面的編碼方式以及頁面內(nèi)容的二進(jìn)制形式。

          在了解get()方法之前我們先了解一下HTTP協(xié)議,通過對HTTP協(xié)議來理解我們訪問網(wǎng)頁這個過程到底都進(jìn)行了哪些工作。

          1.1 淺析HTTP協(xié)議

          超文本傳輸協(xié)議(HTTP,HyperText Transfer Protocol)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。所有的www文件都必須遵守這個標(biāo)準(zhǔn)。HTTP協(xié)議主要有幾個特點:

          • 支持客戶/服務(wù)器模式

          • 簡單快捷:客服向服務(wù)器發(fā)出請求,只需要傳送請求方法和路徑。請求方法常用的有GET, HEAD, POST。每種方法規(guī)定了客戶與服務(wù)器聯(lián)系的類型不同。由于HTTP協(xié)議簡單,使得HTTP服務(wù)器的程序規(guī)模小,因而通信速度快。

          • 靈活:HTTP允許傳輸任意類型的數(shù)據(jù)對象。

          • 無連接:無連接的含義是限制每次連接請求只處理一個請求。服務(wù)器處理完客戶的請求,收到客戶的應(yīng)答后即斷開連接,這種方式可以節(jié)省傳輸時間。

          • 無狀態(tài):HTTP協(xié)議是無狀態(tài)協(xié)議。無狀態(tài)是指協(xié)議對于事物處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則它必須重傳,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大,另一方面,在服務(wù)器不需要先前信息時它的應(yīng)答就較快。

          下面通過一張圖我們來了解一下訪問網(wǎng)頁的過程都發(fā)生了什么:

          1. 首先瀏覽器拿到網(wǎng)址之后先將主機(jī)名解析出來。如 http://www.baidu.com/index.html 則會將主機(jī)名www.baidu.com 解析出來。

          2. 查找ip,根據(jù)主機(jī)名,會首先查找ip,首先查詢hosts文件,成功則返回對應(yīng)的ip地址,如果沒有查詢到,則去DNS服務(wù)器查詢,成功就返回ip,否則會報告連接錯誤。

          3. 發(fā)送http請求,瀏覽器會把自身相關(guān)信息與請求相關(guān)信息封裝成HTTP請求 消息發(fā)送給服務(wù)器。

          4. 服務(wù)器處理請求,服務(wù)器讀取HTTP請求中的內(nèi)容,在經(jīng)過解析主機(jī),解析站點名稱,解析訪問資源后,會查找相關(guān)資源,如果查找成功,則返回狀態(tài)碼200,失敗就會返回大名鼎鼎的404了,在服務(wù)器監(jiān)測到請求不在的資源后,可以按照程序員設(shè)置的跳轉(zhuǎn)到別的頁面。所以有各種有個性的404錯誤頁面。

          5. 服務(wù)器返回HTTP響應(yīng),瀏覽器得到返回數(shù)據(jù)后就可以提取數(shù)據(jù),然后調(diào)用解析內(nèi)核進(jìn)行翻譯,最后顯示出頁面。之后瀏覽器會對其引用的文件比如圖片,css,js等文件不斷進(jìn)行上述過程,直到所有文件都被下載下來之后,網(wǎng)頁就會顯示出來。

          HTTP請求,http請求由三部分組成,分別是:請求行、消息報頭、請求正文。請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:

          • GET 請求獲取Request-URI所標(biāo)識的資源

          • POST 在Request-URI所標(biāo)識的資源后附加新的數(shù)據(jù)

          • HEAD 請求獲取由Request-URI所標(biāo)識的資源的響應(yīng)消息報頭

          • PUT 請求服務(wù)器存儲一個資源,并用Request-URI作為其標(biāo)識

          • DELETE 請求服務(wù)器刪除Request-URI所標(biāo)識的資源

          • TRACE 請求服務(wù)器回送收到的請求信息,主要用于測試或診斷

          • CONNECT 保留將來使用

          • OPTIONS 請求查詢服務(wù)器的性能,或者查詢與資源相關(guān)的選項和需求

          GET方法應(yīng)用舉例:在瀏覽器的地址欄中輸入網(wǎng)址的方式訪問網(wǎng)頁時,瀏覽器采用GET方法向服務(wù)器獲取資源,eg:GET /form.html HTTP/1.1 (CRLF)

          HTTP響應(yīng)也是由三個部分組成,分別是:狀態(tài)行、消息報頭、響應(yīng)正文。

          狀態(tài)行格式如下:HTTP-Version Status-Code Reason-Phrase CRLF,其中,HTTP-Version表示服務(wù)器HTTP協(xié)議的版本;Status-Code表示服務(wù)器發(fā)回的響應(yīng)狀態(tài)代碼;Reason-Phrase表示狀態(tài)代碼的文本描述。

          狀態(tài)代碼有三位數(shù)字組成,第一個數(shù)字定義了響應(yīng)的類別,且有五種可能取值:

          • 1xx:指示信息--表示請求已接收,繼續(xù)處理

          • 2xx:成功--表示請求已被成功接收、理解、接受

          • 3xx:重定向--要完成請求必須進(jìn)行更進(jìn)一步的操作

          • 4xx:客戶端錯誤--請求有語法錯誤或請求無法實現(xiàn)

          • 5xx:服務(wù)器端錯誤--服務(wù)器未能實現(xiàn)合法的請求

          常見狀態(tài)代碼、狀態(tài)描述、說明:

          200 OK //客戶端請求成功

          400 Bad Request //客戶端請求有語法錯誤,不能被服務(wù)器所理解

          401 Unauthorized //請求未經(jīng)授權(quán),這個狀態(tài)代碼必須和WWW-Authenticate報頭域一起使用

          403 Forbidden //服務(wù)器收到請求,但是拒絕提供服務(wù)

          404 Not Found //請求資源不存在,eg:輸入了錯誤的URL

          500 Internal Server Error //服務(wù)器發(fā)生不可預(yù)期的錯誤

          503 Server Unavailable //服務(wù)器當(dāng)前不能處理客戶端的請求,一段時間后可能恢復(fù)正常。

          eg:HTTP/1.1 200 OK (CRLF)

          詳細(xì)的HTTP協(xié)議可以參考這篇文章:

          http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html

          前面我們了解了HTTP協(xié)議,那么我們訪問網(wǎng)頁的過程,那么網(wǎng)頁在是什么樣子的。爬蟲眼中的網(wǎng)頁又是什么樣子的。

          網(wǎng)是靜態(tài)的,但爬蟲是動態(tài)的,所以爬蟲的基本思想就是沿著網(wǎng)頁(蜘蛛網(wǎng)的節(jié)點)上的鏈接的爬取有效信息。當(dāng)然網(wǎng)頁也有動態(tài)(一般用PHP或ASP等寫成,例如用戶登陸界面就是動態(tài)網(wǎng)頁)的,但如果一張蛛網(wǎng)搖搖欲墜,蜘蛛會感到不那么安穩(wěn),所以動態(tài)網(wǎng)頁的優(yōu)先級一般會被搜索引擎排在靜態(tài)網(wǎng)頁的后面。

          知道了爬蟲的基本思想,那么具體如何操作呢?這得從網(wǎng)頁的基本概念說起。一個網(wǎng)頁有三大構(gòu)成要素,分別是html文件、css文件和JavaScript文件。如果把一個網(wǎng)頁看做一棟房子,那么html相當(dāng)于房子外殼;css相當(dāng)于地磚涂料,美化房子外觀內(nèi)飾;JavaScript則相當(dāng)于家具電器浴池等,增加房子的功能。從上述比喻可以看出,html才是網(wǎng)頁的根本,畢竟地磚顏料在市場上也有,家具電器都可以露天擺設(shè),而房子外殼才是獨一無二的。

          下面就是一個簡單網(wǎng)頁的例子:

          而在爬蟲眼里,這個網(wǎng)頁是這樣的:

          因此網(wǎng)頁實質(zhì)上就是超文本(hypertext),網(wǎng)頁上的所有內(nèi)容都是在形如“<>...</>”這樣的標(biāo)簽之內(nèi)的。如果我們要搜集網(wǎng)頁上的所有超鏈接,只需尋找所有標(biāo)簽中前面是"href="的字符串,并查看提取出來的字符串是否以"http"(超文本轉(zhuǎn)換協(xié)議,https表示安全的http協(xié)議)開頭即可。如果超鏈接不以"http"開頭,那么該鏈接很可能是網(wǎng)頁所在的本地文件或者ftp或smtp(文件或郵件轉(zhuǎn)換協(xié)議),應(yīng)該過濾掉。

          在Python中我們使用Requests庫中的方法來幫助我們實現(xiàn)對網(wǎng)頁的請求,從而達(dá)到實現(xiàn)爬蟲的過程。

          1.2 Requests庫的7個主要方法:

          最常用的方法get用來實現(xiàn)一個簡單的小爬蟲,通過示例代碼展示:

          2. Robots協(xié)議

          Robots協(xié)議(也稱為爬蟲協(xié)議、機(jī)器人協(xié)議等)的全稱是“網(wǎng)絡(luò)爬蟲排除標(biāo)準(zhǔn)”(Robots Exclusion Protocol),網(wǎng)站通過Robots協(xié)議告訴搜索引擎哪些頁面可以抓取,哪些頁面不能抓取。通過幾個小例子來解讀一下robots.txt中的內(nèi)容,robots.txt默認(rèn)放置于網(wǎng)站的根目錄小,對于一個沒有robots.txt文件的網(wǎng)站,默認(rèn)是允許所有爬蟲獲取其網(wǎng)站內(nèi)容的。

          我們對于robots協(xié)議的理解,如果是商業(yè)利益我們是必須要遵守robots協(xié)議內(nèi)容,否則會承擔(dān)相應(yīng)的法律責(zé)任。當(dāng)只是個人玩轉(zhuǎn)網(wǎng)頁、練習(xí)則是建議遵守,提高自己編寫爬蟲的友好程度。

          3. 網(wǎng)頁解析

          BeautifulSoup嘗試化平淡為神奇,通過定位HTML標(biāo)簽來格式化和組織復(fù)雜的網(wǎng)絡(luò)信息,用簡單易用的Python對象為我們展示XML結(jié)構(gòu)信息。

          BeautifulSoup是解析、遍歷、維護(hù)“標(biāo)簽樹”的功能庫。

          3.1 BeautifulSoup的解析器

          BeautifulSoup通過以上四種解析器來對我們獲取的網(wǎng)頁內(nèi)容進(jìn)行解析。使用官網(wǎng)的例子來看一下解析結(jié)果:

          首先獲取以上的一段HTML內(nèi)容,我們通過BeautifulSoup解析之后,并且輸出解析后的結(jié)果來對比一下:

          通過解析的網(wǎng)頁內(nèi)容,我們就可以使用BeautifulSoup中的方法來輕而易舉的獲得網(wǎng)頁中的主要信息:

          3.2 BeautifulSoup類的基本元素

          3.3 BeautifulSoup的遍歷功能

          遍歷分為上行遍歷、下行遍歷、平行遍歷三種。

          • 下行遍歷:

          • 上行遍歷:

          • 平行遍歷:

          4. 正則表達(dá)式

          正則表達(dá)式,又稱規(guī)則表達(dá)式。(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計算機(jī)科學(xué)的一個概念。正則表通常被用來檢索、替換那些符合某個模式(規(guī)則)的文本。

          筆者也是初學(xué)正則表達(dá)式,感覺自己不能簡潔清晰的講述正則表達(dá)式,建議參考網(wǎng)上的教程( http://deerchao.net/tutorials/regex/regex.htm#mission )圖文并茂,詳細(xì)講解了正則表達(dá)式。

          通過掌握正則表示也可以幫助我們獲取網(wǎng)頁中的主要信息。

          5. 爬蟲框架Scrapy

          Scrapy是Python開發(fā)的一個快速,高層次的屏幕抓取和web抓取框架,用于抓取web站點并從頁面中提取結(jié)構(gòu)化的數(shù)據(jù)。Scrapy用途廣泛,可以用于數(shù)據(jù)挖掘、監(jiān)測和自動化測試。

          Scrapy吸引人的地方在于它是一個框架,任何人都可以根據(jù)需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持。

          5.1 Scrapy爬蟲框架結(jié)構(gòu)

          Engine: 控制所有模塊之間的數(shù)據(jù)流、根據(jù)條件觸發(fā)事件。

          Downloader: 根據(jù)請求下載網(wǎng)頁

          Scheduler: 對所有爬去請求進(jìn)行調(diào)度管理

          Spider: 解析Downloader返回的響應(yīng)、產(chǎn)生爬取項、產(chǎn)生額外的爬去請求。

          Item Pipelines: 以流水線方式處理Spider產(chǎn)生的爬取項、可能包括清理、檢驗和查重爬取項中的HTML數(shù)據(jù)、將數(shù)據(jù)存儲到數(shù)據(jù)庫。

          5.2 數(shù)據(jù)流

          1. 引擎打開一個網(wǎng)站(open a domain),找到處理該網(wǎng)站的Spider并向該spider請求第一個要爬取的URL(s)。

          2. 引擎從Spider中獲取到第一個要爬取的URL并在調(diào)度器(Scheduler)以Request調(diào)度。

          3. 引擎向調(diào)度器請求下一個要爬取的URL。

          4. 調(diào)度器返回下一個要爬取的URL給引擎,引擎將URL通過下載中間件(請求(request)方向)轉(zhuǎn)發(fā)給下載器(Downloader)。

          5. 一旦頁面下載完畢,下載器生成一個該頁面的Response,并將其通過下載中間件(返回(response)方向)發(fā)送給引擎。

          6. 引擎從下載器中接收到Response并通過Spider中間件(輸入方向)發(fā)送給Spider處理。

          7. Spider處理Response并返回爬取到的Item及(跟進(jìn)的)新的Request給引擎。

          8. 引擎將(Spider返回的)爬取到的Item給Item Pipeline,將(Spider返回的)Request給調(diào)度器。

          9. (從第二步)重復(fù)直到調(diào)度器中沒有更多地request,引擎關(guān)閉該網(wǎng)站。

          6. 分布式爬蟲

          6.1 多線程爬蟲

          在爬取數(shù)據(jù)量小的情況下,我們使用的都是串行下載網(wǎng)頁的,只有前一次下載完成之后才會啟動新的下載。數(shù)據(jù)量小的情況下尚可應(yīng)對。但面對大型網(wǎng)站就會顯得性能不足,如果我們可以同時下載多個網(wǎng)頁,那么下載時間將會得到顯著改善。

          我們將串行下載爬蟲擴(kuò)展成并行下載。需要注意的是如果濫用這一功能,多線程爬蟲請求內(nèi)容過快,可能會造成服務(wù)器過載,或是IP地址被封禁。為了避免這一問題,我們的爬蟲就要設(shè)置一個delay標(biāo)識,用于設(shè)定請求同一域名時的最小時間間隔。

          在Python中實現(xiàn)多線程是比較簡單的,Python中的thread模塊是比較底層的模塊,Python的threading模塊是對thread做了一些封裝,可以更加方便的被使用。

          簡要的看一下thread模塊中含函數(shù)和常量:

          Thread中常用的函數(shù)和對象:

          一般來說,使用線程有兩種模式, 一種是創(chuàng)建線程要執(zhí)行的函數(shù), 把這個函數(shù)傳遞進(jìn)Thread對象里,讓它來執(zhí)行. 另一種是直接從Thread繼承,創(chuàng)建一個新的class,把線程執(zhí)行的代碼放到這個新的class里。

          實現(xiàn)多進(jìn)程的代碼和例子參考:

          http://www.jianshu.com/p/86b8e78c418a

          6.2 多進(jìn)程爬蟲

          Python中的多線程其實并不是真正的多線程,并不能做到充分利用多核CPU資源。

          如果想要充分利用,在python中大部分情況需要使用多進(jìn)程,那么這個包就叫做 multiprocessing。

          借助它,可以輕松完成從單進(jìn)程到并發(fā)執(zhí)行的轉(zhuǎn)換。multiprocessing支持子進(jìn)程、通信和共享數(shù)據(jù)、執(zhí)行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。

          Process基本使用:

          在multiprocessing中,每一個進(jìn)程都用一個Process類來表示。首先看下它的API:

          • target 表示調(diào)用對象,你可以傳入方法的名字

          • args 表示被調(diào)用對象的位置參數(shù)元組,比如target是函數(shù)a,他有兩個參數(shù)m,n,那么args就傳入(m, n)即可

          • kwargs 表示調(diào)用對象的字典

          • name 是別名,相當(dāng)于給這個進(jìn)程取一個名字

          • group 分組,實際上不使用

          我們先用一個實例來感受一下:

          最簡單的創(chuàng)建Process的過程如上所示,target傳入函數(shù)名,args是函數(shù)的參數(shù),是元組的形式,如果只有一個參數(shù),那就是長度為1的元組。

          然后調(diào)用start()方法即可啟動多個進(jìn)程了。

          另外你還可以通過 cpu_count() 方法還有 active_children() 方法獲取當(dāng)前機(jī)器的 CPU 核心數(shù)量以及得到目前所有的運(yùn)行的進(jìn)程。

          通過一個實例來感受一下:

          運(yùn)行結(jié)果:

          通過開啟多個進(jìn)程實現(xiàn)爬蟲,會大大縮減爬取信息的速度。詳細(xì)介紹請參考:

          http://cuiqingcai.com/3335.html

          7. 異步網(wǎng)站數(shù)據(jù)采集

          在收集網(wǎng)頁信息時我們會遇到,網(wǎng)頁的加載模型為瀑布流形式,頁面URL沒有改變,但依然可以加載出內(nèi)容。這時候就需要我們分析網(wǎng)頁中JavaScript中的一些代碼,從中獲取我們所需要的數(shù)據(jù)。

          面對使用JS渲染的頁面推薦使用PhantomJS,無界面,可腳本編程的WebKit瀏覽器。參考 : http://cuiqingcai.com/2577.html

          Selenium一種自動化測試工具??梢苑奖銓崿F(xiàn)Web界面測試。使用PhantomJS渲染解析JS,Selenium用來驅(qū)動以及寫與Python的對接,然后Python進(jìn)行后期處理。參考: http://cuiqingcai.com/2599.html

          8. 爬蟲的存儲

          在剛開始接觸爬蟲的時候,我們習(xí)慣將小的爬蟲結(jié)果輸出在命令行中,看著命令行中一行行的數(shù)據(jù)顯得頗有成就感,但是隨著數(shù)據(jù)的增多,并且需要進(jìn)行數(shù)據(jù)分析時,將數(shù)據(jù)打印到命令行就不是辦法了。為了可以遠(yuǎn)程使用大部分網(wǎng)絡(luò)爬蟲,我們還是需要將收集的數(shù)據(jù)存儲起來。

          8.1 媒體文件

          媒體文件常見的有兩種存儲方式:只獲取URL鏈接,或者直接把源文件下載下來。但是推薦使用第一種方式。優(yōu)點如下:

          • 爬蟲運(yùn)行的更快,耗費(fèi)的流量更少,因為只存儲鏈接,不需要下載文件。

          • 節(jié)省存儲空間,因為不需要存儲媒體文件。

          • 存儲URL的代碼更容易寫,也不需要實現(xiàn)文件下載代碼

          • 不下載文件能夠降低目標(biāo)主機(jī)服務(wù)器的負(fù)載。

          當(dāng)然這樣做也存在一些缺點:

          • 內(nèi)嵌在我們網(wǎng)頁中的外站鏈接被稱為盜鏈,使用這種鏈接會讓我們麻煩不斷,每個網(wǎng)站都會實施防盜鏈措施。

          • 因為你的鏈接文件在別人的服務(wù)器,所以我們的應(yīng)用就要跟著別人的節(jié)奏運(yùn)行了。

          • 盜鏈很容易改變,如果把盜鏈放在博客等地,被對方發(fā)現(xiàn)很可能被惡搞?;蛘呤前裊RL存儲備用,等到用的時候發(fā)現(xiàn)鏈接已經(jīng)過期了。

          • 在現(xiàn)實中網(wǎng)絡(luò)瀏覽器不僅可以訪問HTML頁面并切換頁面,它們也會下載訪問頁面上的所有資源。下載文件會讓我們的爬蟲看起來更像人在瀏覽頁面。

          8.2 把數(shù)據(jù)存儲到CSV

          CSV是存儲表格數(shù)據(jù)的常用文件格式。每行都用一個換行符分隔,列與列之間用逗號分隔。Python中的CSV庫可以非常簡單的修改CSV文件,也可以從零開始創(chuàng)建一個CSV文件:

          我們可以使用csv模塊提供的功能將爬蟲獲取的信息存入csv文件中。

          8.3 MySQL

          對于大量的爬蟲數(shù)據(jù),并且在之后我們需要反復(fù)用來篩選分析的數(shù)據(jù),我們選擇存儲在數(shù)據(jù)庫中。

          MySQL是目前最受歡迎的開源關(guān)系型數(shù)據(jù)庫管理系統(tǒng),它是一種非常靈活、穩(wěn)定、功能齊全的DBMS,許多頂級網(wǎng)站都在用它,YouTube、Twitter、Facebook等。

          Python中沒有內(nèi)置的MySQL支持工具,不過,有很多開源的庫可以用來與MySQL做交互,最為出名的就是PyMySQL。

          結(jié)合上述過程將爬蟲獲取到的數(shù)據(jù)存入數(shù)據(jù)庫中。

          9. 爬蟲的常見技巧

          9.1 模擬登錄

          目前的網(wǎng)站多是采用cookie跟蹤用戶是否已經(jīng)登錄的信息。一旦網(wǎng)站驗證了你的登錄權(quán)證,它就會保存在你瀏覽器的cookie中,里面通常包含一個服務(wù)器生成的命令牌、登錄有效時限和狀態(tài)跟蹤信息。網(wǎng)站會把這個cookie當(dāng)作信息驗證的證據(jù),在我們?yōu)g覽網(wǎng)站的每個頁面時出示給服務(wù)器。

          通過Chrome等瀏覽器自帶的開發(fā)者工具,我們從Network中獲取請求網(wǎng)頁的頭部和表單,在Header中我們就可以查看cookie中存儲的登錄信息,我們可以通過Scrapy設(shè)置請求網(wǎng)頁的頭部信息,并將cookie存儲在本地,來實現(xiàn)模擬登陸的效果。詳細(xì)的操作可以查看博客:http://www.jianshu.com /p/b7f41df6202d

          9.2 網(wǎng)頁驗證碼

          簡單的說,驗證碼就是一張圖片,圖片上有字符串。網(wǎng)站是如何實現(xiàn)的呢?有WEB基礎(chǔ)的人可能會知道,每個瀏覽器基本都有cookie,作為這次回話的唯一標(biāo)示。每次訪問網(wǎng)站,瀏覽器都會把這個cookie發(fā)送給服務(wù)器。驗證碼就是和這個cookie綁定到一起的。如何理解呢?舉個例子,現(xiàn)在有網(wǎng)站W(wǎng),有A和B兩個人,同時訪問W,W給A返回的驗證碼是X,給B返回的驗證碼是Y,這兩個驗證碼都是正確的,但是如果A輸入了B的驗證碼,肯定驗證不通過。那服務(wù)器是怎么區(qū)分A和B呢,就是用到的cookie。再舉個例子,有些網(wǎng)站你登錄一次之后,下次繼續(xù)訪問可能就自動登陸了,也是用cookie來標(biāo)示唯一身份的,如果清除了cookie也就無法自動登陸了。

          對于一些簡單的驗證碼我們可以通過機(jī)器識別,但是對于一些人眼都很難識別的驗證碼就只能尋找更加復(fù)雜的技術(shù)了。簡單的驗證碼識別過程就是對驗證碼圖片的一個處理過程。

          • 灰度圖轉(zhuǎn)換,可以結(jié)合opencv中的imread方法。

          • 圖像去噪(均值濾波器、高斯濾波器等等)。

          • 圖像二值化(這個過程中驗證碼中的字符串已經(jīng)成為黑色的,底色為白色)。

          • 使用圖像識別方式,識別圖中的字符串達(dá)到識別驗證碼的目的。

          推薦閱讀:

          http://www.jianshu.com/p/dd699561671b

          http://www.cnblogs.com/hearzeus/p/5166299.html(上篇)

          http://www.cnblogs.com/hearzeus/p/5226546.html(下篇)

          9.3 爬蟲代理池

          由于筆者是個爬蟲初學(xué)者也沒有用到過這么復(fù)雜的技術(shù),不過筆者在爬蟲的過程中的確是體會了被封IP地址的痛苦。所以推薦大家有精力的可以來學(xué)習(xí)并完成一個。

          推薦閱讀:

          https://www.zhihu.com/question/47464143

          10. 防爬蟲

          由于暴力爬蟲會對網(wǎng)站的服務(wù)器產(chǎn)生很大的壓力,所以各個網(wǎng)站對爬蟲都有限制,大多數(shù)網(wǎng)站會定義robots.txt.文件可以讓爬蟲了解該網(wǎng)站的限制。限制是作為建議給出。但是爬蟲前檢查該文件可以最小化我們的爬蟲被封禁的可能。

          一篇關(guān)于反爬蟲的文章: https://segmentfault.com/a/ 1190000005840672 (來自攜程技術(shù)中心)

          11. 學(xué)習(xí)資料

          推薦書籍:

          • 《Python網(wǎng)絡(luò)數(shù)據(jù)采集》 陶俊杰、陳小莉 譯

          • 《用Python寫網(wǎng)絡(luò)爬蟲》 李斌 譯

          推薦博客:

          • 崔慶才得個人博客,有大量關(guān)于爬蟲的文章,而且講解的比較細(xì)致。

            http://cuiqingcai.com/

          • 數(shù)據(jù)挖掘與入門實戰(zhàn)微信公眾號分享的一篇文章,《Python開源爬蟲項目代碼:抓取淘寶、京東、QQ、知網(wǎng)數(shù)據(jù)》,有十九個開源的爬蟲項目,可以給大家提供參考。https://github.com/hlpassion/blog/issues/6

          推薦視頻:

          • 網(wǎng)易云課堂,例子清晰,可以跟做。

            http://study.163.com/course/introduction.htm?courseId=1002794001#/courseDetail

          • Python網(wǎng)絡(luò)爬蟲與信息提取

            http://www.icourse163.org/course/BIT-1001870001

          • 更多精彩請關(guān)注清華-青島數(shù)據(jù)科學(xué)研究院微信官方公眾平臺“THU數(shù)據(jù)派”

          、前言

          強(qiáng)烈建議:請在電腦的陪同下,閱讀本文。本文以實戰(zhàn)為主,閱讀過程如稍有不適,還望多加練習(xí)。

          本文的實戰(zhàn)內(nèi)容有:

          • 網(wǎng)絡(luò)小說下載(靜態(tài)網(wǎng)站)
          • 優(yōu)美壁紙下載(動態(tài)網(wǎng)站)
          • 愛奇藝VIP視頻下載

          二、網(wǎng)絡(luò)爬蟲簡介

          網(wǎng)絡(luò)爬蟲,也叫網(wǎng)絡(luò)蜘蛛(Web Spider)。它根據(jù)網(wǎng)頁地址(URL)爬取網(wǎng)頁內(nèi)容,而網(wǎng)頁地址(URL)就是我們在瀏覽器中輸入的網(wǎng)站鏈接。比如:https://www.baidu.com/,它就是一個URL。

          在講解爬蟲內(nèi)容之前,我們需要先學(xué)習(xí)一項寫爬蟲的必備技能:審查元素(如果已掌握,可跳過此部分內(nèi)容)。

          1. 審查元素

          在瀏覽器的地址欄輸入URL地址,在網(wǎng)頁處右鍵單擊,找到檢查,如下圖所示:(不同瀏覽器的叫法不同,Chrome瀏覽器叫做檢查,F(xiàn)irefox瀏覽器叫做查看元素,但是功能都是相同的)

          我們可以看到,右側(cè)出現(xiàn)了一大推代碼,這些代碼就叫做HTML。什么是HTML?舉個容易理解的例子:我們的基因決定了我們的原始容貌,服務(wù)器返回的HTML決定了網(wǎng)站的原始容貌。

          為啥說是原始容貌呢?因為人可以整容??!扎心了,有木有?那網(wǎng)站也可以"整容"嗎?可以!請看下圖:

          我能有這么多錢嗎?顯然不可能。我是怎么給網(wǎng)站"整容"的呢?就是通過修改服務(wù)器返回的HTML信息。我們每個人都是"整容大師",可以修改頁面信息。我們在頁面的哪個位置點擊審查元素,瀏覽器就會為我們定位到相應(yīng)的HTML位置,進(jìn)而就可以在本地更改HTML信息。

          再舉個小例子:我們都知道,使用瀏覽器"記住密碼"的功能,密碼會變成一堆小黑點,是不可見的。可以讓密碼顯示出來嗎?可以,只需給頁面"動個小手術(shù)"!以淘寶為例,在輸入密碼框處右鍵,點擊檢查。

          可以看到,瀏覽器為我們自動定位到了相應(yīng)的HTML位置。將下圖中的password屬性值改為text屬性值(直接在右側(cè)代碼處修改):

          就這樣,瀏覽器"記住的密碼"顯現(xiàn)出來了:

          說這么多,什么意思呢?瀏覽器就是作為客戶端從服務(wù)器端獲取信息,然后將信息解析,并展示給我們的。我們可以在本地修改HTML信息,為網(wǎng)頁"整容",但是我們修改的信息不會回傳到服務(wù)器,服務(wù)器存儲的HTML信息不會改變。刷新一下界面,頁面還會回到原本的樣子。這就跟人整容一樣,我們能改變一些表面的東西,但是不能改變我們的基因。

          2. 簡單實例

          網(wǎng)絡(luò)爬蟲的第一步就是根據(jù)URL,獲取網(wǎng)頁的HTML信息。在Python3中,可以使用urllib.request和requests進(jìn)行網(wǎng)頁爬取。

          • urllib庫是python內(nèi)置的,無需我們額外安裝,只要安裝了Python就可以使用這個庫。
          • requests庫是第三方庫,需要我們自己安裝。

          requests庫強(qiáng)大好用,所以本文使用requests庫獲取網(wǎng)頁的HTML信息。requests庫的github地址:https://github.com/requests/requests

          (1)requests安裝

          在學(xué)習(xí)使用requests庫之前,我們需要在電腦中安裝好requests庫。在cmd中,使用如下指令安裝requests庫:

          • pip install requests
          • easy_install requests

          使用pip和easy_install都可以安裝,二選一即可。

          (2)簡單實例

          安裝好requests庫之后,我們先來大體瀏覽一下requests庫的基礎(chǔ)方法:

          官方中文教程地址:

          http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

          requests庫的開發(fā)者為我們提供了詳細(xì)的中文教程,查詢起來很方便。本文不會對其所有內(nèi)容進(jìn)行講解,摘取其部分使用到的內(nèi)容,進(jìn)行實戰(zhàn)說明。

          首先,讓我們看下requests.get()方法,它用于向服務(wù)器發(fā)起GET請求,不了解GET請求沒有關(guān)系。我們可以這樣理解:get的中文意思是得到、抓住,那這個requests.get()方法就是從服務(wù)器得到、抓住數(shù)據(jù),也就是獲取數(shù)據(jù)。讓我們看一個例子(以 www.gitbook.cn為例)來加深理解:

              # -*- coding:UTF-8 -*-
              import requests
              if __name__ == '__main__':
                  target = 'http://gitbook.cn/'
                  req = requests.get(url=target)
                  print(req.text)
          

          requests.get()方法必須設(shè)置的一個參數(shù)就是url,因為我們得告訴GET請求,我們的目標(biāo)是誰,我們要獲取誰的信息。我們將GET請求獲得的響應(yīng)內(nèi)容存放到req變量中,然后使用req.text就可以獲得HTML信息了。運(yùn)行結(jié)果如下:

          左側(cè)是我們程序獲得的結(jié)果,右側(cè)是我們在www.gitbook.cn網(wǎng)站審查元素獲得的信息。我們可以看到,我們已經(jīng)順利獲得了該網(wǎng)頁的HTML信息。這就是一個最簡單的爬蟲實例,可能你會問,我只是爬取了這個網(wǎng)頁的HTML信息,有什么用呢?客官稍安勿躁,接下來進(jìn)入我們的實戰(zhàn)正文。

          三、爬蟲實戰(zhàn)

          實戰(zhàn)內(nèi)容由簡單到復(fù)雜,難度逐漸增加,但均屬于入門級難度。下面開始我們的第一個實戰(zhàn)內(nèi)容:網(wǎng)絡(luò)小說下載。

          1. 小說下載

          (1)實戰(zhàn)背景

          小說網(wǎng)站《筆趣看》URL:http://www.biqukan.com/

          《筆趣看》是一個盜版小說網(wǎng)站,這里有很多起點中文網(wǎng)的小說,該網(wǎng)站小說的更新速度稍滯后于起點中文網(wǎng)正版小說的更新速度。并且該網(wǎng)站只支持在線瀏覽,不支持小說打包下載。因此,本次實戰(zhàn)就是從該網(wǎng)站爬取并保存一本名為《一念永恒》的小說,該小說是耳根正在連載中的一部玄幻小說。PS:本實例僅為交流學(xué)習(xí),支持耳根大大,請上起點中文網(wǎng)訂閱。

          (2)小試牛刀

          我們先看下《一念永恒》小說的第一章內(nèi)容,URL:http://www.biqukan.com/1_1094/5403177.html

          用已經(jīng)學(xué)到的知識獲取HTML信息試一試,編寫代碼如下:

              # -*- coding:UTF-8 -*-
              import requests
          
              if __name__ == '__main__':
                  target = 'http://www.biqukan.com/1_1094/5403177.html'
                  req = requests.get(url=target)
                  print(req.text)
          

          運(yùn)行代碼,可以看到如下結(jié)果:

          可以看到,我們很輕松地獲取了HTML信息。但是,很顯然,很多信息是我們不想看到的,我們只想獲得如右側(cè)所示的正文內(nèi)容,我們不關(guān)心那些看著眼暈的英文字母。如何把正文內(nèi)容從這些眾多的HTML信息中提取出來呢?這就是本小節(jié)實戰(zhàn)的主要內(nèi)容。

          (3)Beautiful Soup

          爬蟲的第一步,獲取整個網(wǎng)頁的HTML信息,我們已經(jīng)完成。接下來就是爬蟲的第二步,解析HTML信息,提取我們感興趣的內(nèi)容。對于本小節(jié)的實戰(zhàn),我們感興趣的內(nèi)容就是文章的正文。提取的方法有很多,例如使用正則表達(dá)式、Xpath、Beautiful Soup等。對于初學(xué)者而言,最容易理解,并且使用簡單的方法就是使用Beautiful Soup提取感興趣內(nèi)容。

          Beautiful Soup的安裝方法和requests一樣,使用如下指令安裝(也是二選一):

          • pip install beautifulsoup4
          • easy_install beautifulsoup4

          一個強(qiáng)大的第三方庫,都會有一個詳細(xì)的官方文檔。我們很幸運(yùn),Beautiful Soup也是有中文的官方文檔。URL:

          http://beautifulsoup.readthedocs.io/zh_CN/latest/

          同理,我會根據(jù)實戰(zhàn)需求,講解Beautiful Soup庫的部分使用方法,更詳細(xì)的內(nèi)容,請查看官方文檔。

          現(xiàn)在,我們使用已經(jīng)掌握的審查元素方法,查看一下我們的目標(biāo)頁面,你會看到如下內(nèi)容:

          不難發(fā)現(xiàn),文章的所有內(nèi)容都放在了一個名為div的“東西下面”,這個"東西"就是html標(biāo)簽。HTML標(biāo)簽是HTML語言中最基本的單位,HTML標(biāo)簽是HTML最重要的組成部分。不理解,沒關(guān)系,我們再舉個簡單的例子:

          • 一個女人的包包里,會有很多東西,她們會根據(jù)自己的習(xí)慣將自己的東西進(jìn)行分類放好。鏡子和口紅這些會經(jīng)常用到的東西,會歸放到容易拿到的外側(cè)口袋里。那些不經(jīng)常用到,需要注意安全存放的證件會放到不容易拿到的里側(cè)口袋里。

          html標(biāo)簽就像一個個“口袋”,每個“口袋”都有自己的特定功能,負(fù)責(zé)存放不同的內(nèi)容。顯然,上述例子中的div標(biāo)簽下存放了我們關(guān)心的正文內(nèi)容。這個div標(biāo)簽是這樣的:

              <div id="content", class="showtxt">
          

          細(xì)心的朋友可能已經(jīng)發(fā)現(xiàn),除了div字樣外,還有id和class。id和class就是div標(biāo)簽的屬性,content和showtxt是屬性值,一個屬性對應(yīng)一個屬性值。這東西有什么用?它是用來區(qū)分不同的div標(biāo)簽的,因為div標(biāo)簽可以有很多,我們怎么加以區(qū)分不同的div標(biāo)簽?zāi)??就是通過不同的屬性值。

          仔細(xì)觀察目標(biāo)網(wǎng)站一番,我們會發(fā)現(xiàn)這樣一個事實:class屬性為showtxt的div標(biāo)簽,獨一份!這個標(biāo)簽里面存放的內(nèi)容,是我們關(guān)心的正文部分。

          知道這個信息,我們就可以使用Beautiful Soup提取我們想要的內(nèi)容了,編寫代碼如下:

              # -*- coding:UTF-8 -*-
              from bs4 import BeautifulSoup
              import requests
          
              if __name__ == "__main__":
                  target = 'http://www.biqukan.com/1_1094/5403177.html'
                  req = requests.get(url = target)
                  html = req.text
                  bf = BeautifulSoup(html)
                  texts = bf.find_all('div', class_ = 'showtxt')
                  print(texts)
          

          在解析html之前,我們需要創(chuàng)建一個Beautiful Soup對象。BeautifulSoup函數(shù)里的參數(shù)就是我們已經(jīng)獲得的html信息。然后我們使用find_all方法,獲得html信息中所有class屬性為showtxt的div標(biāo)簽。find_all方法的第一個參數(shù)是獲取的標(biāo)簽名,第二個參數(shù)class_是標(biāo)簽的屬性,為什么不是class,而帶了一個下劃線呢?因為python中class是關(guān)鍵字,為了防止沖突,這里使用class_表示標(biāo)簽的class屬性,class_后面跟著的showtxt就是屬性值了??聪挛覀円ヅ涞臉?biāo)簽格式:

              <div id="content", class="showtxt">     
          

          這樣對應(yīng)的看一下,是不是就懂了?可能有人會問了,為什么不是find_all('div', id = 'content', class_ = 'showtxt')?這樣其實也是可以的,屬性是作為查詢時候的約束條件,添加一個class_='showtxt'條件,我們就已經(jīng)能夠準(zhǔn)確匹配到我們想要的標(biāo)簽了,所以我們就不必再添加id這個屬性了。運(yùn)行代碼查看我們匹配的結(jié)果:

          我們可以看到,我們已經(jīng)順利匹配到我們關(guān)心的正文內(nèi)容,但是還有一些我們不想要的東西。比如div標(biāo)簽名,br標(biāo)簽,以及各種空格。怎么去除這些東西呢?我們繼續(xù)編寫代碼:

              # -*- coding:UTF-8 -*-
              from bs4 import BeautifulSoup
              import requests
          
              if __name__ == "__main__":
                  target = 'http://www.biqukan.com/1_1094/5403177.html'
                  req = requests.get(url = target)
                  html = req.text
                  bf = BeautifulSoup(html)
                  texts = bf.find_all('div', class_ = 'showtxt')
                  print(texts[0].text.replace('\xa0'*8,'\n\n'))
          

          find_all匹配的返回的結(jié)果是一個列表。提取匹配結(jié)果后,使用text屬性,提取文本內(nèi)容,濾除br標(biāo)簽。隨后使用replace方法,剔除空格,替換為回車進(jìn)行分段。 在html中是用來表示空格的。replace('\xa0'*8,'\n\n')就是去掉下圖的八個空格符號,并用回車代替:

          程序運(yùn)行結(jié)果如下:

          可以看到,我們很自然的匹配到了所有正文內(nèi)容,并進(jìn)行了分段。我們已經(jīng)順利獲得了一個章節(jié)的內(nèi)容,要想下載正本小說,我們就要獲取每個章節(jié)的鏈接。我們先分析下小說目錄:

          URL:http://www.biqukan.com/1_1094/

          通過審查元素,我們發(fā)現(xiàn)可以發(fā)現(xiàn),這些章節(jié)都存放在了class屬性為listmain的div標(biāo)簽下,選取部分html代碼如下:

              <div class="listmain">
              <dl>
              <dt>《一念永恒》最新章節(jié)列表</dt>
              <dd><a href="/1_1094/15932394.html">第1027章 第十道門</a></dd>
              <dd><a href="/1_1094/15923072.html">第1026章 絕倫道法!</a></dd>
              <dd><a href="/1_1094/15921862.html">第1025章 長生燈!</a></dd>
              <dd><a href="/1_1094/15918591.html">第1024章 一目晶淵</a></dd>
              <dd><a href="/1_1094/15906236.html">第1023章 通天道門</a></dd>
              <dd><a href="/1_1094/15903775.html">第1022章 四大兇獸!</a></dd>
              <dd><a href="/1_1094/15890427.html">第1021章 鱷首!</a></dd>
              <dd><a href="/1_1094/15886627.html">第1020章 一觸即發(fā)!</a></dd>
              <dd><a href="/1_1094/15875306.html">第1019章 魁祖的氣息!</a></dd>
              <dd><a href="/1_1094/15871572.html">第1018章 絕望的魁皇城</a></dd>
              <dd><a href="/1_1094/15859514.html">第1017章 我還是恨你!</a></dd>
              <dd><a href="/1_1094/15856137.html">第1016章 從來沒有世界之門!</a></dd>
              <dt>《一念永恒》正文卷</dt>
              <dd><a href="/1_1094/5386269.html">外傳1 柯父。</a></dd>
              <dd><a href="/1_1094/5386270.html">外傳2 楚玉嫣。</a></dd>
              <dd><a href="/1_1094/5386271.html">外傳3 鸚鵡與皮凍。</a></dd>
              <dd><a href="/1_1094/5403177.html">第一章 他叫白小純</a></dd>
              <dd><a href="/1_1094/5428081.html">第二章 火灶房</a></dd>
              <dd><a href="/1_1094/5433843.html">第三章 六句真言</a></dd>
              <dd><a href="/1_1094/5447905.html">第四章 煉靈</a></dd>
              </dl>
              </div>
          

          在分析之前,讓我們先介紹一個概念:父節(jié)點、子節(jié)點、孫節(jié)點。<div>和</div>限定了<div>標(biāo)簽的開始和結(jié)束的位置,他們是成對出現(xiàn)的,有開始位置,就有結(jié)束位置。我們可以看到,在<div>標(biāo)簽包含<dl>標(biāo)簽,那這個<dl>標(biāo)簽就是<div>標(biāo)簽的子節(jié)點,<dl>標(biāo)簽又包含<dt>標(biāo)簽和<dd>標(biāo)簽,那么<dt>標(biāo)簽和<dd>標(biāo)簽就是<div>標(biāo)簽的孫節(jié)點。有點繞?那你記住這句話:誰包含誰,誰就是誰兒子!

          他們之間的關(guān)系都是相對的。比如對于<dd>標(biāo)簽,它的子節(jié)點是<a>標(biāo)簽,它的父節(jié)點是<dl>標(biāo)簽。這跟我們?nèi)耸且粯拥模嫌欣舷掠行 ?/span>

          看到這里可能有人會問,這有好多<dd>標(biāo)簽和<a>標(biāo)簽啊!不同的<dd>標(biāo)簽,它們是什么關(guān)系啊?顯然,兄弟姐妹嘍!我們稱它們?yōu)樾值芙Y(jié)點。

          好了,概念明確清楚,接下來,讓我們分析一下問題。我們看到每個章節(jié)的名字存放在了<a>標(biāo)簽里面。<a>標(biāo)簽還有一個href屬性。這里就不得不提一下<a> 標(biāo)簽的定義了,<a> 標(biāo)簽定義了一個超鏈接,用于從一張頁面鏈接到另一張頁面。<a> 標(biāo)簽最重要的屬性是 href 屬性,它指示鏈接的目標(biāo)。

          我們將之前獲得的第一章節(jié)的URL和<a> 標(biāo)簽對比看一下:

              http://www.biqukan.com/1_1094/5403177.html
              <a href="/1_1094/5403177.html">第一章 他叫白小純</a>
          

          不難發(fā)現(xiàn),<a> 標(biāo)簽中href屬性存放的屬性值/1_1094/5403177.html是章節(jié)URLhttp://www.biqukan.com/1_1094/5403177.html的后半部分。其他章節(jié)也是如此!那這樣,我們就可以根據(jù)<a> 標(biāo)簽的href屬性值獲得每個章節(jié)的鏈接和名稱了。

          總結(jié)一下:小說每章的鏈接放在了class屬性為listmain的<div>標(biāo)簽下的<a>標(biāo)簽中。鏈接具體位置放在html->body->div->dl->dd->a的href屬性中。先匹配class屬性為listmain的<div>標(biāo)簽,再匹配<a>標(biāo)簽。編寫代碼如下:

              # -*- coding:UTF-8 -*-
              from bs4 import BeautifulSoup
              import requests
          
              if __name__ == "__main__":
                  target = 'http://www.biqukan.com/1_1094/'
                  req = requests.get(url = target)
                  html = req.text
                  div_bf = BeautifulSoup(html)
                  div = div_bf.find_all('div', class_ = 'listmain')
                  print(div[0])
          

          還是使用find_all方法,運(yùn)行結(jié)果如下:

          很順利,接下來再匹配每一個<a>標(biāo)簽,并提取章節(jié)名和章節(jié)文章。如果我們使用Beautiful Soup匹配到了下面這個<a>標(biāo)簽,如何提取它的href屬性和<a>標(biāo)簽里存放的章節(jié)名呢?

          <a href="/1_1094/5403177.html">第一章 他叫白小純</a>

          方法很簡單,對Beautiful Soup返回的匹配結(jié)果a,使用a.get('href')方法就能獲取href的屬性值,使用a.string就能獲取章節(jié)名,編寫代碼如下:

              # -*- coding:UTF-8 -*-
              from bs4 import BeautifulSoup
              import requests
          
              if __name__ == "__main__":
                  server = 'http://www.biqukan.com/'
                  target = 'http://www.biqukan.com/1_1094/'
                  req = requests.get(url = target)
                  html = req.text
                  div_bf = BeautifulSoup(html)
                  div = div_bf.find_all('div', class_ = 'listmain')
                  a_bf = BeautifulSoup(str(div[0]))
                  a = a_bf.find_all('a')
                  for each in a:
                      print(each.string, server + each.get('href'))
          

          因為find_all返回的是一個列表,里邊存放了很多的<a>標(biāo)簽,所以使用for循環(huán)遍歷每個<a>標(biāo)簽并打印出來,運(yùn)行結(jié)果如下。

          最上面匹配的一千多章的內(nèi)容是最新更新的12章節(jié)的鏈接。這12章內(nèi)容會和下面的重復(fù),所以我們要濾除,除此之外,還有那3個外傳,我們也不想要。這些都簡單地剔除就好。

          (3)整合代碼

          每個章節(jié)的鏈接、章節(jié)名、章節(jié)內(nèi)容都有了。接下來就是整合代碼,將獲得內(nèi)容寫入文本文件存儲就好了。編寫代碼如下:

              # -*- coding:UTF-8 -*-
              from bs4 import BeautifulSoup
              import requests, sys
          
              """
              類說明:下載《筆趣看》網(wǎng)小說《一念永恒》
              Parameters:
                  無
              Returns:
                  無
              Modify:
                  2017-09-13
              """
              class downloader(object):
          
                  def __init__(self):
                      self.server = 'http://www.biqukan.com/'
                      self.target = 'http://www.biqukan.com/1_1094/'
                      self.names = []         #存放章節(jié)名
                      self.urls = []          #存放章節(jié)鏈接
                      self.nums = 0           #章節(jié)數(shù)
          
                  """
                  函數(shù)說明:獲取下載鏈接
                  Parameters:
                      無
                  Returns:
                      無
                  Modify:
                      2017-09-13
                  """
                  def get_download_url(self):
                      req = requests.get(url = self.target)
                      html = req.text
                      div_bf = BeautifulSoup(html)
                      div = div_bf.find_all('div', class_ = 'listmain')
                      a_bf = BeautifulSoup(str(div[0]))
                      a = a_bf.find_all('a')
                      self.nums = len(a[15:])                             #剔除不必要的章節(jié),并統(tǒng)計章節(jié)數(shù)
                      for each in a[15:]:
                          self.names.append(each.string)
                          self.urls.append(self.server + each.get('href'))
          
                  """
                  函數(shù)說明:獲取章節(jié)內(nèi)容
                  Parameters:
                      target - 下載連接(string)
                  Returns:
                      texts - 章節(jié)內(nèi)容(string)
                  Modify:
                      2017-09-13
                  """
                  def get_contents(self, target):
                      req = requests.get(url = target)
                      html = req.text
                      bf = BeautifulSoup(html)
                      texts = bf.find_all('div', class_ = 'showtxt')
                      texts = texts[0].text.replace('\xa0'*8,'\n\n')
                      return texts
          
                  """
                  函數(shù)說明:將爬取的文章內(nèi)容寫入文件
                  Parameters:
                      name - 章節(jié)名稱(string)
                      path - 當(dāng)前路徑下,小說保存名稱(string)
                      text - 章節(jié)內(nèi)容(string)
                  Returns:
                      無
                  Modify:
                      2017-09-13
                  """
                  def writer(self, name, path, text):
                      write_flag = True
                      with open(path, 'a', encoding='utf-8') as f:
                          f.write(name + '\n')
                          f.writelines(text)
                          f.write('\n\n')
          
              if __name__ == "__main__":
                  dl = downloader()
                  dl.get_download_url()
                  print('《一年永恒》開始下載:')
                  for i in range(dl.nums):
                      dl.writer(dl.names[i], '一念永恒.txt', dl.get_contents(dl.urls[i]))
                      sys.stdout.write("  已下載:%.3f%%" %  float(i/dl.nums) + '\r')
                      sys.stdout.flush()
                  print('《一年永恒》下載完成')
          

          很簡單的程序,單進(jìn)程跑,沒有開進(jìn)程池。下載速度略慢,喝杯茶休息休息吧。代碼運(yùn)行效果如下圖所示:

          2. 優(yōu)美壁紙下載

          (1)實戰(zhàn)背景

          已經(jīng)會爬取文字了,是不是感覺爬蟲還是蠻好玩的呢?接下來,讓我們進(jìn)行一個進(jìn)階實戰(zhàn),了解一下反爬蟲。

          URL:https://unsplash.com/

          看一看這些優(yōu)美的壁紙,這個網(wǎng)站的名字叫做Unsplash,免費(fèi)高清壁紙分享網(wǎng)是一個堅持每天分享高清的攝影圖片的站點,每天更新一張高質(zhì)量的圖片素材,全是生活中的景象作品,清新的生活氣息圖片可以作為桌面壁紙也可以應(yīng)用于各種需要的環(huán)境。

          看到這么優(yōu)美的圖片,我的第一反應(yīng)就是想收藏一些,作為知乎文章的題圖再好不過了。每張圖片我都很喜歡,批量下載吧,不多爬,就下載50張好了。

          (2)實戰(zhàn)進(jìn)階

          我們已經(jīng)知道了每個html標(biāo)簽都有各自的功能。<a>標(biāo)簽存放一下超鏈接,圖片存放在哪個標(biāo)簽里呢?html規(guī)定,圖片統(tǒng)統(tǒng)給我放到<img>標(biāo)簽中!既然這樣,我們截取就Unsplash網(wǎng)站中的一個<img>標(biāo)簽,分析一下:

              <img alt="Snow-capped mountain slopes under blue sky" src="https://images.unsplash.com/photo-1428509774491-cfac96e12253?dpr=1&auto=compress,format&fit=crop&w=360&h=240&q=80&cs=tinysrgb&crop=" class="cV68d" style="width: 220px; height: 147px;">
          

          可以看到,<img>標(biāo)簽有很多屬性,有alt、src、class、style屬性,其中src屬性存放的就是我們需要的圖片保存地址,我們根據(jù)這個地址就可以進(jìn)行圖片的下載。

          那么,讓我們先捋一捋這個過程:

          1. 使用requeusts獲取整個網(wǎng)頁的HTML信息;
          2. 使用Beautiful Soup解析HTML信息,找到所有<img>標(biāo)簽,提取src屬性,獲取圖片存放地址;
          3. 根據(jù)圖片存放地址,下載圖片。

          我們信心滿滿地按照這個思路爬取Unsplash試一試,編寫代碼如下:

              # -*- coding:UTF-8 -*-
              import requests
          
              if __name__ == '__main__':
                  target = 'https://unsplash.com/'
                  req = requests.get(url=target)
                  print(req.text)
          

          按照我們的設(shè)想,我們應(yīng)該能找到很多<img>標(biāo)簽。但是我們發(fā)現(xiàn),除了一些<script>標(biāo)簽和一些看不懂的代碼之外,我們一無所獲,一個<img>標(biāo)簽都沒有!跟我們在網(wǎng)站審查元素的結(jié)果完全不一樣,這是為什么?

          答案就是,這個網(wǎng)站的所有圖片都是動態(tài)加載的!網(wǎng)站有靜態(tài)網(wǎng)站和動態(tài)網(wǎng)站之分,上一個實戰(zhàn)爬取的網(wǎng)站是靜態(tài)網(wǎng)站,而這個網(wǎng)站是動態(tài)網(wǎng)站,動態(tài)加載有一部分的目的就是為了反爬蟲。

          對于什么是動態(tài)加載,你可以這樣理解:

          我們知道化妝術(shù)學(xué)的好,賊厲害,可以改變一個人的容貌。相應(yīng)的,動態(tài)加載用的好,也賊厲害,可以改變一個網(wǎng)站的容貌。

          動態(tài)網(wǎng)站使用動態(tài)加載常用的手段就是通過調(diào)用JavaScript來實現(xiàn)的。怎么實現(xiàn)JavaScript動態(tài)加載,我們不必深究,我們只要知道,動態(tài)加載的JavaScript腳本,就像化妝術(shù)需要用的化妝品,五花八門。有粉底、口紅、睫毛膏等等,它們都有各自的用途。動態(tài)加載的JavaScript腳本也一樣,一個動態(tài)加載的網(wǎng)站可能使用很多JavaScript腳本,我們只要找到負(fù)責(zé)動態(tài)加載圖片的JavaScript腳本,不就找到我們需要的鏈接了嗎?

          對于初學(xué)者,我們不必看懂JavaScript執(zhí)行的內(nèi)容是什么,做了哪些事情,因為我們有強(qiáng)大的抓包工具,它自然會幫我們分析。這個強(qiáng)大的抓包工具就是Fiddler:

          URL:http://www.telerik.com/fiddler

          PS:也可以使用瀏覽器自帶的Networks,但是我更推薦這個軟件,因為它操作起來更高效。

          安裝方法很簡單,傻瓜式安裝,一直下一步即可,對于經(jīng)常使用電腦的人來說,應(yīng)該沒有任何難度。

          這個軟件的使用方法也很簡單,打開軟件,然后用瀏覽器打開我們的目標(biāo)網(wǎng)站,以Unsplash為例,抓包結(jié)果如下:

          我們可以看到,上圖左側(cè)紅框處是我們的GET請求的地址,就是網(wǎng)站的URL,右下角是服務(wù)器返回的信息,我們可以看到,這些信息也是我們上一個程序獲得的信息。這個不是我們需要的鏈接,我們繼續(xù)往下看。

          我們發(fā)現(xiàn)上圖所示的就是一個JavaScript請求,看右下側(cè)服務(wù)器返回的信息是一個json格式的數(shù)據(jù)。這里面,就有我們需要的內(nèi)容。我們局部放大看一下:

          這是Fiddler右側(cè)的信息,上面是請求的Headers信息,包括這個Javascript的請求地 址:http://unsplash.com/napi/feeds/home,其他信息我們先不管,我們看看下面的內(nèi)容。里面有很多圖片的信息,包括圖片的id,圖片的大小,圖片的鏈接,還有下一頁的地址。這個腳本以json格式存儲傳輸?shù)臄?shù)據(jù),json格式是一種輕量級的數(shù)據(jù)交換格式,起到封裝數(shù)據(jù)的作用,易于人閱讀和編寫,同時也易于機(jī)器解析和生成。這么多鏈接,可以看到圖片的鏈接有很多,根據(jù)哪個鏈接下載圖片呢?先別急,讓我們繼續(xù)分析:

          在這個網(wǎng)站,我們可以按這個按鈕進(jìn)行圖片下載。我們抓包分下下這個動作,看看發(fā)送了哪些請求。

              https://unsplash.com/photos/1PrQ2mHW-Fo/download?force=true
              https://unsplash.com/photos/JX7nDtafBcU/download?force=true
              https://unsplash.com/photos/HCVbP3zqX4k/download?force=true
          
          
          

          通過Fiddler抓包,我們發(fā)現(xiàn),點擊不同圖片的下載按鈕,GET請求的地址都是不同的。但是它們很有規(guī)律,就是中間有一段代碼是不一樣的,其他地方都一樣。中間那段代碼是不是很熟悉?沒錯,它就是我們之前抓包分析得到j(luò)son數(shù)據(jù)中的照片的id。我們只要解析出每個照片的id,就可以獲得圖片下載的請求地址,然后根據(jù)這個請求地址,我們就可以下載圖片了。那么,現(xiàn)在的首要任務(wù)就是解析json數(shù)據(jù)了。

          json格式的數(shù)據(jù)也是分層的。可以看到next_page里存放的是下一頁的請求地址,很顯然Unsplash下一頁的內(nèi)容,也是動態(tài)加載的。在photos下面的id里,存放著圖片的id,這個就是我們需要獲得的圖片id號。

          怎么編程提取這些json數(shù)據(jù)呢?我們也是分步完成:

          1. 獲取整個json數(shù)據(jù)
          2. 解析json數(shù)據(jù)

          編寫代碼,嘗試獲取json數(shù)據(jù):

              # -*- coding:UTF-8 -*-
              import requests
          
              if __name__ == '__main__':
                  target = 'http://unsplash.com/napi/feeds/home'
                  req = requests.get(url=target)
                  print(req.text)
          

          很遺憾,程序報錯了,問題出在哪里?通過錯誤信息,我們可以看到SSL認(rèn)證錯誤,SSL認(rèn)證是指客戶端到服務(wù)器端的認(rèn)證。一個非常簡單的解決這個認(rèn)證錯誤的方法就是設(shè)置requests.get()方法的verify參數(shù)。這個參數(shù)默認(rèn)設(shè)置為True,也就是執(zhí)行認(rèn)證。我們將其設(shè)置為False,繞過認(rèn)證不就可以了?

          有想法就要嘗試,編寫代碼如下:

              # -*- coding:UTF-8 -*-
              import requests
          
              if __name__ == '__main__':
                  target = 'http://unsplash.com/napi/feeds/home'
                  req = requests.get(url=target, verify=False)
                  print(req.text)
          

          認(rèn)證問題解決了,又有新問題了:

          可以看到,我們GET請求又失敗了,這是為什么?這個網(wǎng)站反爬蟲的手段除了動態(tài)加載,還有一個反爬蟲手段,那就是驗證Request Headers。接下來,讓我們分析下這個Requests Headers:

          我截取了Fiddler的抓包信息,可以看到Requests Headers里又很多參數(shù),有Accept、Accept-Encoding、Accept-Language、DPR、User-Agent、Viewport-Width、accept-version、Referer、x-unsplash-client、authorization、Connection、Host。它們都是什么意思呢?

          專業(yè)的解釋能說的太多,我挑重點:

          • User-Agent:這里面存放瀏覽器的信息。可以看到上圖的參數(shù)值,它表示我是通過Windows的Chrome瀏覽器,訪問的這個服務(wù)器。如果我們不設(shè)置這個參數(shù),用Python程序直接發(fā)送GET請求,服務(wù)器接受到的User-Agent信息就會是一個包含python字樣的User-Agent。如果后臺設(shè)計者驗證這個User-Agent參數(shù)是否合法,不讓帶Python字樣的User-Agent訪問,這樣就起到了反爬蟲的作用。這是一個最簡單的,最常用的反爬蟲手段。
          • Referer:這個參數(shù)也可以用于反爬蟲,它表示這個請求是從哪發(fā)出的。可以看到我們通過瀏覽器訪問網(wǎng)站,這個請求是從https://unsplash.com/,這個地址發(fā)出的。如果后臺設(shè)計者,驗證這個參數(shù),對于不是從這個地址跳轉(zhuǎn)過來的請求一律禁止訪問,這樣就也起到了反爬蟲的作用。
          • authorization:這個參數(shù)是基于AAA模型中的身份驗證信息允許訪問一種資源的行為。在我們用瀏覽器訪問的時候,服務(wù)器會為訪問者分配這個用戶ID。如果后臺設(shè)計者,驗證這個參數(shù),對于沒有用戶ID的請求一律禁止訪問,這樣就又起到了反爬蟲的作用。

          Unsplash是根據(jù)哪個參數(shù)反爬蟲的呢?根據(jù)我的測試,是authorization。我們只要通過程序手動添加這個參數(shù),然后再發(fā)送GET請求,就可以順利訪問了。怎么什么設(shè)置呢?還是requests.get()方法,我們只需要添加headers參數(shù)即可。編寫代碼如下:

              # -*- coding:UTF-8 -*-
              import requests
          
              if __name__ == '__main__':
                  target = 'http://unsplash.com/napi/feeds/home'
                  headers = {'authorization':'your Client-ID'}
                  req = requests.get(url=target, headers=headers, verify=False)
                  print(req.text)
          

          headers參數(shù)值是通過字典傳入的。記得將上述代碼中your Client-ID換成諸位自己抓包獲得的信息。代碼運(yùn)行結(jié)果如下:

          皇天不負(fù)有心人,可以看到我們已經(jīng)順利獲得json數(shù)據(jù)了,里面有next_page和照片的id。接下來就是解析json數(shù)據(jù)。根據(jù)我們之前分析可知,next_page放在了json數(shù)據(jù)的最外側(cè),照片的id放在了photos->id里。我們使用json.load()方法解析數(shù)據(jù),編寫代碼如下:

              # -*- coding:UTF-8 -*-
              import requests, json
          
              if __name__ == '__main__':
                  target = 'http://unsplash.com/napi/feeds/home'
                  headers = {'authorization':'your Client-ID'}
                  req = requests.get(url=target, headers=headers, verify=False)
                  html = json.loads(req.text)
                  next_page = html['next_page']
                  print('下一頁地址:',next_page)
                  for each in html['photos']:
                      print('圖片ID:',each['id'])
          

          解析json數(shù)據(jù)很簡單,跟字典操作一樣,就是字典套字典。json.load()里面的參數(shù)是原始的json格式的數(shù)據(jù)。程序運(yùn)行結(jié)果如下:

          圖片的ID已經(jīng)獲得了,再通過字符串處理一下,就生成了我們需要的圖片下載請求地址。根據(jù)這個地址,我們就可以下載圖片了。下載方式,使用直接寫入文件的方法。

          (3)整合代碼

          每次獲取鏈接加一個1s延時,因為人在瀏覽頁面的時候,翻頁的動作不可能太快。我們要讓我們的爬蟲盡量友好一些。

              # -*- coding:UTF-8 -*-
              import requests, json, time, sys
              from contextlib import closing
          
              class get_photos(object):
          
                  def __init__(self):
                      self.photos_id = []
                      self.download_server = 'https://unsplash.com/photos/xxx/download?force=trues'
                      self.target = 'http://unsplash.com/napi/feeds/home'
                      self.headers = {'authorization':'your Client-ID'}
          
                  """
                  函數(shù)說明:獲取圖片ID
                  Parameters:
                      無
                  Returns:
                      無
                  Modify:
                      2017-09-13
                  """ 
                  def get_ids(self):
                      req = requests.get(url=self.target, headers=self.headers, verify=False)
                      html = json.loads(req.text)
                      next_page = html['next_page']
                      for each in html['photos']:
                          self.photos_id.append(each['id'])
                      time.sleep(1)
                      for i in range(4):
                          req = requests.get(url=next_page, headers=self.headers, verify=False)
                          html = json.loads(req.text)
                          next_page = html['next_page']
                          for each in html['photos']:
                              self.photos_id.append(each['id'])
                          time.sleep(1)
          
          
                  """
                  函數(shù)說明:圖片下載
                  Parameters:
                      無
                  Returns:
                      無
                  Modify:
                      2017-09-13
                  """ 
                  def download(self, photo_id, filename):
                      headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}
                      target = self.download_server.replace('xxx', photo_id)
                      with closing(requests.get(url=target, stream=True, verify = False, headers = self.headers)) as r:
                          with open('%d.jpg' % filename, 'ab+') as f:
                              for chunk in r.iter_content(chunk_size = 1024):
                                  if chunk:
                                      f.write(chunk)
                                      f.flush()
          
              if __name__ == '__main__':
                  gp = get_photos()
                  print('獲取圖片連接中:')
                  gp.get_ids()
                  print('圖片下載中:')
                  for i in range(len(gp.photos_id)):
                      print('  正在下載第%d張圖片' % (i+1))
                      gp.download(gp.photos_id[i], (i+1))
          

          下載速度還行,有的圖片下載慢是因為圖片太大??梢钥吹接覀?cè)也打印了一些警報信息,這是因為我們沒有進(jìn)行SSL驗證。

          學(xué)會了爬取圖片,簡單的動態(tài)加載的網(wǎng)站也難不倒你了。趕快試試國內(nèi)的一些圖片網(wǎng)站吧!

          3. 愛奇藝VIP視頻下載

          (1)實戰(zhàn)背景

          愛奇藝的VIP視頻只有會員能看,普通用戶只能看前6分鐘。比如加勒比海盜5:

          URL:http://www.iqiyi.com/v_19rr7qhfg0.html#vfrm=19-9-0-1

          我們怎么免費(fèi)看VIP視頻呢?一個簡單的方法,就是通過旋風(fēng)視頻VIP解析網(wǎng)站。

          URL:http://api.xfsub.com/

          這個網(wǎng)站為我們提供了免費(fèi)的視頻解析,它的通用解析方式是:

              http://api.xfsub.com/index.php?url=[播放地址或視頻id]
          

          比如,對于繡春刀這個電影,我們只需要在瀏覽器地址欄輸入:

              http://api.xfsub.com/index.php?url=http://www.iqiyi.com/v_19rr7qhfg0.html#vfrm=19-9-0-1
          

          這樣,我們就可以在線觀看這些VIP視頻了:

          但是這個網(wǎng)站只提供了在線解析視頻的功能,沒有提供下載接口,如果想把視頻下載下來,我們就可以利用網(wǎng)絡(luò)爬蟲進(jìn)行抓包,將視頻下載下來。

          (2)實戰(zhàn)升級

          分析方法相同,我們使用Fiddler進(jìn)行抓包:

          我們可以看到,有用的請求并不多,我們逐條分析。我們先看第一個請求返回的信息。

          可以看到第一個請求是GET請求,沒有什么有用的信息,繼續(xù)看下一條。

          我們看到,第二條GET請求地址變了,并且在返回的信息中,我們看到,這個網(wǎng)頁執(zhí)行了一個POST請求。POST請求是啥呢?它跟GET請求正好相反,GET是從服務(wù)器獲得數(shù)據(jù),而POST請求是向服務(wù)器發(fā)送數(shù)據(jù),服務(wù)器再根據(jù)POST請求的參數(shù),返回相應(yīng)的內(nèi)容。這個POST請求有四個參數(shù),分別為time、key、url、type。記住這個有用的信息,我們在抓包結(jié)果中,找一下這個請求,看看這個POST請求做了什么。

          很顯然,這個就是我們要找的POST請求,我們可以看到POST請求的參數(shù)以及返回的json格式的數(shù)據(jù)。其中url存放的參數(shù)如下:

              xfsub_api\/url.php?key=02896e4af69fb18f70129b6046d7c718&time=1505724557&url=http%3A%2F%2Fwww.iqiyi.com%2Fv_19rr7qhfg0.html&type=&xml=1
          

          這個信息有轉(zhuǎn)義了,但是沒有關(guān)系,我們手動提取一下,變成如下形式:

              xfsub_api/url.php?key=02896e4af69fb18f70129b6046d7c718&time=1505724557&url=http://www.iqiyi.com/v_19rr7qhfg0.html&type=&xml=1
          

          我們已經(jīng)知道了這個解析視頻的服務(wù)器的域名,再把域名加上:

              http://api.xfsub.com/xfsub_api\url.php?key=02896e4af69fb18f70129b6046d7c718&time=1505724557&url=http://www.iqiyi.com/v_19rr7qhfg0.html&type=&xml=1
          

          這里面存放的是什么東西?不會視頻解析后的地址吧?我們有瀏覽器打開這個地址看一下:

          果然,我們可以看到視頻地址近在眼前啊,URL如下:

              http://disp.titan.mgtv.com/vod.do?fmt=4&pno=1121&fid=1FEA2622E0BD9A1CA625FBE9B5A238A6&file=/c1/2017/09/06_0/1FEA2622E0BD9A1CA625FBE9B5A238A6_20170906_1_1_705.mp4
          

          我們再打開這個視頻地址:

          瞧,我們就這樣得到了這個視頻在服務(wù)器上的緩存地址。根據(jù)這個地址,我們就可以輕松下載視頻了。

          PS:需要注意一點,這些URL地址,都是有一定時效性的,很快就會失效,因為里面包含時間信息。所以,各位在分析的時候,要根據(jù)自己的URL結(jié)果打開網(wǎng)站才能看到視頻。

          接下來,我們的任務(wù)就是編程實現(xiàn)我們所分析的步驟,根據(jù)不同的視頻播放地址獲得視頻存放的地址。

          現(xiàn)在梳理一下編程思路:

          1. 用正則表達(dá)式匹配到key、time、url等信息。
          2. 根據(jù)匹配的到信息發(fā)POST請求,獲得一個存放視頻信息的url。
          3. 根據(jù)這個url獲得視頻存放的地址。
          4. 根據(jù)最終的視頻地址,下載視頻。

          (3)編寫代碼

          編寫代碼的時候注意一個問題,就是我們需要使用requests.session()保持我們的會話請求。簡單理解就是,在初次訪問服務(wù)器的時候,服務(wù)器會給你分配一個身份證明。我們需要拿著這個身份證去繼續(xù)訪問,如果沒有這個身份證明,服務(wù)器就不會再讓你訪問。這也就是這個服務(wù)器的反爬蟲手段,會驗證用戶的身份。

              #-*- coding:UTF-8 -*-
              import requests,re, json
              from bs4 import BeautifulSoup
          
              class video_downloader():
                  def __init__(self, url):
                      self.server = 'http://api.xfsub.com'
                      self.api = 'http://api.xfsub.com/xfsub_api/?url='
                      self.get_url_api = 'http://api.xfsub.com/xfsub_api/url.php'
                      self.url = url.split('#')[0]
                      self.target = self.api + self.url
                      self.s = requests.session()
          
                  """
                  函數(shù)說明:獲取key、time、url等參數(shù)
                  Parameters:
                      無
                  Returns:
                      無
                  Modify:
                      2017-09-18
                  """
                  def get_key(self):
                      req = self.s.get(url=self.target)
                      req.encoding = 'utf-8'
                      self.info = json.loads(re.findall('"url.php",\ (.+),', req.text)[0])    #使用正則表達(dá)式匹配結(jié)果,將匹配的結(jié)果存入info變量中
          
                  """
                  函數(shù)說明:獲取視頻地址
                  Parameters:
                      無
                  Returns:
                      video_url - 視頻存放地址
                  Modify:
                      2017-09-18
                  """
                  def get_url(self):
                      data = {'time':self.info['time'],
                          'key':self.info['key'],
                          'url':self.info['url'],
                          'type':''}
                      req = self.s.post(url=self.get_url_api,data=data)
                      url = self.server + json.loads(req.text)['url']
                      req = self.s.get(url)
                      bf = BeautifulSoup(req.text,'xml')                                      #因為文件是xml格式的,所以要進(jìn)行xml解析。
                      video_url = bf.find('file').string                                      #匹配到視頻地址
                          return video_url
          
          
              if __name__ == '__main__':
                  url = 'http://www.iqiyi.com/v_19rr7qhfg0.html#vfrm=19-9-0-1'
                  vd = video_downloader(url)
                  vd.get_key()
                  print(vd.get_url())
          

          思路已經(jīng)給出,希望喜歡爬蟲的人可以在運(yùn)行下代碼之后,自己重頭編寫程序,因為只有經(jīng)過自己分析和測試之后,才能真正明白這些代碼的意義。上述代碼運(yùn)行結(jié)果如下:

          我們已經(jīng)順利獲得了mp4這個視頻文件地址。根據(jù)視頻地址,使用urllib.request.urlretrieve()即可將視頻下載下來。編寫代碼如下:

              #-*- coding:UTF-8 -*-
              import requests,re, json, sys
              from bs4 import BeautifulSoup
              from urllib import request
          
              class video_downloader():
                  def __init__(self, url):
                      self.server = 'http://api.xfsub.com'
                      self.api = 'http://api.xfsub.com/xfsub_api/?url='
                      self.get_url_api = 'http://api.xfsub.com/xfsub_api/url.php'
                      self.url = url.split('#')[0]
                      self.target = self.api + self.url
                      self.s = requests.session()
          
                  """
                  函數(shù)說明:獲取key、time、url等參數(shù)
                  Parameters:
                      無
                  Returns:
                      無
                  Modify:
                      2017-09-18
                  """
                  def get_key(self):
                      req = self.s.get(url=self.target)
                      req.encoding = 'utf-8'
                      self.info = json.loads(re.findall('"url.php",\ (.+),', req.text)[0])    #使用正則表達(dá)式匹配結(jié)果,將匹配的結(jié)果存入info變量中
          
                  """
                  函數(shù)說明:獲取視頻地址
                  Parameters:
                      無
                  Returns:
                      video_url - 視頻存放地址
                  Modify:
                      2017-09-18
                  """
                  def get_url(self):
                      data = {'time':self.info['time'],
                          'key':self.info['key'],
                          'url':self.info['url'],
                          'type':''}
                      req = self.s.post(url=self.get_url_api,data=data)
                      url = self.server + json.loads(req.text)['url']
                      req = self.s.get(url)
                      bf = BeautifulSoup(req.text,'xml')                                      #因為文件是xml格式的,所以要進(jìn)行xml解析。
                      video_url = bf.find('file').string                                      #匹配到視頻地址
                      return video_url
          
                  """
                  函數(shù)說明:回調(diào)函數(shù),打印下載進(jìn)度
                  Parameters:
                      a b c - 返回信息
                  Returns:
                      無
                  Modify:
                      2017-09-18
                  """
                  def Schedule(self, a, b, c):
                      per = 100.0*a*b/c
                      if per > 100 :
                          per = 1
                      sys.stdout.write("  " + "%.2f%% 已經(jīng)下載的大小:%ld 文件大小:%ld" % (per,a*b,c) + '\r')
                      sys.stdout.flush()
          
                  """
                  函數(shù)說明:視頻下載
                  Parameters:
                      url - 視頻地址
                      filename - 視頻名字
                  Returns:
                      無
                  Modify:
                      2017-09-18
                  """
                  def video_download(self, url, filename):
                      request.urlretrieve(url=url,filename=filename,reporthook=self.Schedule)
          
              if __name__ == '__main__':
                  url = 'http://www.iqiyi.com/v_19rr7qhfg0.html#vfrm=19-9-0-1'
                  vd = video_downloader(url)
                  filename = '加勒比海盜5'
                  print('%s下載中:' % filename)
                  vd.get_key()
                  video_url = vd.get_url()
                  print('  獲取地址成功:%s' % video_url)
                  vd.video_download(video_url, filename+'.mp4')
                  print('\n下載完成!')
          

          urlretrieve()有三個參數(shù),第一個url參數(shù)是視頻存放的地址,第二個參數(shù)filename是保存的文件名,最后一個是回調(diào)函數(shù),它方便我們查看下載進(jìn)度。代碼量不大,很簡單,主要在于分析過程。代碼運(yùn)行結(jié)果如下:

          下載速度挺快的,幾分鐘視頻下載好了。

          對于這個程序,感興趣的朋友可以進(jìn)行擴(kuò)展一下,設(shè)計出一個小軟件,根據(jù)用戶提供的url,提供PC在線觀看、手機(jī)在線觀看、視頻下載等功能。


          主站蜘蛛池模板: 国产一区二区在线| 无码精品黑人一区二区三区 | 日本高清一区二区三区| 亚洲Aⅴ无码一区二区二三区软件| 久久99热狠狠色精品一区| 风流老熟女一区二区三区| 日本国产一区二区三区在线观看 | 中文字幕一区一区三区| 中文字幕一区日韩在线视频 | 日本高清一区二区三区| 精品亚洲AV无码一区二区三区| 久久婷婷久久一区二区三区| 伦理一区二区三区| 国产人妖视频一区二区 | 亚洲午夜一区二区三区| 老熟妇仑乱视频一区二区| 国产一区二区电影在线观看| 国产精品无码一区二区三区电影| 久久久国产一区二区三区| 亚洲熟女少妇一区二区| 免费无码一区二区三区蜜桃| 东京热人妻无码一区二区av| 91麻豆精品国产自产在线观看一区| 高清一区二区三区| 国产亚洲情侣一区二区无码AV| 中文字幕精品无码一区二区| 台湾无码一区二区| 国产美女在线一区二区三区| 欧美日韩一区二区成人午夜电影| 人妻精品无码一区二区三区| 国产乱子伦一区二区三区| 美女视频黄a视频全免费网站一区 美女免费视频一区二区 | 午夜福利一区二区三区在线观看| 中文字幕日本一区| 国产自产V一区二区三区C| 一本大道东京热无码一区| 少妇人妻偷人精品一区二区| 亚洲色欲一区二区三区在线观看| 麻豆一区二区99久久久久| 亚洲熟妇无码一区二区三区导航| 韩国精品一区二区三区无码视频|