整合營(yíng)銷(xiāo)服務(wù)商

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

          免費(fèi)咨詢(xún)熱線(xiàn):

          R語(yǔ)言爬蟲(chóng)系列|動(dòng)態(tài)數(shù)據(jù)抓取范例

          過(guò)前面幾期的推送,小編基本上已經(jīng)將R語(yǔ)言爬蟲(chóng)所需要的基本知識(shí)介紹完了。R雖然是以一門(mén)統(tǒng)計(jì)分析工具出現(xiàn)在大多數(shù)人印象中的,但其畢竟本質(zhì)上是一門(mén)編程語(yǔ)言,對(duì)于爬蟲(chóng)的支持雖不如Python那樣多快好省,但悉心研究一下總能做出一些讓你驚喜的效果。

          大約很早之前,小編就寫(xiě)過(guò)關(guān)于R語(yǔ)言爬蟲(chóng)新貴rvest的抓取介紹,之前說(shuō)rvest+SelectGadgetor是結(jié)構(gòu)化網(wǎng)頁(yè)抓取的實(shí)戰(zhàn)利器,大家的溢美之詞不斷。詳情可見(jiàn)推文:

          R語(yǔ)言爬蟲(chóng)利器:rvest包+SelectorGadget抓取鏈家杭州二手房數(shù)據(jù)

          但網(wǎng)絡(luò)爬蟲(chóng)這個(gè)江湖太險(xiǎn)惡,單靠一招rvest行走江湖必然兇多吉少,一不小心碰到什么AJAX和動(dòng)態(tài)網(wǎng)頁(yè)憑僅掌握rvest的各位必定束手無(wú)策。本文小編就簡(jiǎn)單介紹下在用R語(yǔ)言進(jìn)行實(shí)際的網(wǎng)絡(luò)數(shù)據(jù)抓取時(shí)如何將動(dòng)態(tài)數(shù)據(jù)給弄到手。

          所謂動(dòng)態(tài)網(wǎng)頁(yè)和異步加載,在之前的系列4的時(shí)候小編已通過(guò)AJAX介紹過(guò)了,簡(jiǎn)單而言就是我明明在網(wǎng)頁(yè)中看到了這個(gè)數(shù)據(jù),但到后臺(tái)HTML中卻找不到了,這通常就是XHR在作祟。這時(shí)候我們就不要看原始的HTML數(shù)據(jù)了,需要進(jìn)行二次請(qǐng)求,通過(guò)web開(kāi)發(fā)者工具找到真實(shí)請(qǐng)求的url。下面小編就以?xún)蓚€(gè)網(wǎng)頁(yè)為例,分別通過(guò)GET和POST請(qǐng)求拿到動(dòng)態(tài)網(wǎng)頁(yè)數(shù)據(jù),全過(guò)程主要使用httr包來(lái)實(shí)現(xiàn),httr包可謂是RCurl包的精簡(jiǎn)版,說(shuō)其短小精悍也不為過(guò)。httr包與RCurl包在主要函數(shù)的區(qū)別如下所示:

          GET請(qǐng)求抓取微信好友列表數(shù)據(jù)

          很早之前圈子里就看到過(guò)用Python抓取自己微信好友數(shù)據(jù)的案例分享,今天便以微信網(wǎng)頁(yè)版為例,探一探其網(wǎng)頁(yè)結(jié)構(gòu)。首先登錄個(gè)人微信網(wǎng)頁(yè)版,右鍵打開(kāi)web開(kāi)發(fā)者工具,下來(lái)一大堆請(qǐng)求:

          簡(jiǎn)單找一下發(fā)現(xiàn)網(wǎng)頁(yè)中的微信好友列表信息并沒(méi)有呈現(xiàn)在HTML 中,大概可以斷定微信好友數(shù)據(jù)是通過(guò)動(dòng)態(tài)加載來(lái)顯示的,所以直接定位到XHR中,經(jīng)過(guò)幾番嘗試,結(jié)合右側(cè)的preview,我們會(huì)發(fā)現(xiàn)大量整齊劃一的數(shù)據(jù),所以二次請(qǐng)求的url真的就是它了:

          找到真正的url之后,接下來(lái)就是獲取請(qǐng)求信息了,切換到Headers版塊,Header版塊下的4個(gè)子信息我們都需要關(guān)注,它們是我們構(gòu)造爬蟲(chóng)請(qǐng)求頭的關(guān)鍵。

          從Header中我們可以看到該信息是通過(guò)GET方法請(qǐng)求得到的,General信息下的Request URL,Request Method, Status Code; Response Headers信息下的Connection, Content-Type; Request Headers信息下的Accept, Cookie, Referer, User-Agent以及最后的Query String Parameters都是我們需要重點(diǎn)關(guān)注的。

          找到相應(yīng)的信息之后,我們便可以直接利用httr包在R中構(gòu)建爬蟲(chóng)請(qǐng)求:

          #傳入微信cookie信息
          Cookie <- “我的微信cookie”
          #構(gòu)造請(qǐng)求頭
          headers <- c('Accept'='application/json',
           'Content-Type'='text/plain', 
           'User-Agent'='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537. 36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X Met aSr 1.0', 
           'Referer'='https://wx.qq.com/',
           'Connection'='keep-alive',
           'cookie'=Cookie
          )

          二次請(qǐng)求實(shí)際的url:

          #實(shí)際請(qǐng)求的url
          url<-"https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?r=1507597918348&seq=0&skey=@crypt_ee7cd3e3_70091604da65a07600cfdca47b81cfaf"

          GET方法單次執(zhí)行請(qǐng)求:

          #執(zhí)行請(qǐng)求
          louwill <- GET(url,add_headers(.headers =headers))

          響應(yīng)結(jié)果如下:

          -> GET /cgi-bin/mmwebwx-bin/webwxgetcontact?r=1507597918348&seq=0&skey=@crypt_ee7cd3e3_70091604da65a07600cfdca47b81cfaf HTTP/1.1
          -> Host: wx.qq.com
          -> Accept-Encoding: gzip, deflate
          -> Accept: application/json
          -> Content-Type: text/plain
          -> User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0
          -> Referer: https://wx.qq.com/
          -> Connection: keep-alive
          -> cookie: 我的微信cookie
          ->
          <- HTTP/1.1 200 OK
          <- Connection: keep-alive
          <- Content-Type: text/plain
          <- Content-Encoding: gzip
          <- Content-Length: 90977
          <-

          響應(yīng)狀態(tài)碼為200,okay。

          從響應(yīng)中提取原始字符內(nèi)容:

          content(louwill)
          [1] "{\n\"BaseResponse\": {\n\"Ret\": 0,\n\"ErrMsg\": \"\"\n}\n,\n\"MemberCount\": 658,\n\"MemberList\": [{\n\"Uin\": 0,\n\"UserName\": \"weixin\",\n\"NickName\": \"微信團(tuán)隊(duì)\",\n\"HeadImgUrl\": \"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=570002&username=weixin&skey=@crypt_ee7cd3e3_70091604da65a07600cfdca47b81cfaf\",\n\"ContactFlag\": 1,\n\"MemberCount\": 0,\n\"MemberList\": [],\n\"RemarkName\": \"\",\n\"HideInputBarFlag\": 0,\n\"Sex\": 0,\n\"Signature\": \"微信團(tuán)隊(duì)官方帳號(hào)\",\n\"VerifyFlag\": 56,\n\"OwnerUin\": 0,\n\"PYInitial\": \"WXTD\",\n\"PYQuanPin\": \"weixintuandui\",\n\"RemarkPYInitial\": \"\",\n\"RemarkPYQuanPin\": \"\",\n\"StarFriend\": 0,\n\"AppAccountFlag\": 0,\n\"Statues\": 0,\n\"AttrStatus\": 4,\n\"Province\": \"\",\n\"City\": \"\",\n\"Alias\": \"\",\n\"SnsFlag\": 0,\n\"UniFriend\": 0,\n\"DisplayName\": \"\",\n\"ChatRoomId\": 0,\n\"KeyWord\": \"wei\",\n\"EncryChatRoomId\": \"\",\n\"IsOwner\": 0\n}\n,{\n\"Uin\": 0,\n\"UserName\": \"@34c5cc09db0a616522f7ccc7309b1d29\",\n\"NickName\": \"微信支付... <truncated>

          從結(jié)果中可以看出,微信好友列表的信息就被抓取下來(lái)了,數(shù)據(jù)信息非常雜亂,需要進(jìn)一步清洗整理,小編這里重在展示GET請(qǐng)求獲取動(dòng)態(tài)網(wǎng)頁(yè)數(shù)據(jù)(主要是懶)就不往下整理啦。

          POST請(qǐng)求抓取網(wǎng)易云課堂數(shù)據(jù)

          雖說(shuō)動(dòng)態(tài)網(wǎng)站數(shù)據(jù)請(qǐng)求也有GET方法的,但小編發(fā)現(xiàn)POST方法才是動(dòng)態(tài)網(wǎng)頁(yè)的主要的請(qǐng)求方式。受杜老師小魔方文章啟發(fā),小編也試一下這個(gè)網(wǎng)頁(yè)上的效果。登錄網(wǎng)易云課堂賬號(hào),右鍵開(kāi)發(fā)者工具,直接定位到XHR,查找課程數(shù)據(jù)屬于哪個(gè)url。通過(guò)嘗試和preview,可以發(fā)現(xiàn)課程信息都被封裝在一個(gè)studycourse.json的文件中:

          跟GET請(qǐng)求方法一樣,切換到Header版塊后繼續(xù)關(guān)注General等四個(gè)子信息,但POST請(qǐng)求下我們需要注意的一點(diǎn)是:POST請(qǐng)求下沒(méi)有像GET請(qǐng)求一樣的Query String Parameters,而是由Request Payload來(lái)構(gòu)造請(qǐng)求頭表單參數(shù),這一點(diǎn)和GET方法大不相同。總而言之,在動(dòng)態(tài)網(wǎng)頁(yè)的HTTP請(qǐng)求中,如果是GET請(qǐng)求,請(qǐng)求頭表單參數(shù)以name=value&name1=value1的形式直接附在url后面,如果是POST請(qǐng)求,請(qǐng)求頭表單參數(shù)以相同的形式放在構(gòu)造的表單體中,所以對(duì)于網(wǎng)易云課堂的數(shù)據(jù)請(qǐng)求在R中構(gòu)造如下:

          #構(gòu)造請(qǐng)求頭
          #這里小編沒(méi)有登錄賬號(hào),cookie就不要了
          headers <- c('Accept'='application/json',
           'Content-Type'='application/json', 
           'User-Agent'='ozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0', 
           'edu-script-token'= '37aa682d1473455c8a77e6a4476e8f9e',
           'Referer'='http://study.163.com/courses',
           'Connection'='keep-alive'
          )
          #POST請(qǐng)求需要構(gòu)造請(qǐng)求頭表單參數(shù)
          payload<-list(
           'pageIndex'=1,
           'pageSize'=50,
           'relativeOffset'=0,
           'frontCategoryId'=-1
          )

          二次請(qǐng)求實(shí)際的url:

          url <- "http://study.163.com/p/search/studycourse.json"

          POST方法單次執(zhí)行請(qǐng)求:

          louwill2<-POST(url,add_headers(.headers =headers),body =payload, encode="json")

          結(jié)果如下:

          -> POST /p/search/studycourse.json HTTP/1.1
          -> Host: study.163.com
          -> Accept-Encoding: gzip, deflate
          -> Cookie: EDUWEBDEVICE=5d0eadccd2314c4d8bc6e758b8b23d4e; NTESSTUDYSI=d3d36984547a43d6924334ee6a184a08
          -> Accept: application/json
          -> Content-Type: application/json
          -> User-Agent: ozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0
          -> edu-script-token: 83de95a25f5d45eb84bfeff8ec334e15
          -> Referer: http://study.163.com/courses
          -> Connection: keep-alive
          -> cookie: 網(wǎng)易云課堂cookie
          -> Content-Length: 69
          ->
          >> {"pageIndex":1,"pageSize":50,"relativeOffset":0,"frontCategoryId":-1}
          <- HTTP/1.1 200 OK
          <- Server: nginx
          <- Date: Tue, 10 Oct 2017 08:29:51 GMT
          <- Content-Type: application/json;charset=UTF-8
          <- Transfer-Encoding: chunked
          <- Connection: keep-alive
          <- Vary: Accept-Encoding
          <- Server-Host: hzayq-study-platform7
          <- Content-Encoding: gzip
          <-
          Response [http://study.163.com/p/search/studycourse.json]
           Date: 2017-10-10 08:29
           Status: 200
           Content-Type: application/json;charset=UTF-8
           Size: 71 kB

          請(qǐng)求狀態(tài)碼200,也okay。

          從響應(yīng)中提取原始字符內(nèi)容:

          head(content(louwill2))
          $result$list[[39]]
          $result$list[[39]]$productId
          [1] 1002971001
          $result$list[[39]]$courseId
          [1] 1002971001
          $result$list[[39]]$productName
          [1] "英語(yǔ)知識(shí)點(diǎn)解析及小學(xué)單詞帶讀"
          $result$list[[39]]$productType
          [1] 0
          $result$list[[39]]$startTime
          [1] -1
          $result$list[[39]]$endTime
          [1] 9.223372e+18
          $result$list[[39]]$description
          [1] "通過(guò)幾分鐘的微課片段,精講中小學(xué)的英語(yǔ)知識(shí)點(diǎn),讓學(xué)生通過(guò)比較學(xué)習(xí),把這些知識(shí)點(diǎn)編織成有系統(tǒng)的知識(shí)網(wǎng)。"
          $result$list[[39]]$provider
          [1] "中小學(xué)英語(yǔ)語(yǔ)法王"

          跟前面一樣,后續(xù)的數(shù)據(jù)處理與清洗小編就懶得弄啦。POST方法與GET方法略有區(qū)別,就是需要構(gòu)造請(qǐng)求頭表單參數(shù)。R語(yǔ)言針對(duì)動(dòng)態(tài)網(wǎng)頁(yè)抓取,使用RCurl/httr包,認(rèn)真分析網(wǎng)頁(yè)結(jié)構(gòu),一般都能搞定。

          End.

          運(yùn)行人員:中國(guó)統(tǒng)計(jì)網(wǎng)小編(微信號(hào):itongjilove)

          微博ID:中國(guó)統(tǒng)計(jì)網(wǎng)

          中國(guó)統(tǒng)計(jì)網(wǎng),是國(guó)內(nèi)最早的大數(shù)據(jù)學(xué)習(xí)網(wǎng)站,公眾號(hào):中國(guó)統(tǒng)計(jì)網(wǎng)

          http://www.itongji.cn

          為一名毫無(wú)開(kāi)發(fā)經(jīng)驗(yàn)的非計(jì)算機(jī)出身的數(shù)據(jù)愛(ài)好者,初入此坑時(shí)深受爬蟲(chóng)難學(xué)之苦,當(dāng)初未通Python之道,寫(xiě)個(gè)scrapy框架就痛苦至極。想想現(xiàn)在大數(shù)據(jù)技術(shù)那么牛逼了,為什么我抓個(gè)數(shù)據(jù)還處處被封,后來(lái)又覺(jué)得是自己技術(shù)不夠強(qiáng)大。本文以拉勾網(wǎng)為例給大家介紹一款便捷快速的R語(yǔ)言爬蟲(chóng)方法,通過(guò)Rvest包+SelectorGdaget選擇器即可輕松實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)抓取。

          01準(zhǔn)備工具:Rvest包+SelectorGadget選擇器

          下載安裝Rvest包:

          install.packages("Rvest")

          library(Rvest)

          要想全面了解Rvest包的朋友可以去查官方幫助文檔:

          help(package="Rvest")

          Selectorgadget插件作為一個(gè)輕便快捷的CSS選擇器,好用程度簡(jiǎn)直爆炸,鼠標(biāo)點(diǎn)擊幾下即可生成你想要抓取的html節(jié)點(diǎn)信息。這么一款神器,調(diào)用方法也是極其簡(jiǎn)單,打開(kāi)任何一款搜索網(wǎng)頁(yè),鍵入Selectorgadget,點(diǎn)擊第一個(gè)鏈接,也是Selectorgadget官方鏈接,拉到頁(yè)面底端倒數(shù)第二個(gè)鏈接,將其拖拽到你的瀏覽器收藏夾,待下次打開(kāi)需要爬取的網(wǎng)頁(yè)時(shí)點(diǎn)擊即可啟用。

          需拖拽的鏈接如圖(Or drag this link to your bookmark bar):

          下次調(diào)用時(shí),打開(kāi)需要抓取的網(wǎng)頁(yè),點(diǎn)擊我們拖拽到收藏夾的Selectorgadget會(huì)在網(wǎng)頁(yè)右下角出現(xiàn)一個(gè)長(zhǎng)方形條框,點(diǎn)擊網(wǎng)頁(yè)中任何我們想抓取的信息,條框內(nèi)即可生成相應(yīng)的文本表達(dá)式,將這些文本表達(dá)式復(fù)制到Rvest包對(duì)應(yīng)的爬蟲(chóng)函數(shù)中,即可輕松完成抓取。需要注意的是,使用Selectorgadget選擇節(jié)點(diǎn)信息是一個(gè)篩選的過(guò)程,其間需要將我們不需要的信息(點(diǎn)擊后變紅)重復(fù)點(diǎn)擊以刪除,留下需要的信息(綠色和黃色部分)。

          02,拉勾網(wǎng)數(shù)據(jù)抓取

          我們選擇抓取拉勾網(wǎng)數(shù)據(jù)分析師崗位信息:

          抓取代碼如下:

          library(stringr)library(xml2)
          library(rvest) #加載包
          i<-1:30#設(shè)定抓取頁(yè)數(shù)
          lagou_data<-data.frame()#創(chuàng)建數(shù)據(jù)框存儲(chǔ)數(shù)據(jù)
          #寫(xiě)個(gè)循環(huán),對(duì)固定網(wǎng)頁(yè)結(jié)構(gòu)重復(fù)抓取
          for (i in 1:30){
          web<-read_html(str_c("https://www.lagou.com/zhaopin/shujufenxi/",i),encoding="UTF-8")#read_html函數(shù)解析網(wǎng)頁(yè)并規(guī)定編碼str_c函數(shù)對(duì)頁(yè)數(shù)循環(huán)
          job<-web%>%html_nodes("h2")%>%html_text()#"h2"即為Selectorgadget定位節(jié)點(diǎn)信息
          job[16]<-NA
          job<-job[!is.na(job)]#將多余信息設(shè)置為NA并剔除
          #以此類(lèi)推,抓取崗位其他信息
          company<-web%>%html_nodes(".company_name a")%>%html_text()
          inf1<-web%>%html_nodes(".p_bot .li_b_l")%>%html_text()
          inf2<-web%>%html_nodes(".industry")%>%html_text()
          temptation<-web%>%html_nodes(".li_b_r")%>%html_text()

          #存儲(chǔ)以上信息

          job_inf<-data.frame(job,company,inf1,inf2,temptation)

          lagou_data<-rbind(lagou_data,job_inf)

          }

          write.csv(job_inf,file="D:/Rdata/datasets/job_inf.csv")#寫(xiě)入數(shù)據(jù)

          清洗整理后最終抓取部分?jǐn)?shù)據(jù)示例如圖:

          03,簡(jiǎn)單小結(jié)

          用rvest包結(jié)合SelectorGadget 選擇器能夠快速實(shí)現(xiàn)R語(yǔ)言下的網(wǎng)絡(luò)數(shù)據(jù)抓取,并適當(dāng)結(jié)合stringr包中的字符串處理函數(shù)對(duì)網(wǎng)頁(yè)數(shù)據(jù)進(jìn)行清洗和整理,抓取過(guò)程省時(shí)省力,適合R語(yǔ)言和爬蟲(chóng)入門(mén)的朋友使用學(xué)習(xí)。

          End.

          來(lái)源:公眾號(hào)“R語(yǔ)言中文社區(qū)”

          運(yùn)行人員:中國(guó)統(tǒng)計(jì)網(wǎng)小編(微信號(hào):itongjilove)

          微博ID:中國(guó)統(tǒng)計(jì)網(wǎng)

          中國(guó)統(tǒng)計(jì)網(wǎng),是國(guó)內(nèi)最早的大數(shù)據(jù)學(xué)習(xí)網(wǎng)站,公眾號(hào):中國(guó)統(tǒng)計(jì)網(wǎng)

          http://www.itongji.cn

          讀:本文主要分為兩個(gè)部分:一部分是網(wǎng)絡(luò)爬蟲(chóng)的概述,幫助大家詳細(xì)了解網(wǎng)絡(luò)爬蟲(chóng);另一部分是HTTP請(qǐng)求的Python實(shí)現(xiàn),幫助大家了解Python中實(shí)現(xiàn)HTTP請(qǐng)求的各種方式,以便具備編寫(xiě)HTTP網(wǎng)絡(luò)程序的能力。

          作者:范傳輝

          如需轉(zhuǎn)載請(qǐng)聯(lián)系華章科技

          01 網(wǎng)絡(luò)爬蟲(chóng)概述

          接下來(lái)從網(wǎng)絡(luò)爬蟲(chóng)的概念、用處與價(jià)值和結(jié)構(gòu)等三個(gè)方面,讓大家對(duì)網(wǎng)絡(luò)爬蟲(chóng)有一個(gè)基本的了解。

          1. 網(wǎng)絡(luò)爬蟲(chóng)及其應(yīng)用

          隨著網(wǎng)絡(luò)的迅速發(fā)展,萬(wàn)維網(wǎng)成為大量信息的載體,如何有效地提取并利用這些信息成為一個(gè)巨大的挑戰(zhàn),網(wǎng)絡(luò)爬蟲(chóng)應(yīng)運(yùn)而生。網(wǎng)絡(luò)爬蟲(chóng)(又被稱(chēng)為網(wǎng)頁(yè)蜘蛛、網(wǎng)絡(luò)機(jī)器人),是一種按照一定的規(guī)則,自動(dòng)地抓取萬(wàn)維網(wǎng)信息的程序或者腳本。下面通過(guò)圖3-1展示一下網(wǎng)絡(luò)爬蟲(chóng)在互聯(lián)網(wǎng)中起到的作用:

          ▲圖3-1 網(wǎng)絡(luò)爬蟲(chóng)

          網(wǎng)絡(luò)爬蟲(chóng)按照系統(tǒng)結(jié)構(gòu)和實(shí)現(xiàn)技術(shù),大致可以分為以下幾種類(lèi)型:通用網(wǎng)絡(luò)爬蟲(chóng)、聚焦網(wǎng)絡(luò)爬蟲(chóng)、增量式網(wǎng)絡(luò)爬蟲(chóng)、深層網(wǎng)絡(luò)爬蟲(chóng)。實(shí)際的網(wǎng)絡(luò)爬蟲(chóng)系統(tǒng)通常是幾種爬蟲(chóng)技術(shù)相結(jié)合實(shí)現(xiàn)的。

          搜索引擎(Search Engine),例如傳統(tǒng)的通用搜索引擎baidu、Yahoo和Google等,是一種大型復(fù)雜的網(wǎng)絡(luò)爬蟲(chóng),屬于通用性網(wǎng)絡(luò)爬蟲(chóng)的范疇。但是通用性搜索引擎存在著一定的局限性:

          1. 不同領(lǐng)域、不同背景的用戶(hù)往往具有不同的檢索目的和需求,通用搜索引擎所返回的結(jié)果包含大量用戶(hù)不關(guān)心的網(wǎng)頁(yè)。
          2. 通用搜索引擎的目標(biāo)是盡可能大的網(wǎng)絡(luò)覆蓋率,有限的搜索引擎服務(wù)器資源與無(wú)限的網(wǎng)絡(luò)數(shù)據(jù)資源之間的矛盾將進(jìn)一步加深。
          3. 萬(wàn)維網(wǎng)數(shù)據(jù)形式的豐富和網(wǎng)絡(luò)技術(shù)的不斷發(fā)展,圖片、數(shù)據(jù)庫(kù)、音頻、視頻多媒體等不同數(shù)據(jù)大量出現(xiàn),通用搜索引擎往往對(duì)這些信息含量密集且具有一定結(jié)構(gòu)的數(shù)據(jù)無(wú)能為力,不能很好地發(fā)現(xiàn)和獲取。
          4. 通用搜索引擎大多提供基于關(guān)鍵字的檢索,難以支持根據(jù)語(yǔ)義信息提出的查詢(xún)。

          為了解決上述問(wèn)題,定向抓取相關(guān)網(wǎng)頁(yè)資源的聚焦爬蟲(chóng)應(yīng)運(yùn)而生。

          聚焦爬蟲(chóng)是一個(gè)自動(dòng)下載網(wǎng)頁(yè)的程序,它根據(jù)既定的抓取目標(biāo),有選擇地訪(fǎng)問(wèn)萬(wàn)維網(wǎng)上的網(wǎng)頁(yè)與相關(guān)的鏈接,獲取所需要的信息。與通用爬蟲(chóng)不同,聚焦爬蟲(chóng)并不追求大的覆蓋,而將目標(biāo)定為抓取與某一特定主題內(nèi)容相關(guān)的網(wǎng)頁(yè),為面向主題的用戶(hù)查詢(xún)準(zhǔn)備數(shù)據(jù)資源。

          說(shuō)完了聚焦爬蟲(chóng),接下來(lái)再說(shuō)一下增量式網(wǎng)絡(luò)爬蟲(chóng)。增量式網(wǎng)絡(luò)爬蟲(chóng)是指對(duì)已下載網(wǎng)頁(yè)采取增量式更新和只爬行新產(chǎn)生的或者已經(jīng)發(fā)生變化網(wǎng)頁(yè)的爬蟲(chóng),它能夠在一定程度上保證所爬行的頁(yè)面是盡可能新的頁(yè)面。

          和周期性爬行和刷新頁(yè)面的網(wǎng)絡(luò)爬蟲(chóng)相比,增量式爬蟲(chóng)只會(huì)在需要的時(shí)候爬行新產(chǎn)生或發(fā)生更新的頁(yè)面,并不重新下載沒(méi)有發(fā)生變化的頁(yè)面,可有效減少數(shù)據(jù)下載量,及時(shí)更新已爬行的網(wǎng)頁(yè),減小時(shí)間和空間上的耗費(fèi),但是增加了爬行算法的復(fù)雜度和實(shí)現(xiàn)難度。

          例如:想獲取趕集網(wǎng)的招聘信息,以前爬取過(guò)的數(shù)據(jù)沒(méi)有必要重復(fù)爬取,只需要獲取更新的招聘數(shù)據(jù),這時(shí)候就要用到增量式爬蟲(chóng)。

          最后說(shuō)一下深層網(wǎng)絡(luò)爬蟲(chóng)。Web頁(yè)面按存在方式可以分為表層網(wǎng)頁(yè)和深層網(wǎng)頁(yè)。表層網(wǎng)頁(yè)是指?jìng)鹘y(tǒng)搜索引擎可以索引的頁(yè)面,以超鏈接可以到達(dá)的靜態(tài)網(wǎng)頁(yè)為主構(gòu)成的Web頁(yè)面。深層網(wǎng)絡(luò)是那些大部分內(nèi)容不能通過(guò)靜態(tài)鏈接獲取的、隱藏在搜索表單后的,只有用戶(hù)提交一些關(guān)鍵詞才能獲得的Web頁(yè)面。

          例如用戶(hù)登錄或者注冊(cè)才能訪(fǎng)問(wèn)的頁(yè)面。可以想象這樣一個(gè)場(chǎng)景:爬取貼吧或者論壇中的數(shù)據(jù),必須在用戶(hù)登錄后,有權(quán)限的情況下才能獲取完整的數(shù)據(jù)。

          2. 網(wǎng)絡(luò)爬蟲(chóng)結(jié)構(gòu)

          下面用一個(gè)通用的網(wǎng)絡(luò)爬蟲(chóng)結(jié)構(gòu)來(lái)說(shuō)明網(wǎng)絡(luò)爬蟲(chóng)的基本工作流程,如圖3-4所示。

          ▲圖3-4 網(wǎng)絡(luò)爬蟲(chóng)結(jié)構(gòu)

          網(wǎng)絡(luò)爬蟲(chóng)的基本工作流程如下:

          1. 首先選取一部分精心挑選的種子URL。
          2. 將這些URL放入待抓取URL隊(duì)列。
          3. 從待抓取URL隊(duì)列中讀取待抓取隊(duì)列的URL,解析DNS,并且得到主機(jī)的IP,并將URL對(duì)應(yīng)的網(wǎng)頁(yè)下載下來(lái),存儲(chǔ)進(jìn)已下載網(wǎng)頁(yè)庫(kù)中。此外,將這些URL放進(jìn)已抓取URL隊(duì)列。
          4. 分析已抓取URL隊(duì)列中的URL,從已下載的網(wǎng)頁(yè)數(shù)據(jù)中分析出其他URL,并和已抓取的URL進(jìn)行比較去重,最后將去重過(guò)的URL放入待抓取URL隊(duì)列,從而進(jìn)入下一個(gè)循環(huán)。

          02 HTTP請(qǐng)求的Python實(shí)現(xiàn)

          通過(guò)上面的網(wǎng)絡(luò)爬蟲(chóng)結(jié)構(gòu),我們可以看到讀取URL、下載網(wǎng)頁(yè)是每一個(gè)爬蟲(chóng)必備而且關(guān)鍵的功能,這就需要和HTTP請(qǐng)求打交道。接下來(lái)講解Python中實(shí)現(xiàn)HTTP請(qǐng)求的三種方式:urllib2/urllib、httplib/urllib以及Requests。

          1. urllib2/urllib實(shí)現(xiàn)

          urllib2和urllib是Python中的兩個(gè)內(nèi)置模塊,要實(shí)現(xiàn)HTTP功能,實(shí)現(xiàn)方式是以u(píng)rllib2為主,urllib為輔。

          1.1 首先實(shí)現(xiàn)一個(gè)完整的請(qǐng)求與響應(yīng)模型

          urllib2提供一個(gè)基礎(chǔ)函數(shù)urlopen,通過(guò)向指定的URL發(fā)出請(qǐng)求來(lái)獲取數(shù)據(jù)。最簡(jiǎn)單的形式是:

          import urllib2
          response=urllib2.urlopen('http://www.zhihu.com')
          html=response.read()
          print html
          

          其實(shí)可以將上面對(duì)http://www.zhihu.com的請(qǐng)求響應(yīng)分為兩步,一步是請(qǐng)求,一步是響應(yīng),形式如下:

          import urllib2
          # 請(qǐng)求
          request=urllib2.Request('http://www.zhihu.com')
          # 響應(yīng)
          response = urllib2.urlopen(request)
          html=response.read()
          print html
          

          上面這兩種形式都是GET請(qǐng)求,接下來(lái)演示一下POST請(qǐng)求,其實(shí)大同小異,只是增加了請(qǐng)求數(shù)據(jù),這時(shí)候用到了urllib。示例如下:

          import urllib
          import urllib2
          url = 'http://www.xxxxxx.com/login'
          postdata = {'username' : 'qiye',
           'password' : 'qiye_pass'}
          # info 需要被編碼為urllib2能理解的格式,這里用到的是urllib
          data = urllib.urlencode(postdata)
          req = urllib2.Request(url, data)
          response = urllib2.urlopen(req)
          html = response.read()
          

          但是有時(shí)會(huì)出現(xiàn)這種情況:即使POST請(qǐng)求的數(shù)據(jù)是對(duì)的,但是服務(wù)器拒絕你的訪(fǎng)問(wèn)。這是為什么呢?問(wèn)題出在請(qǐng)求中的頭信息,服務(wù)器會(huì)檢驗(yàn)請(qǐng)求頭,來(lái)判斷是否是來(lái)自瀏覽器的訪(fǎng)問(wèn),這也是反爬蟲(chóng)的常用手段。

          1.2 請(qǐng)求頭headers處理

          將上面的例子改寫(xiě)一下,加上請(qǐng)求頭信息,設(shè)置一下請(qǐng)求頭中的User-Agent域和Referer域信息。

          import urllib
          import urllib2
          url = 'http://www.xxxxxx.com/login'
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          referer='http://www.xxxxxx.com/'
          postdata = {'username' : 'qiye',
           'password' : 'qiye_pass'}
          # 將user_agent,referer寫(xiě)入頭信息
          headers={'User-Agent':user_agent,'Referer':referer}
          data = urllib.urlencode(postdata)
          req = urllib2.Request(url, data,headers)
          response = urllib2.urlopen(req)
          html = response.read()
          

          也可以這樣寫(xiě),使用add_header來(lái)添加請(qǐng)求頭信息,修改如下:

          import urllib
          import urllib2
          url = 'http://www.xxxxxx.com/login'
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          referer='http://www.xxxxxx.com/'
          postdata = {'username' : 'qiye',
           'password' : 'qiye_pass'}
          data = urllib.urlencode(postdata)
          req = urllib2.Request(url)
          # 將user_agent,referer寫(xiě)入頭信息
          req.add_header('User-Agent',user_agent)
          req.add_header('Referer',referer)
          req.add_data(data)
          response = urllib2.urlopen(req)
          html = response.read()
          

          對(duì)有些header要特別留意,服務(wù)器會(huì)針對(duì)這些header做檢查,例如:

          • User-Agent:有些服務(wù)器或Proxy會(huì)通過(guò)該值來(lái)判斷是否是瀏覽器發(fā)出的請(qǐng)求。
          • Content-Type:在使用REST接口時(shí),服務(wù)器會(huì)檢查該值,用來(lái)確定HTTP Body中的內(nèi)容該怎樣解析。在使用服務(wù)器提供的RESTful或SOAP服務(wù)時(shí),Content-Type設(shè)置錯(cuò)誤會(huì)導(dǎo)致服務(wù)器拒絕服務(wù)。常見(jiàn)的取值有:application/xml(在XML RPC,如RESTful/SOAP調(diào)用時(shí)使用)、application/json(在JSON RPC調(diào)用時(shí)使用)、application/x-www-form-urlencoded(瀏覽器提交Web表單時(shí)使用)。
          • Referer:服務(wù)器有時(shí)候會(huì)檢查防盜鏈。

          1.3 Cookie處理

          urllib2對(duì)Cookie的處理也是自動(dòng)的,使用CookieJar函數(shù)進(jìn)行Cookie的管理。如果需要得到某個(gè)Cookie項(xiàng)的值,可以這么做:

          import urllib2
          import cookielib
          cookie = cookielib.CookieJar()
          opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
          response = opener.open('http://www.zhihu.com')
          for item in cookie:
           print item.name+':'+item.value
          

          但是有時(shí)候會(huì)遇到這種情況,我們不想讓urllib2自動(dòng)處理,我們想自己添加Cookie的內(nèi)容,可以通過(guò)設(shè)置請(qǐng)求頭中的Cookie域來(lái)做:

          import urllib2
          opener = urllib2.build_opener()
          opener.addheaders.append( ( 'Cookie', 'email=' + "xxxxxxx@163.com" ) )
          req = urllib2.Request( "http://www.zhihu.com/" )
          response = opener.open(req)
          print response.headers
          retdata = response.read()
          

          1.4 Timeout設(shè)置超時(shí)

          在Python2.6之前的版本,urllib2的API并沒(méi)有暴露Timeout的設(shè)置,要設(shè)置Timeout值,只能更改Socket的全局Timeout值。示例如下:

          import urllib2
          import socket
          socket.setdefaulttimeout(10) # 10 秒鐘后超時(shí)
          urllib2.socket.setdefaulttimeout(10) # 另一種方式
          

          在Python2.6及新的版本中,urlopen函數(shù)提供了對(duì)Timeout的設(shè)置,示例如下:

          import urllib2
          request=urllib2.Request('http://www.zhihu.com')
          response = urllib2.urlopen(request,timeout=2)
          html=response.read()
          print html
          

          1.5 獲取HTTP響應(yīng)碼

          對(duì)于200 OK來(lái)說(shuō),只要使用urlopen返回的response對(duì)象的getcode()方法就可以得到HTTP的返回碼。但對(duì)其他返回碼來(lái)說(shuō),urlopen會(huì)拋出異常。這時(shí)候,就要檢查異常對(duì)象的code屬性了,示例如下:

          import urllib2
          try:
           response = urllib2.urlopen('http://www.google.com')
           print response
          except urllib2.HTTPError as e:
           if hasattr(e, 'code'):
           print 'Error code:',e.code
          

          1.6 重定向

          urllib2默認(rèn)情況下會(huì)針對(duì)HTTP 3XX返回碼自動(dòng)進(jìn)行重定向動(dòng)作。要檢測(cè)是否發(fā)生了重定向動(dòng)作,只要檢查一下Response的URL和Request的URL是否一致就可以了,示例如下:

          import urllib2
          response = urllib2.urlopen('http://www.zhihu.cn')
          isRedirected = response.geturl() == 'http://www.zhihu.cn'
          

          如果不想自動(dòng)重定向,可以自定義HTTPRedirectHandler類(lèi),示例如下:

          import urllib2
          class RedirectHandler(urllib2.HTTPRedirectHandler):
           def http_error_301(self, req, fp, code, msg, headers):
           pass
           def http_error_302(self, req, fp, code, msg, headers):
           result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, 
           msg, headers)
           result.status = code
           result.newurl = result.geturl()
           return result
          opener = urllib2.build_opener(RedirectHandler)
          opener.open('http://www.zhihu.cn')
          

          1.7 Proxy的設(shè)置

          在做爬蟲(chóng)開(kāi)發(fā)中,必不可少地會(huì)用到代理。urllib2默認(rèn)會(huì)使用環(huán)境變量http_proxy來(lái)設(shè)置HTTP Proxy。但是我們一般不采用這種方式,而是使用ProxyHandler在程序中動(dòng)態(tài)設(shè)置代理,示例代碼如下:

          import urllib2
          proxy = urllib2.ProxyHandler({'http': '127.0.0.1:8087'})
          opener = urllib2.build_opener([proxy,])
          urllib2.install_opener(opener)
          response = urllib2.urlopen('http://www.zhihu.com/')
          print response.read()
          

          這里要注意的一個(gè)細(xì)節(jié),使用urllib2.install_opener()會(huì)設(shè)置urllib2的全局opener,之后所有的HTTP訪(fǎng)問(wèn)都會(huì)使用這個(gè)代理。這樣使用會(huì)很方便,但不能做更細(xì)粒度的控制,比如想在程序中使用兩個(gè)不同的Proxy設(shè)置,這種場(chǎng)景在爬蟲(chóng)中很常見(jiàn)。比較好的做法是不使用install_opener去更改全局的設(shè)置,而只是直接調(diào)用opener的open方法代替全局的urlopen方法,修改如下:

          import urllib2
          proxy = urllib2.ProxyHandler({'http': '127.0.0.1:8087'})
          opener = urllib2.build_opener(proxy,)
          response = opener.open("http://www.zhihu.com/")
          print response.read()
          

          2. httplib/urllib實(shí)現(xiàn)

          httplib模塊是一個(gè)底層基礎(chǔ)模塊,可以看到建立HTTP請(qǐng)求的每一步,但是實(shí)現(xiàn)的功能比較少,正常情況下比較少用到。在Python爬蟲(chóng)開(kāi)發(fā)中基本上用不到,所以在此只是進(jìn)行一下知識(shí)普及。下面介紹一下常用的對(duì)象和函數(shù):

          • 創(chuàng)建HTTPConnection對(duì)象:
          • class httplib.HTTPConnection(host[, port[, strict[, timeout[, source_address]]]])。
          • 發(fā)送請(qǐng)求:
          • HTTPConnection.request(method, url[, body[, headers]])。
          • 獲得響應(yīng):
          • HTTPConnection.getresponse()。
          • 讀取響應(yīng)信息:
          • HTTPResponse.read([amt])。
          • 獲得指定頭信息:
          • HTTPResponse.getheader(name[, default])。
          • 獲得響應(yīng)頭(header, value)元組的列表:
          • HTTPResponse.getheaders()。
          • 獲得底層socket文件描述符:
          • HTTPResponse.fileno()。
          • 獲得頭內(nèi)容:
          • HTTPResponse.msg。
          • 獲得頭http版本:
          • HTTPResponse.version。
          • 獲得返回狀態(tài)碼:
          • HTTPResponse.status。
          • 獲得返回說(shuō)明:
          • HTTPResponse.reason。

          接下來(lái)演示一下GET請(qǐng)求和POST請(qǐng)求的發(fā)送,首先是GET請(qǐng)求的示例,如下所示:

          import httplib
          conn =None
          try:
           conn = httplib.HTTPConnection("www.zhihu.com")
           conn.request("GET", "/")
           response = conn.getresponse()
           print response.status, response.reason
           print '-' * 40
           headers = response.getheaders()
           for h in headers:
           print h
           print '-' * 40
           print response.msg
          except Exception,e:
           print e
          finally:
           if conn:
           conn.close()
          

          POST請(qǐng)求的示例如下:

          import httplib, urllib
          conn = None
          try:
           params = urllib.urlencode({'name': 'qiye', 'age': 22})
           headers = {"Content-type": "application/x-www-form-urlencoded"
           , "Accept": "text/plain"}
           conn = httplib.HTTPConnection("www.zhihu.com", 80, timeout=3)
           conn.request("POST", "/login", params, headers)
           response = conn.getresponse()
           print response.getheaders() # 獲取頭信息
           print response.status
           print response.read()
          except Exception, e:
           print e
           finally:
           if conn:
           conn.close()
          

          3. 更人性化的Requests

          Python中Requests實(shí)現(xiàn)HTTP請(qǐng)求的方式,是本人極力推薦的,也是在Python爬蟲(chóng)開(kāi)發(fā)中最為常用的方式。Requests實(shí)現(xiàn)HTTP請(qǐng)求非常簡(jiǎn)單,操作更加人性化。

          Requests庫(kù)是第三方模塊,需要額外進(jìn)行安裝。Requests是一個(gè)開(kāi)源庫(kù),源碼位于:

          GitHub: https://github.com/kennethreitz/requests

          希望大家多多支持作者。

          使用Requests庫(kù)需要先進(jìn)行安裝,一般有兩種安裝方式:

          • 使用pip進(jìn)行安裝,安裝命令為:pip install requests,不過(guò)可能不是最新版。
          • 直接到GitHub上下載Requests的源代碼,下載鏈接為:
          • https://github.com/kennethreitz/requests/releases
          • 將源代碼壓縮包進(jìn)行解壓,然后進(jìn)入解壓后的文件夾,運(yùn)行setup.py文件即可。

          如何驗(yàn)證Requests模塊安裝是否成功呢?在Python的shell中輸入import requests,如果不報(bào)錯(cuò),則是安裝成功。如圖3-5所示。

          ▲圖3-5 驗(yàn)證Requests安裝

          3.1 首先還是實(shí)現(xiàn)一個(gè)完整的請(qǐng)求與響應(yīng)模型

          以GET請(qǐng)求為例,最簡(jiǎn)單的形式如下:

          import requests
          r = requests.get('http://www.baidu.com')
          print r.content
          

          大家可以看到比urllib2實(shí)現(xiàn)方式的代碼量少。接下來(lái)演示一下POST請(qǐng)求,同樣是非常簡(jiǎn)短,更加具有Python風(fēng)格。示例如下:

          import requests
          postdata={'key':'value'}
          r = requests.post('http://www.xxxxxx.com/login',data=postdata)
          print r.content
          

          HTTP中的其他請(qǐng)求方式也可以用Requests來(lái)實(shí)現(xiàn),示例如下:

          r = requests.put('http://www.xxxxxx.com/put', data = {'key':'value'})
          r = requests.delete('http://www.xxxxxx.com/delete')
          r = requests.head('http://www.xxxxxx.com/get')
          r = requests.options('http://www.xxxxxx.com/get')
          

          接著講解一下稍微復(fù)雜的方式,大家肯定見(jiàn)過(guò)類(lèi)似這樣的URL:

          http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

          就是在網(wǎng)址后面緊跟著“?”,“?”后面還有參數(shù)。那么這樣的GET請(qǐng)求該如何發(fā)送呢?肯定有人會(huì)說(shuō),直接將完整的URL帶入即可,不過(guò)Requests還提供了其他方式,示例如下:

          import requests
           payload = {'Keywords': 'blog:qiyeboy','pageindex':1}
          r = requests.get('http://zzk.cnblogs.com/s/blogpost', params=payload)
          print r.url
          

          通過(guò)打印結(jié)果,我們看到最終的URL變成了:

          http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

          3.2 響應(yīng)與編碼

          還是從代碼入手,示例如下:

          import requests
          r = requests.get('http://www.baidu.com')
          print 'content-->'+r.content
          print 'text-->'+r.text
          print 'encoding-->'+r.encoding
          r.encoding='utf-8'
          print 'new text-->'+r.text
          

          其中r.content返回的是字節(jié)形式,r.text返回的是文本形式,r.encoding返回的是根據(jù)HTTP頭猜測(cè)的網(wǎng)頁(yè)編碼格式。

          輸出結(jié)果中:“text-->”之后的內(nèi)容在控制臺(tái)看到的是亂碼,“encoding-->”之后的內(nèi)容是ISO-8859-1(實(shí)際上的編碼格式是UTF-8),由于Requests猜測(cè)編碼錯(cuò)誤,導(dǎo)致解析文本出現(xiàn)了亂碼。Requests提供了解決方案,可以自行設(shè)置編碼格式,r.encoding='utf-8'設(shè)置成UTF-8之后,“new text-->”的內(nèi)容就不會(huì)出現(xiàn)亂碼。

          但是這種手動(dòng)的方式略顯笨拙,下面提供一種更加簡(jiǎn)便的方式:chardet,這是一個(gè)非常優(yōu)秀的字符串/文件編碼檢測(cè)模塊。安裝方式如下:

          pip install chardet
          

          安裝完成后,使用chardet.detect()返回字典,其中confidence是檢測(cè)精確度,encoding是編碼形式。示例如下:

          import requests
          r = requests.get('http://www.baidu.com')
          print chardet.detect(r.content)
          r.encoding = chardet.detect(r.content)['encoding']
          print r.text
          

          直接將chardet探測(cè)到的編碼,賦給r.encoding實(shí)現(xiàn)解碼,r.text輸出就不會(huì)有亂碼了。

          除了上面那種直接獲取全部響應(yīng)的方式,還有一種流模式,示例如下:

          import requests
          r = requests.get('http://www.baidu.com',stream=True)
          print r.raw.read(10)
          

          設(shè)置stream=True標(biāo)志位,使響應(yīng)以字節(jié)流方式進(jìn)行讀取,r.raw.read函數(shù)指定讀取的字節(jié)數(shù)。

          3.3 請(qǐng)求頭headers處理

          Requests對(duì)headers的處理和urllib2非常相似,在Requests的get函數(shù)中添加headers參數(shù)即可。示例如下:

          import requests
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          headers={'User-Agent':user_agent}
          r = requests.get('http://www.baidu.com',headers=headers)
          print r.content
          

          3.4 響應(yīng)碼code和響應(yīng)頭headers處理

          獲取響應(yīng)碼是使用Requests中的status_code字段,獲取響應(yīng)頭使用Requests中的headers字段。示例如下:

          import requests
          r = requests.get('http://www.baidu.com')
          if r.status_code == requests.codes.ok:
           print r.status_code# 響應(yīng)碼
           print r.headers# 響應(yīng)頭
           print r.headers.get('content-type')# 推薦使用這種獲取方式,獲取其中的某個(gè)字段
           print r.headers['content-type']# 不推薦使用這種獲取方式
          else:
           r.raise_for_status()
          

          上述程序中,r.headers包含所有的響應(yīng)頭信息,可以通過(guò)get函數(shù)獲取其中的某一個(gè)字段,也可以通過(guò)字典引用的方式獲取字典值,但是不推薦,因?yàn)槿绻侄沃袥](méi)有這個(gè)字段,第二種方式會(huì)拋出異常,第一種方式會(huì)返回None。

          r.raise_for_status()是用來(lái)主動(dòng)地產(chǎn)生一個(gè)異常,當(dāng)響應(yīng)碼是4XX或5XX時(shí),raise_for_status()函數(shù)會(huì)拋出異常,而響應(yīng)碼為200時(shí),raise_for_status()函數(shù)返回None。

          3.5 Cookie處理

          如果響應(yīng)中包含Cookie的值,可以如下方式獲取Cookie字段的值,示例如下:

          import requests
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          headers={'User-Agent':user_agent}
          r = requests.get('http://www.baidu.com',headers=headers)
          # 遍歷出所有的cookie字段的值
          for cookie in r.cookies.keys():
           print cookie+':'+r.cookies.get(cookie)
          

          如果想自定義Cookie值發(fā)送出去,可以使用以下方式,示例如下:

          import requests
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          headers={'User-Agent':user_agent}
          cookies = dict(name='qiye',age='10')
          r = requests.get('http://www.baidu.com',headers=headers,cookies=cookies)
          print r.text
          

          還有一種更加高級(jí),且能自動(dòng)處理Cookie的方式,有時(shí)候我們不需要關(guān)心Cookie值是多少,只是希望每次訪(fǎng)問(wèn)的時(shí)候,程序自動(dòng)把Cookie的值帶上,像瀏覽器一樣。Requests提供了一個(gè)session的概念,在連續(xù)訪(fǎng)問(wèn)網(wǎng)頁(yè),處理登錄跳轉(zhuǎn)時(shí)特別方便,不需要關(guān)注具體細(xì)節(jié)。使用方法示例如下:

          import Requests
          oginUrl = 'http://www.xxxxxxx.com/login'
          s = requests.Session()
          #首先訪(fǎng)問(wèn)登錄界面,作為游客,服務(wù)器會(huì)先分配一個(gè)cookie
          r = s.get(loginUrl,allow_redirects=True)
          datas={'name':'qiye','passwd':'qiye'}
          #向登錄鏈接發(fā)送post請(qǐng)求,驗(yàn)證成功,游客權(quán)限轉(zhuǎn)為會(huì)員權(quán)限
          r = s.post(loginUrl, data=datas,allow_redirects= True)
          print r.text
          

          上面的這段程序,其實(shí)是正式做Python開(kāi)發(fā)中遇到的問(wèn)題,如果沒(méi)有第一步訪(fǎng)問(wèn)登錄的頁(yè)面,而是直接向登錄鏈接發(fā)送Post請(qǐng)求,系統(tǒng)會(huì)把你當(dāng)做非法用戶(hù),因?yàn)樵L(fǎng)問(wèn)登錄界面時(shí)會(huì)分配一個(gè)Cookie,需要將這個(gè)Cookie在發(fā)送Post請(qǐng)求時(shí)帶上,這種使用Session函數(shù)處理Cookie的方式之后會(huì)很常用。

          3.6 重定向與歷史信息

          處理重定向只是需要設(shè)置一下allow_redirects字段即可,例如:

          r=requests.get('http://www.baidu.com',allow_redirects=True)

          將allow_redirects設(shè)置為T(mén)rue,則是允許重定向;設(shè)置為False,則是禁止重定向。如果是允許重定向,可以通過(guò)r.history字段查看歷史信息,即訪(fǎng)問(wèn)成功之前的所有請(qǐng)求跳轉(zhuǎn)信息。示例如下:

          import requests
          r = requests.get('http://github.com')
          print r.url
          print r.status_code
          print r.history
          

          打印結(jié)果如下:

          https://github.com/
          200
          (<Response [301]>,)
          

          上面的示例代碼顯示的效果是訪(fǎng)問(wèn)GitHub網(wǎng)址時(shí),會(huì)將所有的HTTP請(qǐng)求全部重定向?yàn)镠TTPS。

          3.7 超時(shí)設(shè)置

          超時(shí)選項(xiàng)是通過(guò)參數(shù)timeout來(lái)進(jìn)行設(shè)置的,示例如下:

          requests.get('http://github.com', timeout=2)
          

          3.8 代理設(shè)置

          使用代理Proxy,你可以為任意請(qǐng)求方法通過(guò)設(shè)置proxies參數(shù)來(lái)配置單個(gè)請(qǐng)求:

          import requests
          proxies = {
           "http": "http://0.10.1.10:3128",
           "https": "http://10.10.1.10:1080",
          }
          requests.get("http://example.org", proxies=proxies)
          

          也可以通過(guò)環(huán)境變量HTTP_PROXY和HTTPS_PROXY?來(lái)配置代理,但是在爬蟲(chóng)開(kāi)發(fā)中不常用。你的代理需要使用HTTP Basic Auth,可以使用http://user:password@host/語(yǔ)法:

          proxies = {
           "http": "http://user:pass@10.10.1.10:3128/",
          }
          

          03 小結(jié)

          本文主要講解了網(wǎng)絡(luò)爬蟲(chóng)的結(jié)構(gòu)和應(yīng)用,以及Python實(shí)現(xiàn)HTTP請(qǐng)求的幾種方法。希望大家對(duì)本文中的網(wǎng)絡(luò)爬蟲(chóng)工作流程和Requests實(shí)現(xiàn)HTTP請(qǐng)求的方式重點(diǎn)吸收消化。

          關(guān)于作者:范傳輝,資深網(wǎng)蟲(chóng),Python開(kāi)發(fā)者,參與開(kāi)發(fā)了多項(xiàng)網(wǎng)絡(luò)應(yīng)用,在實(shí)際開(kāi)發(fā)中積累了豐富的實(shí)戰(zhàn)經(jīng)驗(yàn),并善于總結(jié),貢獻(xiàn)了多篇技術(shù)文章廣受好評(píng)。研究興趣是網(wǎng)絡(luò)安全、爬蟲(chóng)技術(shù)、數(shù)據(jù)分析、驅(qū)動(dòng)開(kāi)發(fā)等技術(shù)。

          本文摘編自《Python爬蟲(chóng)開(kāi)發(fā)與項(xiàng)目實(shí)戰(zhàn)》,經(jīng)出版方授權(quán)發(fā)布。

          延伸閱讀《Python爬蟲(chóng)開(kāi)發(fā)與項(xiàng)目實(shí)戰(zhàn)》

          推薦語(yǔ):零基礎(chǔ)學(xué)習(xí)爬蟲(chóng)技術(shù),從Python和Web前端基礎(chǔ)開(kāi)始講起,由淺入深,包含大量案例,實(shí)用性強(qiáng)。


          主站蜘蛛池模板: 精品视频无码一区二区三区| 3d动漫精品啪啪一区二区中| 国产一区二区免费| 国产在线一区二区三区在线| 中文字幕av一区| 午夜影院一区二区| 亚洲色精品VR一区区三区| 激情内射亚洲一区二区三区 | 国产aⅴ一区二区三区| 尤物精品视频一区二区三区 | 国产在线视频一区二区三区| 在线观看视频一区二区| 欧美日韩精品一区二区在线视频| 韩国美女vip福利一区| 欲色aV无码一区二区人妻| 竹菊影视欧美日韩一区二区三区四区五区 | 亚洲欧美日韩一区二区三区 | 日韩一区二区在线视频| 麻豆AV无码精品一区二区 | 黄桃AV无码免费一区二区三区| 国产精品成人一区无码| 一本色道久久综合一区| 日本道免费精品一区二区| 东京热人妻无码一区二区av| 亚欧成人中文字幕一区| 亚洲国产日韩在线一区| 欧美日韩国产免费一区二区三区 | 国产激情精品一区二区三区| 久久久国产一区二区三区| 国产成人AV一区二区三区无码| 日韩精品无码一区二区三区免费 | 视频一区视频二区在线观看| 精品国产免费一区二区三区| 大香伊蕉日本一区二区| 久久精品综合一区二区三区| 久久国产一区二区| 少妇一夜三次一区二区| 一区 二区 三区 中文字幕| 久久久一区二区三区| 欧美日本精品一区二区三区| 国产精品一区12p|