摘要:所以說(shuō),我們所看到的微博頁(yè)面的真實(shí)數(shù)據(jù)并不是最原始的頁(yè)面返回的,而是后來(lái)執(zhí)行后再次向后臺(tái)發(fā)送了請(qǐng)求,拿到數(shù)據(jù)后再進(jìn)一步渲染出來(lái)的。結(jié)果提取仍然是拿微博為例,我們接下來(lái)用來(lái)模擬這些請(qǐng)求,把馬云發(fā)過(guò)的微博爬取下來(lái)。
上一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---34、數(shù)據(jù)存儲(chǔ):非關(guān)系型數(shù)據(jù)庫(kù)存儲(chǔ):Redis
下一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---36、分析Ajax爬取今日頭條街拍美圖
有時(shí)候我們?cè)谟?Requests 抓取頁(yè)面的時(shí)候,得到的結(jié)果可能和在瀏覽器中看到的是不一樣的,在瀏覽器中可以看到正常顯示的頁(yè)面數(shù)據(jù),但是使用 Requests 得到的結(jié)果并沒(méi)有,這其中的原因是 Requests 獲取的都是原始的 HTML 文檔,而瀏覽器中的頁(yè)面則是頁(yè)面又經(jīng)過(guò) JavaScript 處理數(shù)據(jù)后生成的結(jié)果,這些數(shù)據(jù)的來(lái)源有多種,可能是通過(guò) Ajax 加載的,可能是包含在了 HTML 文檔中的,也可能是經(jīng)過(guò) JavaScript 經(jīng)過(guò)特定算法計(jì)算后生成的。
對(duì)于第一種情況,數(shù)據(jù)的加載是一種異步加載方式,原始的頁(yè)面最初不會(huì)包含某些數(shù)據(jù),原始頁(yè)面加載完后會(huì)會(huì)再向服務(wù)器請(qǐng)求某個(gè)接口獲取數(shù)據(jù),然后數(shù)據(jù)再被處理才呈現(xiàn)到網(wǎng)頁(yè)上,這其實(shí)就是發(fā)送了一個(gè) Ajax 請(qǐng)求。
照 Web 發(fā)展的趨勢(shì)來(lái)看,這種形式的頁(yè)面越來(lái)越多,網(wǎng)頁(yè)原始 HTML 文檔不會(huì)包含任何數(shù)據(jù),數(shù)據(jù)都是通過(guò) Ajax 來(lái)統(tǒng)一加載然后再呈現(xiàn)出來(lái),這樣在 Web 開(kāi)發(fā)上可以做到前后端分離,而且降低服務(wù)器直接渲染頁(yè)面帶來(lái)的壓力。
所以如果我們遇到這樣的頁(yè)面,如果我們?cè)倮?Requests 等庫(kù)來(lái)抓取原始頁(yè)面是無(wú)法獲取到有效數(shù)據(jù)的,這時(shí)我們需要做的就是分析網(wǎng)頁(yè)的后臺(tái)向接口發(fā)送的 Ajax 請(qǐng)求,如果我們可以用 Requests 來(lái)模擬 Ajax 請(qǐng)求,那就可以成功抓取了。
所以本章我們的主要目的是了解什么是 Ajax 以及如何去分析和抓取 Ajax 請(qǐng)求。
1、什么是AjaxAjax,全稱(chēng)為 Asynchronous JavaScript and XML,即異步的 JavaScript 和 XML。
Ajax 不是一門(mén)編程語(yǔ)言,而是利用 JavaScript 在保證頁(yè)面不被刷新、頁(yè)面鏈接不改變的情況下與服務(wù)器交換數(shù)據(jù)并更新部分網(wǎng)頁(yè)的技術(shù)。
對(duì)于傳統(tǒng)的網(wǎng)頁(yè),如果想更新其內(nèi)容,那么必須要刷新整個(gè)頁(yè)面,但有了 Ajax,我們便可以實(shí)現(xiàn)在頁(yè)面不被全部刷新的情況下更新其內(nèi)容。在這個(gè)過(guò)程中,頁(yè)面實(shí)際是在后臺(tái)與服務(wù)器進(jìn)行了數(shù)據(jù)交互,獲取到數(shù)據(jù)之后,再利用 JavaScript 改變網(wǎng)頁(yè),這樣網(wǎng)頁(yè)內(nèi)容就會(huì)更新了。
可以到 W3School 上體驗(yàn)幾個(gè) Demo 來(lái)感受一下:http://www.w3school.com.cn/aj...。
我們?cè)跒g覽網(wǎng)頁(yè)的時(shí)候會(huì)發(fā)現(xiàn)很多網(wǎng)頁(yè)都有上滑查看更多的選項(xiàng),比如拿微博來(lái)說(shuō),我們以馬云的主頁(yè)為例:https://m.weibo.cn/u/2145291155,切換到微博頁(yè)面,一直下滑,可以發(fā)現(xiàn)下滑幾個(gè)微博之后,再向下就沒(méi)有了,轉(zhuǎn)而會(huì)出現(xiàn)一個(gè)加載的動(dòng)畫(huà),不一會(huì)兒下方就繼續(xù)出現(xiàn)了新的微博內(nèi)容,那么這個(gè)過(guò)程其實(shí)就是 Ajax 加載的過(guò)程,如圖 6-1 所示:
evernotecid://D603D29C-DFBA-4C04-85E9-CCA3C33763F6/appyinxiangcom/23852268/ENResource/p142
圖 6-1 頁(yè)面加載過(guò)程
我們注意到頁(yè)面其實(shí)并沒(méi)有整個(gè)刷新,也就意味著頁(yè)面的鏈接是沒(méi)有變化的,但是這個(gè)過(guò)程網(wǎng)頁(yè)中卻又多了新的內(nèi)容,也就是后面刷出來(lái)的新的微博。這就是通過(guò) Ajax 獲取新的數(shù)據(jù)并呈現(xiàn)而實(shí)現(xiàn)的過(guò)程。
初步了解了 Ajax 之后我們?cè)賮?lái)詳細(xì)了解一下它的基本原理,發(fā)送 Ajax 請(qǐng)求到網(wǎng)頁(yè)更新的這個(gè)過(guò)程可以簡(jiǎn)單分為三步:
發(fā)送請(qǐng)求解析內(nèi)容渲染網(wǎng)頁(yè)下面我們分別來(lái)詳細(xì)介紹一下這幾個(gè)過(guò)程。
我們知道 JavaScript 可以實(shí)現(xiàn)頁(yè)面的各種交互功能,那么 Ajax 也不例外,它也是由 JavaScript 來(lái)實(shí)現(xiàn)的,實(shí)際它就是執(zhí)行了類(lèi)似如下的代碼:
var xmlhttp; if (window.XMLHttpRequest) { ??? // code for IE7+, Firefox, Chrome, Opera, Safari ??? xmlhttp=new XMLHttpRequest(); } else {// code for IE6, IE5 ??? xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=function() { ??? if (xmlhttp.readyState==4 && xmlhttp.status==200) { ??????? document.getElementById("myDiv").innerHTML=xmlhttp.responseText; ??? } } xmlhttp.open("POST","/ajax/",true); xmlhttp.send();
這是 JavaScript 對(duì) Ajax 最底層的實(shí)現(xiàn),實(shí)際上就是新建了 XMLHttpRequest 對(duì)象,然后調(diào)用了onreadystatechange 屬性設(shè)置了監(jiān)聽(tīng),然后調(diào)用 open() 和 send() 方法向某個(gè)鏈接也就是服務(wù)器發(fā)送了一個(gè)請(qǐng)求,我們?cè)谇懊嬗?Python 實(shí)現(xiàn)請(qǐng)求發(fā)送之后是可以得到響應(yīng)結(jié)果的,只不過(guò)在這里請(qǐng)求的發(fā)送變成了 JavaScript 來(lái)完成,由于設(shè)置了監(jiān)聽(tīng),所以當(dāng)服務(wù)器返回響應(yīng)時(shí),onreadystatechange 對(duì)應(yīng)的方法便會(huì)被觸發(fā),然后在這個(gè)方法里面解析響應(yīng)內(nèi)容即可。
解析內(nèi)容得到響應(yīng)之后,onreadystatechange 屬性對(duì)應(yīng)的方法便會(huì)被觸發(fā),此時(shí)利用 xmlhttp 的 responseText 屬性便可以取到響應(yīng)的內(nèi)容。這也就是類(lèi)似于 Python 中利用 Requests 向服務(wù)器發(fā)起了一個(gè)請(qǐng)求,然后得到響應(yīng)的過(guò)程。那么返回內(nèi)容可能是 HTML,可能是 Json,接下來(lái)只需要在方法中用 JavaScript 進(jìn)一步處理即可。比如如果是 Json 的話(huà),可以進(jìn)行解析和轉(zhuǎn)化。
渲染網(wǎng)頁(yè)JavaScript 有改變網(wǎng)頁(yè)內(nèi)容的能力,解析完響應(yīng)內(nèi)容之后,就可以調(diào)用 JavaScript 來(lái)針對(duì)解析完的內(nèi)容對(duì)網(wǎng)頁(yè)進(jìn)行下一步的處理了。比如通過(guò)document.getElementById().innerHTML 這樣的操作便可以對(duì)某個(gè)元素內(nèi)的源代碼進(jìn)行更改,這樣網(wǎng)頁(yè)顯示的內(nèi)容就改變了,這樣的操作也被稱(chēng)作 DOM 操作,即對(duì) Document網(wǎng)頁(yè)文檔進(jìn)行操作,如更改、刪除等。
如上例中,document.getElementById("myDiv").innerHTML=xmlhttp.responseText 便將 ID 為 myDiv 的節(jié)點(diǎn)內(nèi)部的 HTML 代碼更改為服務(wù)器返回的內(nèi)容,這樣 myDiv 元素內(nèi)部便會(huì)呈現(xiàn)出服務(wù)器返回的新數(shù)據(jù),網(wǎng)頁(yè)的部分內(nèi)容看上去就更新了。
以上就是Ajax的三個(gè)步驟。
我們觀察到,以上的步驟其實(shí)都是由 JavaScript 來(lái)完成的,它完成了整個(gè)請(qǐng)求、解析、渲染的過(guò)程。
所以再回想微博的下拉刷新,這其實(shí)就是 JavaScript 向服務(wù)器發(fā)送了一個(gè) Ajax 請(qǐng)求,然后獲取新的微博數(shù)據(jù),將其解析,并將其渲染在網(wǎng)頁(yè)中。
因此我們可以知道,真實(shí)的數(shù)據(jù)其實(shí)都是一次次 Ajax 請(qǐng)求得到的,我們?nèi)绻胍ト∵@些數(shù)據(jù),就需要知道這些請(qǐng)求到底是怎么發(fā)送的,發(fā)往哪里,發(fā)了哪些參數(shù)。如果我們知道了這些,不就可以用 Python 來(lái)模擬這個(gè)發(fā)送操作,獲取到這其中的結(jié)果了嗎?
所以在下一節(jié)我們就來(lái)了解下到哪可以看到這些后臺(tái) Ajax 操作,去了解它到底是怎么發(fā)送的,發(fā)送了什么參數(shù)。
還是以上文中的微博為例,我們已經(jīng)知道了拖動(dòng)刷新的內(nèi)容是由 Ajax 加載的,而且頁(yè)面的 URL 沒(méi)有變化,那么我們應(yīng)該到哪去查看這些 Ajax 請(qǐng)求呢?
1. 查看請(qǐng)求在這里我們還是需要借助于瀏覽器的開(kāi)發(fā)者工具,我們以 Chrome 瀏覽器為例來(lái)看一下怎樣操作。
首先用 Chrome 瀏覽器打開(kāi)微博的鏈接:https://m.weibo.cn/u/2145291155,隨后在頁(yè)面中點(diǎn)擊鼠標(biāo)右鍵,會(huì)出現(xiàn)一個(gè)檢查的選項(xiàng),點(diǎn)擊它便會(huì)彈出開(kāi)發(fā)者工具,如圖 6-2 所示:
圖 6-2 開(kāi)發(fā)者工具
那么在 Elements 選項(xiàng)卡便會(huì)觀察到網(wǎng)頁(yè)的源代碼,右側(cè)便是節(jié)點(diǎn)的樣式。
不過(guò)這不是我們想要尋找的內(nèi)容,我們切換到 Network 選項(xiàng)卡,隨后重新刷新頁(yè)面,可以發(fā)現(xiàn)在這里出現(xiàn)了非常多的條目,如圖 6-3 所示:
圖 6-3 Network 面板結(jié)果
前文我們也提到過(guò),這里其實(shí)就是在頁(yè)面加載過(guò)程中瀏覽器與服務(wù)器之間發(fā)送 Request 和接收 Response 的所有記錄。
Ajax其實(shí)有其特殊的請(qǐng)求類(lèi)型,它叫做 xhr,在上圖中我們可以發(fā)現(xiàn)一個(gè)名稱(chēng)為 getIndex 開(kāi)頭的請(qǐng)求,其 Type 為 xhr,這就是一個(gè) Ajax 請(qǐng)求,我們鼠標(biāo)點(diǎn)擊這個(gè)請(qǐng)求,可以查看這個(gè)請(qǐng)求的詳細(xì)信息,如圖 6-4 所示:
圖 6-4 詳細(xì)信息
我們?cè)谟覀?cè)可以觀察到其 Request Headers、URL 和 Response Headers 等信息,如圖 6-5 所示:
圖 6-5 詳細(xì)信息
其中 Request Headers 中有一個(gè)信息為 X-Requested-With:XMLHttpRequest,這就標(biāo)記了此請(qǐng)求是 Ajax 請(qǐng)求。
隨后我們點(diǎn)擊一下 Preview,即可看到響應(yīng)的內(nèi)容,響應(yīng)內(nèi)容是 Json 格式,在這里 Chrome 為我們自動(dòng)做了解析,我們可以點(diǎn)擊箭頭來(lái)展開(kāi)和收起相應(yīng)內(nèi)容,如圖 6-6 所示:
圖 6-6 Json 結(jié)果
觀察可以發(fā)現(xiàn),這里的返回結(jié)果是馬云的個(gè)人信息,如昵稱(chēng)、簡(jiǎn)介、頭像等等,這也就是用來(lái)渲染個(gè)人主頁(yè)所使用的數(shù)據(jù),JavaScript 接收到這些數(shù)據(jù)之后,再執(zhí)行相應(yīng)的渲染方法,整個(gè)頁(yè)面就被渲染出來(lái)了。
另外也可以切換到 Response 選項(xiàng)卡,可以觀察到真實(shí)的返回?cái)?shù)據(jù),如圖 6-7 所示:
圖 6-7 Response 內(nèi)容
接下來(lái)我們切回到第一個(gè)請(qǐng)求,觀察一下它的 Response 是什么,如圖 6-8 所示:
圖 6-8 Response 內(nèi)容
這是最原始的鏈接 https://m.weibo.cn/u/2145291155 返回的結(jié)果,其代碼只有五十行,結(jié)構(gòu)也非常簡(jiǎn)單,只是執(zhí)行了一些 JavaScript。
所以說(shuō),我們所看到的微博頁(yè)面的真實(shí)數(shù)據(jù)并不是最原始的頁(yè)面返回的,而是后來(lái)執(zhí)行 JavaScript 后再次向后臺(tái)發(fā)送了 Ajax 請(qǐng)求,拿到數(shù)據(jù)后再進(jìn)一步渲染出來(lái)的。
接下來(lái)我們?cè)倮?Chrome 開(kāi)發(fā)者工具的篩選功能篩選出所有的 Ajax 請(qǐng)求,在請(qǐng)求的上方有一層篩選欄,我們可以點(diǎn)擊 XHR,這樣在下方顯示的所有請(qǐng)求便都是 Ajax 請(qǐng)求了,如圖 6-9 所示:
圖 6-9 Ajax 請(qǐng)求
再接下來(lái)我們我們不斷滑動(dòng)頁(yè)面,可以看到在頁(yè)面底部有一條條新的微博被刷出,而開(kāi)發(fā)者工具下方也一個(gè)個(gè)地出現(xiàn) Ajax 請(qǐng)求,這樣我們就可以捕獲到所有的 Ajax 請(qǐng)求了。
隨意點(diǎn)開(kāi)一個(gè)條目都可以清楚地看到其 Request URL、Request Headers、Response Headers、Response Body等內(nèi)容,想要模擬請(qǐng)求和提取就非常簡(jiǎn)單了。
如圖所示內(nèi)容便是馬云某一頁(yè)微博的列表信息,如圖 6-10 所示:
圖 6-10 微博列表信息
3. 結(jié)語(yǔ)到現(xiàn)在為止我們已經(jīng)可以分析出來(lái) Ajax 請(qǐng)求的一些詳細(xì)信息了,接下來(lái)我們只需要用程序來(lái)模擬這些 Ajax 請(qǐng)求就可以輕松提取我們所需要的信息了。
所以在下一節(jié)我們來(lái)用 Python 實(shí)現(xiàn) Ajax 請(qǐng)求的模擬,從而實(shí)現(xiàn)數(shù)據(jù)的抓取。
仍然是拿微博為例,我們接下來(lái)用 Python 來(lái)模擬這些 Ajax 請(qǐng)求,把馬云發(fā)過(guò)的微博爬取下來(lái)。
1. 分析請(qǐng)求我們打開(kāi) Ajax 的 XHR 過(guò)濾器,然后一直滑動(dòng)頁(yè)面加載新的微博內(nèi)容,可以看到會(huì)不斷有Ajax請(qǐng)求發(fā)出。
我們選定其中一個(gè)請(qǐng)求來(lái)分析一下它的參數(shù)信息,點(diǎn)擊該請(qǐng)求進(jìn)入詳情頁(yè)面,如圖 6-11 所示:
圖 6-11 詳情頁(yè)面
可以發(fā)現(xiàn)這是一個(gè) GET 類(lèi)型的請(qǐng)求,請(qǐng)求鏈接為:https://m.weibo.cn/api/contai...;value=2145291155&containerid=1076032145291155&page=2,請(qǐng)求的參數(shù)有四個(gè):type、value、containerid、page。
隨后我們?cè)倏匆幌缕渌恼?qǐng)求,觀察一下這些請(qǐng)求,發(fā)現(xiàn)它們的 type、value、containerid 始終如一。type 始終為 uid,value 的值就是頁(yè)面的鏈接中的數(shù)字,其實(shí)這就是用戶(hù)的 id,另外還有一個(gè) containerid,經(jīng)過(guò)觀察發(fā)現(xiàn)它就是 107603 然后加上用戶(hù) id。所以改變的值就是 page,很明顯這個(gè)參數(shù)就是用來(lái)控制分頁(yè)的,page=1 代表第一頁(yè),page=2 代表第二頁(yè),以此類(lèi)推。
以上的推斷過(guò)程可以實(shí)際觀察參數(shù)的規(guī)律即可得出。
隨后我們觀察一下這個(gè)請(qǐng)求的響應(yīng)內(nèi)容,如圖 6-12 所示:
圖 6-12 響應(yīng)內(nèi)容
它是一個(gè) Json 格式,瀏覽器開(kāi)發(fā)者工具自動(dòng)為做了解析方便我們查看,可以看到最關(guān)鍵的兩部分信息就是 cardlistInfo 和 cards,將二者展開(kāi),cardlistInfo 里面包含了一個(gè)比較重要的信息就是 total,經(jīng)過(guò)觀察后發(fā)現(xiàn)其實(shí)它是微博的總數(shù)量,我們可以根據(jù)這個(gè)數(shù)字來(lái)估算出分頁(yè)的數(shù)目。
cards 則是一個(gè)列表,它包含了 10 個(gè)元素,我們展開(kāi)其中一個(gè)來(lái)看一下,如圖 6-13 所示:
圖 6-13 列表內(nèi)容
發(fā)現(xiàn)它又有一個(gè)比較重要的字段,叫做 mblog,繼續(xù)把它展開(kāi),發(fā)現(xiàn)它包含的正是微博的一些信息。比如 attitudes_count 贊數(shù)目、comments_count 評(píng)論數(shù)目、reposts_count 轉(zhuǎn)發(fā)數(shù)目、created_at 發(fā)布時(shí)間、text 微博正文等等,得來(lái)全不費(fèi)功夫,而且都是一些格式化的內(nèi)容,所以我們提取信息也更加方便了。
這樣我們可以請(qǐng)求一個(gè)接口就得到 10 條微博,而且請(qǐng)求的時(shí)候只需要改變 page 參數(shù)即可,目前總共 138 條微博那么只需要請(qǐng)求 14 次即可,也就是 page 最大可以設(shè)置為14。
這樣我們只需要簡(jiǎn)單做一個(gè)循環(huán)就可以獲取到所有的微博了。
在這里我們就開(kāi)始用程序來(lái)模擬這些 Ajax 請(qǐng)求,將馬云的所有微博全部爬取下來(lái)。
首先我們定義一個(gè)方法,來(lái)獲取每次請(qǐng)求的結(jié)果,在請(qǐng)求時(shí)page 是一個(gè)可變參數(shù),所以我們將它作為方法的參數(shù)傳遞進(jìn)來(lái),代碼如下:
from urllib.parse import urlencode import requests base_url = "https://m.weibo.cn/api/container/getIndex?" headers = { ??? "Host": "m.weibo.cn", ??? "Referer": "https://m.weibo.cn/u/2145291155", ??? "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", ??? "X-Requested-With": "XMLHttpRequest", } def get_page(page): ??? params = { ??????? "type": "uid", ??????? "value": "2145291155", ??????? "containerid": "1076032145291155", ??????? "page": page ??? } ??? url = base_url + urlencode(params) ??? try: ??????? response = requests.get(url, headers=headers) ??????? if response.status_code == 200: ??????????? return response.json() ??? except requests.ConnectionError as e: ??????? print("Error", e.args)
首先在這里我們定義了一個(gè) base_url 來(lái)表示請(qǐng)求的 URL 的前半部分,接下來(lái)構(gòu)造了一個(gè)參數(shù)字典,其中 type、value、containerid 是固定的參數(shù),只有 page 是可變參數(shù),接下來(lái)我們調(diào)用了 urlencode() 方法將參數(shù)轉(zhuǎn)化為 URL 的 GET請(qǐng)求參數(shù),即類(lèi)似于type=uid&value=2145291155&containerid=1076032145291155&page=2 這樣的形式,隨后 base_url 與參數(shù)拼合形成一個(gè)新的 URL,然后我們用 Requests 請(qǐng)求這個(gè)鏈接,加入 headers 參數(shù),然后判斷響應(yīng)的狀態(tài)碼,如果是200,則直接調(diào)用 json() 方法將內(nèi)容解析為 Json 返回,否則不返回任何信息,如果出現(xiàn)異常則捕獲并輸出其異常信息。
隨后我們需要定義一個(gè)解析方法,用來(lái)從結(jié)果中提取我們想要的信息,比如我們這次想保存微博的 id、正文、贊數(shù)、評(píng)論數(shù)、轉(zhuǎn)發(fā)數(shù)這幾個(gè)內(nèi)容,那可以先將 cards 遍歷,然后獲取 mblog 中的各個(gè)信息,賦值為一個(gè)新的字典返回即可。
from pyquery import PyQuery as pq def parse_page(json): ??? if json: ??????? items = json.get("cards") ??????? for item in items: ??????????? item = item.get("mblog") ??????????? weibo = {} ??????????? weibo["id"] = item.get("id") ??????????? weibo["text"] = pq(item.get("text")).text() ??????????? weibo["attitudes"] = item.get("attitudes_count") ??????????? weibo["comments"] = item.get("comments_count") ??????????? weibo["reposts"] = item.get("reposts_count") ??????????? yield weibo
在這里我們借助于 PyQuery 將正文中的 HTML 標(biāo)簽去除掉。
最后我們遍歷一下 page,一共 14 頁(yè),將提取到的結(jié)果打印輸出即可。
if __name__ == "__main__": ??? for page in range(1, 15): ??????? json = get_page(page) ??????? results = parse_page(json) ??????? for result in results: ??????????? print(result)
另外我們還可以加一個(gè)方法將結(jié)果保存到 MongoDB 數(shù)據(jù)庫(kù)。
from pymongo import MongoClient client = MongoClient() db = client["weibo"] collection = db["weibo"] def save_to_mongo(result): ??? if collection.insert(result): ??????? print("Saved to Mongo")
最后整理一下,最后的程序如下:
import requests from urllib.parse import urlencode from pyquery import PyQuery as pq from pymongo import MongoClient base_url = "https://m.weibo.cn/api/container/getIndex?" headers = { ??? "Host": "m.weibo.cn", ??? "Referer": "https://m.weibo.cn/u/2145291155", ??? "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", ??? "X-Requested-With": "XMLHttpRequest", } client = MongoClient() db = client["weibo"] collection = db["weibo"] max_page = 14 def get_page(page): ??? params = { ??????? "type": "uid", ??????? "value": "2145291155", ??????? "containerid": "1076032145291155", ??????? "page": page ??? } ??? url = base_url + urlencode(params) ??? try: ??????? response = requests.get(url, headers=headers) ??????? if response.status_code == 200: ??????????? return response.json() ??? except requests.ConnectionError as e: ??????? print("Error", e.args) def parse_page(json): ??? if json: ??????? items = json.get("cards") ??????? for item in items: ??????????? item = item.get("mblog") ??????????? weibo = {} ??????????? weibo["id"] = item.get("id") ??????????? weibo["text"] = pq(item.get("text")).text() ??????????? weibo["attitudes"] = item.get("attitudes_count") ??????????? weibo["comments"] = item.get("comments_count") ??????????? weibo["reposts"] = item.get("reposts_count") ??????????? yield weibo def save_to_mongo(result): ??? if collection.insert(result): ??????? print("Saved to Mongo") if __name__ == "__main__": ??? for page in range(1, max_page + 1): ??????? json = get_page(page) ??????? results = parse_page(json) ??????? for result in results: ??????????? print(result) ??????????? save_to_mongo(result)
運(yùn)行程序后樣例輸出結(jié)果如下:
{"id": "3938863363932540", "text": "我們也許不能解決所有的問(wèn)題,但我們可以盡自己的力量去解決一些問(wèn)題。移動(dòng)互聯(lián)網(wǎng)不能只是讓留守孩子多了一個(gè)隔空說(shuō)話(huà)的手機(jī),移動(dòng)互聯(lián)網(wǎng)是要讓父母和孩子一直在一起。過(guò)年了,回家吧…… 農(nóng)村淘寶2016團(tuán)圓賀歲片《福與李》 ", "attitudes": 21785, "comments": 40232, "reposts": 2561} Saved to Mongo {"id": "3932968885900763", "text": "跟來(lái)自陜甘寧云貴川六省的100位優(yōu)秀鄉(xiāng)村教師共度了難忘的兩天,接下來(lái)我又得出遠(yuǎn)門(mén)了。。。為了4000萬(wàn)就讀于鄉(xiāng)村學(xué)校的孩子,所以有了這么一群堅(jiān)毅可愛(ài)的老師,有了這么多關(guān)注鄉(xiāng)村教育的各界人士,這兩天感動(dòng)、欣喜、振奮!我們?cè)诟髯缘念I(lǐng)域里,一直堅(jiān)持和努力吧!", "attitudes": 32057, "comments": 7916, "reposts": 2332} Saved to Mongo
查看一下 MongoDB,相應(yīng)的數(shù)據(jù)也被保存到 MongoDB,如圖 6-14 所示:
圖 6-14 保存結(jié)果
4. 本節(jié)代碼本節(jié)代碼地址:https://github.com/oldmarkfac...
5. 結(jié)語(yǔ)本節(jié)實(shí)例的目的是為了演示 Ajax 的模擬請(qǐng)求過(guò)程,爬取的結(jié)果不是重點(diǎn),該程序仍有很多可以完善的地方,如頁(yè)碼的動(dòng)態(tài)計(jì)算、微博查看全文等,如感興趣可以嘗試一下。
通過(guò)這個(gè)實(shí)例我們主要是為了學(xué)會(huì)怎樣去分析 Ajax 請(qǐng)求,怎樣用程序來(lái)模擬抓取 Ajax 請(qǐng)求,了解了相關(guān)抓取原理之后,下一節(jié)的 Ajax 實(shí)戰(zhàn)演練會(huì)更加得心應(yīng)手。
上一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---34、數(shù)據(jù)存儲(chǔ):非關(guān)系型數(shù)據(jù)庫(kù)存儲(chǔ):Redis
下一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---36、分析Ajax爬取今日頭條街拍美圖
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/44091.html
摘要:上一篇文章網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)數(shù)據(jù)爬取下一篇文章網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)動(dòng)態(tài)渲染頁(yè)面抓取本節(jié)我們以今日頭條為例來(lái)嘗試通過(guò)分析請(qǐng)求來(lái)抓取網(wǎng)頁(yè)數(shù)據(jù)的方法,我們這次要抓取的目標(biāo)是今日頭條的街拍美圖,抓取完成之后將每組圖片分文件夾下載到本地保存下來(lái)。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---35、 Ajax數(shù)據(jù)爬取下一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---37、動(dòng)態(tài)渲染頁(yè)面抓取:Selenium 本節(jié)我們...
摘要:在前面我們講到了和的概念,我們向網(wǎng)站的服務(wù)器發(fā)送一個(gè),返回的的便是網(wǎng)頁(yè)源代碼。渲染頁(yè)面有時(shí)候我們?cè)谟没蜃ト【W(wǎng)頁(yè)時(shí),得到的源代碼實(shí)際和瀏覽器中看到的是不一樣的。所以使用基本請(qǐng)求庫(kù)得到的結(jié)果源代碼可能跟瀏覽器中的頁(yè)面源代碼不太一樣。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---16、Web網(wǎng)頁(yè)基礎(chǔ)下一篇文章:Python3網(wǎng)絡(luò)爬蟲(chóng)實(shí)戰(zhàn)---18、Session和Cookies 爬蟲(chóng),即網(wǎng)...
摘要:,引言注釋上一篇爬蟲(chóng)實(shí)戰(zhàn)安居客房產(chǎn)經(jīng)紀(jì)人信息采集,訪(fǎng)問(wèn)的網(wǎng)頁(yè)是靜態(tài)網(wǎng)頁(yè),有朋友模仿那個(gè)實(shí)戰(zhàn)來(lái)采集動(dòng)態(tài)加載豆瓣小組的網(wǎng)頁(yè),結(jié)果不成功。 showImg(https://segmentfault.com/img/bVzdNZ); 1, 引言 注釋?zhuān)荷弦黄禤ython爬蟲(chóng)實(shí)戰(zhàn)(3):安居客房產(chǎn)經(jīng)紀(jì)人信息采集》,訪(fǎng)問(wèn)的網(wǎng)頁(yè)是靜態(tài)網(wǎng)頁(yè),有朋友模仿那個(gè)實(shí)戰(zhàn)來(lái)采集動(dòng)態(tài)加載豆瓣小組的網(wǎng)頁(yè),結(jié)果不成功...
摘要:為了使用各種應(yīng)用場(chǎng)景,該項(xiàng)目的整個(gè)網(wǎng)絡(luò)爬蟲(chóng)產(chǎn)品線(xiàn)包含了四類(lèi)產(chǎn)品,如下圖所示本實(shí)戰(zhàn)是上圖中的獨(dú)立爬蟲(chóng)的一個(gè)實(shí)例,以采集安居客房產(chǎn)經(jīng)紀(jì)人信息為例,記錄整個(gè)采集流程,包括和依賴(lài)庫(kù)的安裝,即便是初學(xué)者,也可以跟著文章內(nèi)容成功地完成運(yùn)行。 showImg(https://segmentfault.com/img/bVy2Iy); 1, 引言 Python開(kāi)源網(wǎng)絡(luò)爬蟲(chóng)項(xiàng)目啟動(dòng)之初,我們就把網(wǎng)絡(luò)爬蟲(chóng)...
摘要:時(shí)間永遠(yuǎn)都過(guò)得那么快,一晃從年注冊(cè),到現(xiàn)在已經(jīng)過(guò)去了年那些被我藏在收藏夾吃灰的文章,已經(jīng)太多了,是時(shí)候把他們整理一下了。那是因?yàn)槭詹貖A太亂,橡皮擦給設(shè)置私密了,不收拾不好看呀。 ...
閱讀 1679·2021-11-16 11:41
閱讀 2468·2021-11-08 13:14
閱讀 3118·2019-08-29 17:16
閱讀 3088·2019-08-29 16:30
閱讀 1851·2019-08-29 13:51
閱讀 366·2019-08-23 18:38
閱讀 3235·2019-08-23 17:14
閱讀 639·2019-08-23 15:09