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
了編寫一個Java爬蟲,你需要了解以下幾個步驟:
下面是一個基本的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是一款用于解析HTML和XML文檔的Java庫。它提供了類似于jQuery的語法來操作文檔,使得解析和處理文檔變得非常簡單。
以下是Jsoup解析器的一些常用功能:
總之,Jsoup是一款非常實用的HTML和XML解析器,可以幫助Java開發(fā)者快速、簡單地解析和處理HTML文檔,使得爬蟲開發(fā)變得更加容易。
使用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)用。
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ò)爬蟲,也叫網(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)容)。
在瀏覽器的地址欄輸入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信息不會改變。刷新一下界面,頁面還會回到原本的樣子。這就跟人整容一樣,我們能改變一些表面的東西,但是不能改變我們的基因。
網(wǎng)絡(luò)爬蟲的第一步就是根據(jù)URL,獲取網(wǎng)頁的HTML信息。在Python3中,可以使用urllib.request和requests進(jìn)行網(wǎng)頁爬取。
requests庫強(qiáng)大好用,所以本文使用requests庫獲取網(wǎng)頁的HTML信息。requests庫的github地址:https://github.com/requests/requests
(1)requests安裝
在學(xué)習(xí)使用requests庫之前,我們需要在電腦中安裝好requests庫。在cmd中,使用如下指令安裝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)內(nèi)容由簡單到復(fù)雜,難度逐漸增加,但均屬于入門級難度。下面開始我們的第一個實戰(zhàn)內(nèi)容:網(wǎng)絡(luò)小說下載。
(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一樣,使用如下指令安裝(也是二選一):
一個強(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)系,我們再舉個簡單的例子:
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)行效果如下圖所示:
(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)行圖片的下載。
那么,讓我們先捋一捋這個過程:
我們信心滿滿地按照這個思路爬取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ù)呢?我們也是分步完成:
編寫代碼,嘗試獲取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è)的解釋能說的太多,我挑重點:
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)站吧!
(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)在梳理一下編程思路:
(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ī)在線觀看、視頻下載等功能。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。