標題無心冒犯,就是以爲這個廣告挺好玩的html
前期回顧:你要偷偷學Python(第八天)python
上一篇呢,上一篇咱們瞭解了一下網頁的基本結構,而且經過對網頁的分析抓取了一點數據出來。
可是咱們就這麼知足了嗎?這顯然是不可能的,你見過哪一個爬蟲就爬幾個字嘛。web
因此今天,咱們來一次性爬上一大波數據!!!
我行,你也行!!!sql
插播一條推送:(若是是小白的話,能夠看一下下面這一段)apache
若是你們在學習中遇到困難,想找一個python學習交流環境,能夠加入咱們的python圈,裙號947618024,可領取python學習資料,會節約不少時間,減小不少遇到的難題。編程
本系列文默認各位有必定的C或C++基礎,由於我是學了點C++的皮毛以後入手的Python。 本系列文默認各位會百度,學習‘模塊’這個模塊的話,仍是建議你們有本身的編輯器和編譯器的,上一篇已經給你們作了推薦啦? 本系列也會着重培養各位的自主動手能力,畢竟我不可能把全部知識點都給你講到,因此本身解決需求的能力就尤其重要,因此我在文中埋得坑請不要把它們當作坑,那是我留給大家的鍛鍊機會,請各顯神通,自行解決。 1234567
哎,怪我孤陋寡聞,實在找不到適合咱們這個階段的網站,個人爬蟲又不斷地讓人捏死,只好借鑑別人的栗子了。。。json
目標網址:http://books.toscrape.com/
任務:爬取目標網址中的分類目錄:swift
有沒有思路?沒思路看我講。瀏覽器
這個會找吧:ruby
第一步,點亮匹配按鈕(之後我就叫它匹配按鈕了)
第二步,把鼠標放到要選的區域,注意,要顏色徹底覆蓋住你要選的區域,覆蓋不住調整鼠標位置。
第三步,左擊鼠標,定位代碼。
第四步,再看一眼那行標籤是否是最小且公共的了,有虛線,能夠看到那行標籤管到哪一層。
其實你再認真找一下,就會發現咱們上面圖中標出的區域並非最小的,最小的是那個< ul >。
第二步怎麼走啊?第一步能夠理解吧,第一步作完事要爲「find_all」服務的,一籃子全撈出來,那第二步天然是要一個一個揀出來嘛,爲「find」服務。
那具體怎麼作就不用我再說了吧,參照上一步。
來我帶你打開一個標籤看一下:
看到沒,井井有條。
第三步天然就要把目標值取出來了嘛,咱們順便把網址也取了吧。
import requests from bs4 import BeautifulSoup res = requests.get('http://books.toscrape.com/') soup = BeautifulSoup(res.text,'html.parser') items = soup.find('ul',class_ = 'nav nav-list').find('ul').find_all('li') #我驚奇的發現,還有這種騷操做 for item in items: kind = item.find('a') print('分類'+kind.text.strip()+'\n網址'+kind['href']+'\n') 123456789101112
這樣打出來你會發現那根本不是一個完整的網址,這要怎麼辦呢?
其實你打開一個目錄,就會發現它的網址長這樣:(這裏我打開的是第一個目錄)
http://books.toscrape.com/catalogue/category/books/travel_2/index.html
這有什麼特色呢?咱把它分開:
http://books.toscrape.com/ catalogue/category/books/travel_2/index.html 12
好,如今再問你看到了什麼?
這兩部分是否是都能找到出處!!
好,如今咱們微調一下上面的代碼:
import requests from bs4 import BeautifulSoup url = 'http://books.toscrape.com/' res = requests.get(url) soup = BeautifulSoup(res.text,'html.parser') items = soup.find('ul',class_ = 'nav nav-list').find('ul').find_all('li') #我驚奇的發現,還有這種騷操做 for item in items: kind = item.find('a') print('分類:'+kind.text.strip()+'\n網址:'+url+kind['href']+'\n') 1234567891011121314
第一題到此告一段落、
學完這些以後,我就想着去爬個人博客評論了。不過一頓操做下來:
gogogo!!!
好,定位代碼段:
好,層層爬取(演示效果,否則我纔不一層一層撥開):
好,結果顯示爲空。
能夠去打印出爬下來的網頁源代碼:res,而後翻一翻,你會驚奇的發現,評論部分被隱藏了!!!
那怎麼辦呢?接下來那就進入咱們今天的第一個知識點了–json串。
依舊是別人的栗子,我來說給你們懂。
網頁源代碼裏沒有咱們想要的數據,那它究竟藏到了哪裏呢?
想找到答案,須要用到一項新技能——翻找Network!
還記得我一開始就叫你們用谷歌瀏覽器嗎?如今就體現出優點了。
首先,打開一個界面,這裏我選擇了志炫的歌單,我比較喜歡他的歌。
小白請跟我來,由於你並不知道哪些網頁是用json 傳輸什麼數據的,因此練習的時候不要本身亂找網頁。
https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E6%9E%97%E5%BF%97%E7%82%AB
這個界面應該會打開吧,怕你們看不到,我特意多圈了幾圈,點它。
好,然而並無發生什麼。
那不急,咱們從新加載一下這個歌單界面,找到空白處,右擊,從新加載。
這時候你會看到這麼一個界面。
你用別的瀏覽器試試,會是什麼效果。我用火狐試過,找是能夠找,一片亂碼而已。
Network的功能是:記錄在當前頁面上發生的全部請求。如今看上去好像空空如也的樣子,這是由於Network記錄的是實時網絡請求。如今網頁都已經加載完成,因此不會有東西。
咱們點擊一下刷新,瀏覽器會從新訪問網絡,這樣就會有記錄。
好,走到這裏了,我以爲我有必要介紹一下這個頁面上幾個比較重要的東西。
從左往右看啊,紅色的圓鈕是啓用Network監控(默認高亮打開),灰色圓圈是清空面板上的信息。右側勾選框Preserve log(放大鏡旁邊那個),它的做用是「保留請求日誌」。若是不點擊這個,當發生頁面跳轉的時候,記錄就會被清空。因此,咱們在爬取一些會發生跳轉的網頁時,會點亮它。
再往右是對請求進行分類查看。咱們最經常使用的是:ALL(查看所有)/XHR(僅查看XHR)
哎,不廢話了,上操做流程吧。
首先,咱們要找的東西是文本嘛,那怎麼辦呢?直接點XHR就行了。
好,如今這麼多東西了,我就直接跟你說要的東西就在這裏面,你要怎麼找?一個一個點開嗎?你會發現不少都是傳一些邊角料,再而後,你會發現那些邊角料都比目標文案要小不少,因此就直接找size最大的那個點進去就好。
咱也不繞彎子了,進去以後直接點Preview。
好,Preview點進去以後本身玩一玩,看看裏面都是些啥。
看完以後,回來,跟我點開旁邊的Headers。
好,看到了什麼?一個網址是吧,複製它,打開它,是否是和Preview裏面的如出一轍,只是排版亂了些。
我就不貼了啊,密集恐懼症就別點開了。
這說明什麼?這說明咱們要爬的網址實際上是這個。
注:若是這個網址打不開,那就不用爬了,人家並不想讓你爬。
那麼,對於這份XHR來講:這個XHR是一個字典,鍵data對應的值也是一個字典;在該字典裏,鍵song對應的值也是一個字典;在該字典裏,鍵list對應的值是一個列表;在該列表裏,一共有20個元素;每個元素都是一個字典;在每一個字典裏,鍵name的值,對應的是歌曲名。
會不會亂?我以爲不會啊,除非你沒有一步一步實操跟進。
講到這裏尚未講到 json串 啊,你先把這個網頁爬出來,打印出來看看,是一個又有點像字典,又有點像字符串的玩意兒。
這玩意兒就是json串了。
答案很簡單,由於不是全部的編程語言都能讀懂Python裏的數據類型(如,列表/字典),可是全部的編程語言,都支持文本(好比在Python中,用字符串這種數據類型來表示文本)這種最樸素的數據類型。
如此,json數據才能實現,跨平臺,跨語言工做。
而json和XHR之間的關係:XHR用於傳輸數據,它能傳輸不少種數據,json是被傳輸的一種數據格式。就是這樣而已。
咱們老是能夠將json格式的數據,轉換成正常的列表/字典,也能夠將列表/字典,轉換成json。
方法很簡單,請求到數據以後,使用json()方法便可成功讀取。接下來的操做,就和列表/字典相一致。
import requests # 引用requests庫 res_music = requests.get('https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&searchid=67818388354301120&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w=%E6%9E%97%E5%BF%97%E7%82%AB&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0') # 調用get方法,下載這個字典 json_music = res_music.json() # 使用json()方法,將response對象,轉爲列表/字典 print(json_music) # 打印json_music的數據類型 12345678
因此接下來怎麼辦呢?
import requests # 引用requests庫 res_music = requests.get('https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&searchid=67818388354301120&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w=%E6%9E%97%E5%BF%97%E7%82%AB&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0') # 調用get方法,下載這個字典 json_music = res_music.json() # 使用json()方法,將response對象,轉爲列表/字典 list_music = json_music['data']['song']['list'] # 一層一層地取字典,獲取歌單列表 for music in list_music: # list_music是一個列表,music是它裏面的元素 print(music['name']) # 以name爲鍵,查找歌曲名 print('所屬專輯:'+music['album']['name']) # 查找專輯名 print('播放時長:'+str(music['interval'])+'秒') # 查找播放時長 print('播放連接:https://y.qq.com/n/yqq/song/'+music['mid']+'.html\n\n') # 查找播放連接 123456789101112131415161718
這回,經過咱們的一頓操做猛如虎,可算是找對了網址啊:
https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=
這是第一頁的評論網址。
好極,咱們開始吧。
import requests res = requests.get('https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=') # 發起請求,填入請求頭和參數 print(res.status_code) print(res.text) 12345678
好極,就試了下水就讓人給懟回來了。。。
莫非今天還真爬不過去了?
不知道,再說吧。。
服務器可能會對咱們這些「投機取巧」的爬蟲作限制處理。一來能夠下降服務器的訪問壓力,畢竟成千上萬次的訪問對代碼來講就是一個for循環的事兒;二來能夠攔截那些想要經過爬蟲竊取數據的競爭者。
那這就有一個問題,服務器怎麼判斷訪問者是一個普通的用戶(經過瀏覽器),仍是一個爬蟲者(經過代碼)呢?
這須要咱們回到瀏覽器中,從新認識一個新的信息欄:請求頭Request Headers。
看下面這張圖
每個請求,都會有一個Request Headers,咱們把它稱做請求頭。它裏面會有一些關於該請求的基本信息,好比:這個請求是從什麼設備什麼瀏覽器上發出?這個請求是從哪一個頁面跳轉而來?
如上圖,user-agent(中文:用戶代理)會記錄你電腦的信息和瀏覽器版本,若是咱們想告知服務器,咱們不是爬蟲,而是一個正常的瀏覽器。就要去修改user-agent。假若不修改,那麼這裏的默認值就會是Python,會被服務器認出來。
origin(中文:源頭)和referer(中文:引用來源)則記錄了這個請求,最初的起源是來自哪一個頁面。它們的區別是referer會比origin攜帶的信息更多些。
對於爬取某些特定信息,也要求你註明請求的來源,即origin或referer的內容。
import requests url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=' headers = { 'origin':'https://lion-wu.blog.csdn.net', # 請求來源,本案例中實際上是不須要加這個參數的,只是爲了演示 'referer':'https://lion-wu.blog.csdn.net/article/details/108858689', # 請求來源,攜帶的信息比「origin」更豐富,本案例中實際上是不須要加這個參數的,只是爲了演示 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' # 標記了請求從什麼設備,什麼瀏覽器上發出 } # 假裝請求頭 res = requests.get(url,headers=headers) 1234567891011121314
好極,幹!!!
此次,我給小爬蟲進行了一波易容,多是它長得不符合服務器的審美吧,因此次次碰壁,此次易容以後,不知道有沒有長到服務器的審美上去呢?讓咱們拭目以待吧!!!
import requests url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=' headers = { 'origin':'https://lion-wu.blog.csdn.net', # 請求來源,本案例中實際上是不須要加這個參數的,只是爲了演示 'referer':'https://lion-wu.blog.csdn.net/article/details/108858689', # 請求來源,攜帶的信息比「origin」更豐富,本案例中實際上是不須要加這個參數的,只是爲了演示 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' # 標記了請求從什麼設備,什麼瀏覽器上發出 } # 假裝請求頭 res = requests.get(url,headers=headers) print(res.status_code) print(res.text) 1234567891011121314151617
bash:129$ python ~/classroom/apps-2-id-5c3d89848939b4000100e7f5/129/main.py 200 {"code":200,"message":"success","data":{"count":60,"pageCount":6,"floorCount":59,"list":[{"info":{"commentId":13646053,"articleId":108858689,"parentId":0,"postTime":"2020-10-30 11:03:45","content":"刪除多張表:本身想 O(∩_∩)O~","userName":"qq_43055855","digg":2,"diggArr":[],"parentUserName":null,"parentNickName":null,"avatar":"https://profile.csdnimg.cn/C/B/3/3_qq_43055855","nickName":"海海不掉頭髮","dateFormat":"6天前","tag":"碼皇","parentTag":null,"years":null,"vip":null,"vipIcon":null,"companyBlog":null,"companyBlogIcon":null,"flag":null,"flagIcon":null,"levelIcon":null},"sub": 123
我就截取一點吧,太大了,能夠看出來截下來了就好。
別說了,也能夠本身去解析一下,這個以咱們以前學的解不了。後面我解給你看。
當服務器趕上了整容事後的小爬蟲,終於「門戶大開」,大方的給了一頁的數據,一頁的數據,一頁。。。
可是我要的是所有啊,你就給我一頁就想打發我?打發叫花子呢?
那怎麼辦呢?這個死渣男,小氣得很吶,看來又要咱們本身動腦筋咯。
還記得咱們最開始是怎麼找到評論區的包嗎?對,我沒說,我是先將頁面清空,而後請求訪問了第二個頁面,這時候就出現了一個新包,用腳指頭想都知道那就是第二個頁面的包,不過我仍是想用第一個頁面,因而我就切回去了。
那咱們再想一想,這些數據咱們是在哪裏找到的?我不但願看到大家說Preview啊,想清楚啊,想這樣說的朋友,給大家一次從新組織語言的機會。
對,明明就是在Headers的General的url裏面找到的嘛,Preview怎麼爬?對吧。
原本不想多廢話,可是我喜歡分析url,因此就多說兩句唄。
第一個頁面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId= 第二個頁面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=2&size=10&commentId= 第三個頁面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=3&size=10&commentId= 123
一目瞭然了吧,不用我再多放了。
import requests from bs4 import BeautifulSoup import json headers = { 'origin':'https://lion-wu.blog.csdn.net', # 請求來源,本案例中實際上是不須要加這個參數的,只是爲了演示 'referer':'https://lion-wu.blog.csdn.net/article/details/108858689', # 請求來源,攜帶的信息比「origin」更豐富,本案例中實際上是不須要加這個參數的,只是爲了演示 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' # 標記了請求從什麼設備,什麼瀏覽器上發出 } # 假裝請求頭 for i in range(5): res = requests.get('https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page='+str(i)+'&size=10&commentId=',headers=headers) print(res.status_code) soup = BeautifulSoup(res.text,'html.parser') json_items = json.loads(soup.text) items = json_items['data']['list'] for item in items: print(item['info']['content']) print('\n'+'----------'+'\n') 12345678910111213141516171819202122232425
有時候呢,你會發現你抓取的幾個頁面不過是在重複(強行灌輸知識點)
那就灌一下吧。
我也不知道什麼是就要用上,反正先寫上。
因此,其實咱們能夠把Query String Parameters裏的內容,直接複製下來,封裝爲一個字典,傳遞給params。只是有一點要特別注意:要給他們打引號,讓它們變字符串。
因此,代碼最後可能長這樣:
僞代碼
import requests
# 引用requests模塊 url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689' for i in range(5): 'params' = { 'page': str(i) 'size': '10' 'commentId': } # 將參數封裝爲字典 res_comments = requests.get(url,params=params,頭) 123456789101112
好極,好極,這篇就到這裏啦,爽吶。
下一篇會比較輕鬆一些,這篇信息量有點大啊。
最後多說一句,想學習Python可聯繫小編,這裏有我本身整理的整套python學習資料和路線,想要這些資料的均可以進q裙947618024領取。
本文章素材來源於網絡,若有侵權請聯繫刪除。