摘要:一個對應相應的狀態碼,狀態碼表示協議所返回的響應的狀態。下面將狀態碼歸結如下繼續客戶端應當繼續發送請求。繼續處理由擴展的狀態碼,代表處理將被繼續執行。處理方式丟棄該狀態碼不被的應用程序直接使用,只是作為類型回應的默認解釋。
本篇內容為 python 網絡爬蟲初級操作,內容主要有以下 3 部分:
python 關于爬蟲的一些基本操作和知識
靜態網頁抓取
動態網頁抓取
通過下面介紹的網絡爬蟲的流程,我們可以看到包含的知識點有哪些:
獲取網頁——爬取到了整個頁面
Rquest, urllib, selenium
多進程多線程抓取、登錄抓取、突破IP封禁和服務器抓取
解析網頁(提取數據)——從頁面中找自己需要的數據
Re 正則表達式,BeautifulSoup 和 lxml
解決中文亂碼
存儲數據
存入txt文件和csv文件
存入 MySQL 數據庫和 MongoDB 數據庫
我們先來看一個簡單的爬蟲,代碼及注釋如下:
import requests from bs4 import BeautifulSoup link = "http://www.santostang.com/" headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"} #用request的headers偽裝成瀏覽器訪問 r = requests.get(link, headers=headers) # r是requests的Response回復對象 #print(r.text) # 獲取了博客首頁的HTML代碼 # 把HTML代碼轉化為soup對象 soup = BeautifulSoup(r.text, "lxml") #使用BeautifulSoup解析這段代碼 title = soup.find("h1", class_="post-title").a.text.strip() print(title) # 從整個網頁中提取第一篇文章的標題 with open("title.txt", "a+") as f: f.write(title) f.close()
這樣就完成了一次爬蟲,下面我們分為靜態網頁和動態網頁兩部分來進行具體的學習。
靜態網頁抓取響應狀態碼:
返回200,表示請求成功。 返回4XX,表示客戶端錯誤。 返回5XX,表示服務器錯誤響應。
一個 HTTPError 對應相應的狀態碼,HTTP 狀態碼表示 HTTP 協議所返回的響應的狀態。
下面將狀態碼歸結如下:
100:繼續 ?客戶端應當繼續發送請求。客戶端應當繼續發送請求的剩余部分,或者如果請求已經完成,忽略這個響應。 101: 轉換協議 ?在發送完這個響應最后的空行后,服務器將會切換到在Upgrade 消息頭中定義的那些協議。只有在切換新的協議更有好處的時候才應該采取類似措施。 102:繼續處理 ? 由WebDAV(RFC 2518)擴展的狀態碼,代表處理將被繼續執行。 200:請求成功 ? ? ?處理方式:獲得響應的內容,進行處理 201:請求完成,結果是創建了新資源。新創建資源的URI可在響應的實體中得到 ? ?處理方式:爬蟲中不會遇到 202:請求被接受,但處理尚未完成 ? ?處理方式:阻塞等待 204:服務器端已經實現了請求,但是沒有返回新的信 息。如果客戶是用戶代理,則無須為此更新自身的文檔視圖。 ? ?處理方式:丟棄 300:該狀態碼不被HTTP/1.0的應用程序直接使用, 只是作為3XX類型回應的默認解釋。存在多個可用的被請求資源。?? ?處理方式:若程序中能夠處理,則進行進一步處理,如果程序中不能處理,則丟棄 301:請求到的資源都會分配一個永久的URL,這樣就可以在將來通過該URL來訪問此資源?? ?處理方式:重定向到分配的URL 302:請求到的資源在一個不同的URL處臨時保存 ?? ?處理方式:重定向到臨時的URL 304:請求的資源未更新 ?? ?處理方式:丟棄 400:非法請求 ?? ?處理方式:丟棄 401:未授權 ?? ?處理方式:丟棄 403:禁止 ?? ?處理方式:丟棄 404:沒有找到 ?? ?處理方式:丟棄 500:服務器內部錯誤 ?服務器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。一般來說,這個問題都會在服務器端的源代碼出現錯誤時出現。 501:服務器無法識別 ?服務器不支持當前請求所需要的某個功能。當服務器無法識別請求的方法,并且無法支持其對任何資源的請求。 502:錯誤網關 ?作為網關或者代理工作的服務器嘗試執行請求時,從上游服務器接收到無效的響應。 503:服務出錯 ? 由于臨時的服務器維護或者過載,服務器當前無法處理請求。這個狀況是臨時的,并且將在一段時間以后恢復。
HTTPError 實例產生后會有一個 code 屬性,這就是是服務器發送的相關錯誤號。
3 開頭的代號可以被處理,并且 100-299 范圍的號碼指示成功,所以你只能看到 400-599 的錯誤號碼。
HTTPError 的父類是 URLError,根據編程經驗,父類的異常應當寫到子類異常的后面,如果子類捕獲不到,那么可以捕獲父類的異常。
傳遞URL參數:
為了請求特定的數據,在URL的查詢字符串中加入某些數據。如果是自己構建 URL 數據一般會跟在一個問號后面,以鍵/值的形式放在URL中。
在Request 中,可以直接把這些參數保存在字典中,用 params 構建至URL中。
舉例如下:
key_dict={"key1":"value1","key2":"value2"}
r=requests.get("http://httpbin.org/get",params=key_dict)
運行,得到結果:URL 已正確編碼為http://httpbin.org/get?key1=v...
定制請求頭:
請求頭 Headers 提供了關于請求、響應或其他發送實體的信息。如果沒有指定請求頭 或請求的請求頭和實際網頁不一致,就可能無法返回正確的結果。
Requests 并不會基于定制的請求 頭Headers 的具體情況改變自己的行為,只是在最后的請求中,所有的請求頭信息都會被傳遞進去。
發送POST請求:
除發送GET請求外,有時發送一些編碼為表單形式的數據(如在登錄的時候請求就為POST)
——如果用GET請求,密碼就會顯示在URL中,不安全
——如果要實現POST請求,只需要傳遞一個字典給 Requests中的 data參數,這個數據字典就會在發出請求時自動編碼為表單形式。
超時:
用 Requests 在 timeout 參數設定的秒數結束之后停止等待響應。一般把這個值設置為 20 秒。
相應代碼如下:
import requests #傳遞URL參數 r = requests.get("http://www.santostang.com/") #返回一個名為 r 的 response對象 print("文本編碼:", r.encoding) print("響應狀態碼:", r.status_code) print("字符串方式的響應體:", r.text) key_dict = {"key1": "value1", "key2": "value2"} r = requests.get("http://httpbin.org/get", params=key_dict) print("URL 已正確編碼:", r.url) print("字符串方式的響應體: ", r.text) #定制請求頭 headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", "Host":"www.santostang.com" } r = requests.get("http://www.santostang.com", headers=headers) print("響應狀態碼:", r.status_code) key_dict = {"key1": "value1", "key2": "value2"} r = requests.post("http://httpbin.org/post", data=key_dict) print(r.text) #from變量的值為key_dict輸入的值,一個POST請求發送成功 link = "http://www.santostang.com/" r = requests.get(link, timeout=0.01) #異常:時間限制0.01秒內,連接到地址為www.santostang.com的時間已到。動態網頁抓取
在使用 JavaScript 時,很多內容并不會出現在 HTML 源代碼中,爬取靜態網頁的技術可能無法正常使用。用到以下兩種技術:
通過瀏覽器審查元素解析真實網頁地址
使用selenium模擬瀏覽器的方法
異步更新技術——AJAX(Asynchronous JavaScript And XML):
通過在后臺與服務器進行少量的數據交換就可以使網頁實現異步更新。即 可以在不重新加載網頁的情況下對網頁的某部分進行更新。
有時候我們在用requests抓取頁面的時候,得到的結果可能和在瀏覽器中看到的不一樣:在瀏覽器中可以看到正常顯示的頁面數據,但是使用requests得到的結果并沒有。
這是因為requests獲取的都是原始的HTML文檔,而瀏覽器中的頁面則是經過JavaScript處理數據后生成的結果,這些數據的來源有多種:
通過 Ajax 加載的,可能是包含在 HTML 文檔中的
數據加載是一種異步加載方式,原始的頁面最初不會包含某些數據,原始頁面加載完后,會再向服務器請求某個接口獲取數據,然后數據才被處理從而呈現到網頁上,這其實就是發送了一個 Ajax 請求。
如果遇到這樣的頁面,直接利用 requests 等庫來抓取原始頁面,是無法獲取到有效數據的,這時需要分析網頁后臺向接口發送的 Ajax 請求,如果可以用 requests 來模擬 Ajax 請求,那么就可以成功抓取了。
經過JavaScript和特定算法計算后生成的。
數據傳送分為 POST 和 GET 兩種方式:
最重要的區別是 GET 方式是直接以鏈接形式訪問,鏈接中包含了所有的參數,當然如果包含了密碼的話是一種不安全的選擇,不過你可以直觀地看到自己提交了什么內容。
POST則不會在網址上顯示所有的參數,不過如果你想直接查看提交了什么就不太方便了。
通過 selenium 模擬瀏覽器抓取
對于那些很難找到真實地址的 URL 或者很冗長的,使用瀏覽器渲染引擎:直接用瀏覽器在顯示網頁時解析HTML、應用CSS樣式、執行JavaScript的語句。
在爬蟲過程中,會打開一個瀏覽器加載該網頁,自動操作瀏覽器瀏覽各個網頁,順便把數據抓下來。
用 Selenium 控制瀏覽器加載的內容,從而加快 Selenium 的爬取速度:
控制 CSS 的加載
控制圖片文件的顯示
控制JavaScript的運行
學習完動態網頁加載后,我們來看一個使用 Ajax 加載評論的網頁,如何爬取到評論,代碼如下:
import json import requests def single_page_comment(link): #link = "https://api-zero.livere.com/v1/comments/list?callback=jQuery1124030126086598094437_1529672300768&limit=10&repSeq=3871836&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&_=1529672300770" headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"} r = requests.get(link, headers=headers) #它是json數據 #print(r.text) json_string = r.text json_string = json_string[json_string.find("{"):-2] #僅僅提取字符串中符合json格式的部分 #使用 json.loads 把字符串格式的響應體數據轉化為json數據 json_data = json.loads(json_string) #利用 json 數據的結構,提取到評論的列表comment_list comment_list = json_data["results"]["parents"] for eachone in comment_list: message = eachone["content"] print(message) # 截止測試時共69條評論 for page in range(1,7): link1 = "https://api-zero.livere.com/v1/comments/list?callback=jQuery1124030126086598094437_1529672300768&limit=10&offset=" link2 = "&repSeq=3871836&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&_=1529672300773" page_str = str(page) link = link1 + page_str + link2 print(link) single_page_comment(link)
最后我們看一個豆瓣電影 TOP250 的代碼(源代碼可至文末下載):
import requests from bs4 import BeautifulSoup def get_movies(): headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", "Host": "movie.douban.com" } for i in range(0, 10): link = "https://movie.douban.com/top250?start=" + str(i * 25) r = requests.get(link, headers=headers, timeout=10) print(str(i+1), "頁響應狀態碼:", r.status_code) print(r.text) # 得到網頁的HTML代碼 # get_movies() movie_list = [] def get_movies(): headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", "Host": "movie.douban.com" } for i in range(0, 10): link = "https://movie.douban.com/top250?start=" + str(i * 25) r = requests.get(link, headers=headers, timeout=10) print(str(i+1), "頁響應狀態碼:", r.status_code) soup = BeautifulSoup(r.text, "lxml") div_list = soup.find_all("div", class_="hd") for each in div_list: movie = each.a.span.text.strip() movie_list.append(movie) return movie_list movies = get_movies() print(movie_list)
得到結果:
["肖申克的救贖", "霸王別姬", "這個殺手不太冷", "阿甘正傳", "美麗人生", "千與千尋", "泰坦尼克號", "辛德勒的名單", "盜夢空間", "機器人總動員", "三傻大鬧寶萊塢", "海上鋼琴師", "忠犬八公的故事", "放牛班的春天", "大話西游之大圣娶親", "楚門的世界", "龍貓", "教父", "星際穿越", "熔爐", "觸不可及", "亂世佳人", "無間道", "當幸福來敲門", "天堂電影院", "怦然心動", "十二怒漢", "少年派的奇幻漂流", "蝙蝠俠:黑暗騎士", "鬼子來了", "搏擊俱樂部", "活著", "指環王3:王者無敵", "瘋狂動物城", "天空之城", "大話西游之月光寶盒", "羅馬假日", "飛屋環游記", "控方證人", "竊聽風暴", "兩桿大煙槍", "飛越瘋人院", "聞香識女人", "哈爾的移動城堡", "辯護人", "海豚灣", "V字仇殺隊", "死亡詩社", "教父2", "美麗心靈", "指環王2:雙塔奇兵", "指環王1:魔戒再現", "飲食男女", "情書", "摔跤吧!爸爸", "美國往事", "獅子王", "鋼琴家", "素媛", "天使愛美麗", "小鞋子", "七宗罪", "被嫌棄的松子的一生", "致命魔術", "音樂之聲", "本杰明·巴頓奇事", "勇敢的心", "西西里的美麗傳說", "剪刀手愛德華", "低俗小說", "看不見的客人", "黑客帝國", "拯救大兵瑞恩", "沉默的羔羊", "讓子彈飛", "入殮師", "蝴蝶效應", "春光乍泄", "大鬧天宮", "瑪麗和馬克思", "末代皇帝", "心靈捕手", "陽光燦爛的日子", "幽靈公主", "第六感", "重慶森林", "布達佩斯大飯店", "禁閉島", "哈利·波特與魔法石", "狩獵", "大魚", "貓鼠游戲", "致命ID", "斷背山", "射雕英雄傳之東成西就", "甜蜜蜜", "一一", "告白", "陽光姐妹淘", "加勒比海盜", "穿條紋睡衣的男孩", "上帝之城", "摩登時代", "阿凡達", "愛在黎明破曉前", "消失的愛人", "風之谷", "愛在日落黃昏時", "側耳傾聽", "超脫", "紅辣椒", "恐怖直播", "倩女幽魂", "小森林 夏秋篇", "馴龍高手", "菊次郎的夏天", "喜劇之王", "幸福終點站", "螢火蟲之墓", "借東西的小人阿莉埃蒂", "七武士", "歲月神偷", "殺人回憶", "神偷奶爸", "電鋸驚魂", "貧民窟的百萬富翁", "喜宴", "真愛至上", "海洋", "諜影重重3", "東邪西毒", "記憶碎片", "怪獸電力公司", "雨人", "黑天鵝", "瘋狂原始人", "盧旺達飯店", "燃情歲月", "英雄本色", "虎口脫險", "小森林 冬春篇", "7號房的禮物", "哈利·波特與死亡圣器(下)", "傲慢與偏見", "荒蠻故事", "心迷宮", "螢火之森", "戀戀筆記本", "完美的世界", "教父3", "縱橫四海", "花樣年華", "海邊的曼徹斯特", "玩具總動員3", "猜火車", "魂斷藍橋", "穿越時空的少女", "雨中曲", "唐伯虎點秋香", "時空戀旅人", "超能陸戰隊", "請以你的名字呼喚我", "蝙蝠俠:黑暗騎士崛起", "二十二", "我是山姆", "人工智能", "冰川時代", "浪潮", "朗讀者", "香水", "爆裂鼓手", "羅生門", "未麻的部屋", "一次別離", "追隨", "血戰鋼鋸嶺", "撞車", "可可西里", "恐怖游輪", "戰爭之王", "被解救的姜戈", "地球上的星星", "達拉斯買家俱樂部", "夢之安魂曲", "牯嶺街少年殺人事件", "房間", "頭腦特工隊", "魔女宅急便", "諜影重重", "諜影重重2", "驚魂記", "小蘿莉的猴神大叔", "忠犬八公物語", "青蛇", "再次出發之紐約遇見你", "阿飛正傳", "模仿游戲", "東京物語", "哪吒鬧海", "碧海藍天", "一個叫歐維的男人決定去死", "完美陌生人", "你的名字。", "無人知曉", "末路狂花", "秒速5厘米", "源代碼", "終結者2:審判日", "黑客帝國3:矩陣革命", "新龍門客棧", "綠里奇跡", "海盜電臺", "這個男人來自地球", "勇闖奪命島", "城市之光", "卡薩布蘭卡", "變臉", "無恥混蛋", "初戀這件小事", "發條橙", "E.T. 外星人", "黃金三鏢客", "愛在午夜降臨前", "荒野生存", "美國麗人", "遷徙的鳥", "英國病人", "無敵破壞王", "步履不停", "瘋狂的石頭", "燕尾蝶", "非常嫌疑犯", "勇士", "彗星來的那一夜", "叫我第一名", "國王的演講", "穆赫蘭道", "血鉆", "聚焦", "海街日記", "上帝也瘋狂", "槍火", "我愛你", "遺愿清單", "大衛·戈爾的一生", "黑鷹墜落", "荒島余生", "藍色大門", "千鈞一發", "愛·回家"]
本文為崔慶才博客和唐松的《Python網絡爬蟲從入門到實踐》學習記錄與總結,具體內容可參考二者。博主在學習過程中的練習代碼也已上傳至 GitHub。
不足之處,歡迎指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41925.html
摘要:按照同樣的方式扒取所有的自己的用戶。但是為了尊重人家隱私權,好吧,就這樣吧,我也不能做啥。 這幾天我很焦慮,受不了那些先follow我,等我follow回去后又unfollow的人,因為是他們先follow我的,我出于人道主義想著互粉一下的,結果這么對我,太不厚道了。github又不像微博那樣有互粉標志,這真的整得我很心煩,于是想著寫一個爬蟲,把這些壞人揪出來~第一步,當然是放出代碼啦...
摘要:同源策略是什么同源策略是瀏覽器的一個安全功能,不同源的數據禁止訪問。或許你可以說驗證,在瀏覽器沒有同源策略的情況下這些都可以繞過去。總結同源策略是蠻好的,防御了大部分的攻擊。 前端最基礎的就是 HTML+CSS+Javascript。掌握了這三門技術就算入門,但也僅僅是入門,現在前端開發的定義已經遠遠不止這些。前端小課堂(HTML/CSS/JS),本著提升技術水平,打牢基礎知識的中心思...
閱讀 2448·2021-10-14 09:42
閱讀 1138·2021-09-22 15:09
閱讀 3545·2021-09-09 09:33
閱讀 3026·2021-09-07 09:59
閱讀 3639·2021-09-03 10:34
閱讀 3532·2021-07-26 22:01
閱讀 2822·2019-08-30 13:06
閱讀 1203·2019-08-30 10:48