摘要:并不是所有爬蟲都遵守,一般只有大型搜索引擎爬蟲才會遵守。的端口號為的端口號為工作原理網絡爬蟲抓取過程可以理解為模擬瀏覽器操作的過程。表示服務器成功接收請求并已完成整個處理過程。
爬蟲概念
數據獲取的方式:
企業生產的用戶數據:大型互聯網公司有海量用戶,所以他們積累數據有天然優勢。有數據意識的中小型企業,也開始積累的數據。
數據管理咨詢公司
政府/機構提供的公開數據
第三方數據平臺購買數據
爬蟲爬取數據
什么是爬蟲
抓去網頁數據的程序
網頁三大特征:
每個網頁都有自己的URL
網頁都使用HTML標記語言來描述頁面信息
網頁都使用HTTP/HTTPS協議來傳輸HTML數據
爬蟲的設計思路
確定需要爬取的網頁URL地址
通過HTTP/HTTPS協議來獲取對應的HTML頁面
提取HTML頁面中的數據
如果是需要的數據,就保存起來
如果頁面是其它URL,那就繼續爬取
如何抓取HTML頁面
HTTP協議請求的處理,urllib, urllib2, requests,處理后的請求可以模擬瀏覽器發送請求,獲取服務器響應的文件
解析服務器響應的內容
re, xpath(常用), BeautifulSoup4(bs4), jsonpath, pyquery等使用某種描述性一樣來給需要提取的數據定義一個匹配規則,符合這個規則的數據就會被匹配。
如何采集動態HTML,驗證碼的處理
Selenium(自動化測試工具) + PhantomJS(無界面瀏覽器)
驗證碼處理通過Tesseract: 機器圖像識別系統(圖片中的文本識別)
Scrapy框架
(Scrapy, Pyspider)
高性能高定制型(異步網絡框架twisted),所以數據下載速度快
提供了數據存儲,數據下載,提取規則等組件
分布式策略
是否有那么多的機器去做分布式?
獲取的數據是否值得搭建分布式系統?
使用scrapy-redis來搭建,在Scrapy的基礎上添加了一套 Redis數據庫為核心的一套組件,讓Scrapy框架支持分布式的功能。主要在Redis中做請求指紋去重,請求分配,數據臨時存儲
爬蟲 - 反爬蟲 - 反反爬蟲
反爬蟲: User-Agent, IP, 代理, 驗證碼, 動態數據加載, 加密數據
數據的價值,是否值得去費勁去做反爬蟲,一般做到代理階段或封IP。
機器成本 + 人力成本 > 數據價值
爬蟲和反爬蟲之間的斗爭,最后一定是爬蟲獲勝。
只要是真實用戶可以瀏覽的網頁數據,爬蟲就一定能爬下來。(爬蟲模擬瀏覽器獲取數據)
爬蟲集合awesome-spider
通用爬蟲
搜索引擎使用的爬蟲系統
目標:盡可能把互聯網上所有的網頁下載下來,放到本地服務器里形成備份,再對這些網頁做相關處理(提取關鍵字,去掉廣告),最后提供一個用戶檢索接口
抓取流程:
首先選取一部分已有的URL,把這些URL放到待爬取隊列。
從隊列里去取出這些URL,然后解析DNS得到主機IP,然后去這個IP對應的服務器下載HTML頁面,保存到搜索引擎的本地服務器里,之后把這個已經爬過的URL放入到已經爬取隊列中
分析網頁內容,找出網頁中的其它URL內容,繼續爬取。
搜索引擎如何獲取一個新網站的URL:
主動向搜索引擎提交網址: 百度搜索資源平臺
在其它網站設置外鏈
搜索引擎會和DNS服務商進行合作,可以快速收錄新的網站
通用爬蟲并不是萬物皆可爬,它也需要遵守規則:
Robots協議,協議會指明通用爬蟲可以爬取網頁的權限。
Robots.txt并不是所有爬蟲都遵守,一般只有大型搜索引擎爬蟲才會遵守。
通用爬蟲工作流程:
爬取網頁 -> 存儲數據 -> 內容處理 -> 提供檢索/排名服務
搜索引擎排名:
PageRank值:根據網站的流量(pv),流量越高,排名約靠前
競價排名
通用爬蟲的缺點:
只能提供和文本相關的內容(HTML,Word,PDF)等,但是不能提供多媒體(音樂,視頻,圖片)和二進制文件。
提供的結果千篇一律,不能針對不同領域的人提供不同的搜索結果。
不能理解人類語義上的檢索。
DNS: 把域名解析成IP
聚焦爬蟲
爬蟲程序員寫的針對某種內容爬蟲。(針對通用爬蟲的缺點)
面向主題爬蟲,面向需求爬蟲,會針對某種特定的內容去爬取信息,而且會保證內容信息和需求盡可能相關。
HTTP&HTTPSHTTP協議(HyperText Transfer Protocol,超文本傳輸協議):是一種發布和接收HTML頁面的方法。
HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)簡單講是HTTP的安全版,在HTTP下加入SSL層。
SSL(Secure Sockets Layer 安全套接層)主要用于Web的安全傳輸協議,在傳輸層對網絡連接進行加密,保障在Internet上數據傳輸的安全。
HTTP的端口號為80
HTTPS的端口號為443
HTTP工作原理
網絡爬蟲抓取過程可以理解為模擬瀏覽器操作的過程。
瀏覽器的主要功能是向服務器發出請求,在瀏覽器窗口中展示您選擇的網絡資源,HTTP是一套計算機通過網絡進行通信的規則。
常用的請求報頭:
Host (主機和端口號): 對應網址URL中的Web名稱和端口號,用于指定被請求資源的Internet主機和端口號,通常屬于URL的一部分。
Connection (鏈接類型): 表示客戶端與服務連接類型
Client 發起一個包含 Connection:keep-alive 的請求,HTTP/1.1使用 keep-alive 為默認值。
Server收到請求后:如果 Server 支持 keep-alive,回復一個包含 Connection:keep-alive 的響應,不關閉連接; 如果 Server 不支持keep-alive,回復一個包含 Connection:close 的響應,關閉連接。
如果client收到包含 Connection:keep-alive 的響應,向同一個連接發送下一個請求,直到一方主動關閉連接。
keep-alive在很多情況下能夠重用連接,減少資源消耗,縮短響應時間,比如當瀏覽器需要多個文件時(比如一個HTML文件和相關的圖形文件),不需要每次都去請求建立連接。
Upgrade-Insecure-Requests (升級為HTTPS請求): 升級不安全的請求,意思是會在加載 http 資源時自動替換成 https 請求,讓瀏覽器不再顯示https頁面中的http請求警報。(HTTPS 是以安全為目標的 HTTP 通道,所以在 HTTPS 承載的頁面上不允許出現 HTTP 請求,一旦出現就是提示或報錯。)
User-Agent (瀏覽器名稱): 是客戶瀏覽器的名稱
Accept (傳輸文件類型): 指瀏覽器或其他客戶端可以接受的MIME(Multipurpose Internet Mail Extensions(多用途互聯網郵件擴展))文件類型,服務器可以根據它判斷并返回適當的文件格式。
Accept: */*:表示什么都可以接收。
Accept:image/gif:表明客戶端希望接受GIF圖像格式的資源;
Accept:text/html:表明客戶端希望接受html文本。
Accept: text/html, application/xhtml+xml;q=0.9, image/*;q=0.8:表示瀏覽器支持的 MIME 類型分別是 html文本、xhtml和xml文檔、所有的圖像格式資源。html中文件類型的accept屬性有哪些
Referer (頁面跳轉處): 表明產生請求的網頁來自于哪個URL,用戶是從該 Referer頁面訪問到當前請求的頁面。這個屬性可以用來跟蹤Web請求來自哪個頁面,是從什么網站來的等。有時候遇到下載某網站圖片,需要對應的referer,否則無法下載圖片,那是因為人家做了防盜鏈,原理就是根據referer去判斷是否是本網站的地址,如果不是,則拒絕,如果是,就可以下載;
Accept-Encoding(文件編解碼格式): 指出瀏覽器可以接受的編碼方式。編碼方式不同于文件格式,它是為了壓縮文件并加速文件傳遞速度。瀏覽器在接收到Web響應之后先解碼,然后再檢查文件格式,許多情形下這可以減少大量的下載時間。例如:Accept-Encoding:gzip;q=1.0, identity; q=0.5, *;q=0
Accept-Language(語言種類): 指出瀏覽器可以接受的語言種類,如en或en-us指英語,zh或者zh-cn指中文,當服務器能夠提供一種以上的語言版本時要用到。
Accept-Charset(字符編碼): 指出瀏覽器可以接受的字符編碼。例如:Accept-Charset:iso-8859-1,gb2312,utf-8
Cookie (Cookie): 瀏覽器用這個屬性向服務器發送Cookie。Cookie是在瀏覽器中寄存的小型數據體,它可以記載和服務器相關的用戶信息,也可以用來實現會話功能
Content-Type (POST數據類型): POST請求里用來表示的內容類型。例如:Content-Type = Text/XML; charset=gb2312:
常用的響應報頭(了解):
Cache-Control:must-revalidate, no-cache, private: 告訴客戶端,服務端不希望客戶端緩存資源,在下次請求資源時,必須要從新請求服務器,不能從緩存副本中獲取資源。
Connection:keep-alive: 客戶端服務器的tcp連接也是一個長連接,客戶端可以繼續使用這個tcp連接發送http請求
Content-Encoding:gzip: 告訴客戶端,服務端發送的資源是采用gzip編碼的,客戶端看到這個信息后,應該采用gzip對資源進行解碼。
Content-Type:text/html;charset=UTF-8: 告訴客戶端,資源文件的類型,還有字符編碼,客戶端通過utf-8對資源進行解碼,然后對資源進行html解析。
Date:Sun, 21 Sep 2016 06:18:21 GMT: 服務端發送資源時的服務器時間,GMT是格林尼治所在地的標準時間。http協議中發送的時間都是GMT的,這主要是解決在互聯網上,不同時區在相互請求資源的時候,時間混亂問題。
Expires:Sun, 1 Jan 2000 01:00:00 GMT: 這個響應頭也是跟緩存有關的,告訴客戶端在這個時間前,可以直接訪問緩存副本,很顯然這個值會存在問題,因為客戶端和服務器的時間不一定會都是相同的,如果時間不同就會導致問題。所以這個響應頭是沒有Cache-Control:max-age=*這個響應頭準確的,因為max-age=date中的date是個相對時間.
Pragma:no-cache: 這個含義與Cache-Control等同。
Server:Tengine/1.4.6: 這個是服務器和相對應的版本,只是告訴客戶端服務器的信息。
Transfer-Encoding:chunked: 這個響應頭告訴客戶端,服務器發送的資源的方式是分塊發送的。
響應狀態碼:
100~199:表示服務器成功接收部分請求,要求客戶端繼續提交其余請求才能完成整個處理過程。
200~299:表示服務器成功接收請求并已完成整個處理過程。常用200(OK 請求成功)。
300~399:為完成請求,客戶需進一步細化請求。例如:請求的資源已經移動一個新地址、常用302(所請求的頁面已經臨時轉移至新的url)、307和304(使用緩存資源)。
400~499:客戶端的請求有錯誤,常用404(服務器無法找到被請求的頁面)、403(服務器拒絕訪問,權限不夠)。
500~599:服務器端出現錯誤,常用500(請求未完成。服務器遇到不可預知的情況)。
Cookie 和 Session:
因為服務器和客戶端的交互僅限于請求/響應過程,結束之后便斷開,在下一次請求時,服務器會認為新的客戶端。為了維護他們之間的鏈接,讓服務器知道這是前一個用戶發送的請求,必須在一個地方保存客戶端的信息
Cookie:通過在客戶端 記錄的信息確定用戶的身份。
Session:通過在服務器端 記錄的信息確定用戶的身份。
urllib.request
linux中的py源碼文件位置:
python自帶:vim /usr/lib/python2.7/urllib2.py
pip安裝:vim /usr/local/lib/python3.6/site-packages/django/http/cookie.py
urllib2.urlopen
# -*- coding:utf-8 -*- import urllib.request as urllib2 # 返回類文件對象 response = urllib2.urlopen("http://www.baidu.com/") # urlopen不支持構造 # 服務器返回類文件對象支持python文件對象的操作方法 # read()方法就是讀取文件里面的全部內容,返回字符串 html = response.read() print(html)
Request
# -*- coding:utf-8 -*- import urllib.request as urllib2 ua_headres = { "User_Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36" } # urllib2.Request(url, data, headres) # 通過urllib2.Request()方法構造一個請求對象 requset = urllib2.Request("http://www.baidu.com", headers=ua_headres) # 返回類文件對象, urlopen不支持構造 response = urllib2.urlopen(requset) # 服務器返回類文件對象支持python文件對象的操作方法 # read()方法就是讀取文件里面的全部內容,返回字符串 html = response.read() print(html)
User_Agent,是發送請求必須帶的請求頭
Response響應
response是服務器響應的類文件,除了支持文件操作的方法外,常用的方法也有:
respnse.getcode(), response.geturl(), response.info()
#condig=utf-8 import urllib.request as urllib2 # print(dir(urllib2)) ua_headres = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4620.400 QQBrowser/9.7.13014.400" } request = urllib2.Request("http://www.baidu.com/", headers=ua_headres) response = urllib2.urlopen(request) html = response.read() # 返回HTTP的響應碼,成功返回200 # 4 服務器頁面出錯, 5 服務器問題 print(response.getcode()) # 返回實際數據的url,防止重定向問題 print(response.geturl()) # 返回服務器響應報頭信息 print(response.info()) # print(dir(response))
User-Agent歷史
如果用一個合法的身份去請求別人網站,就是歡迎的,所以就應該給這個代碼加上一個身份,就是所謂的User-Agent頭。
urllib2默認的User-Agent頭為:Python-urllib/x.y(x和y是Python主版本和次版本號,例如 Python-urllib/2.7
Mosaic世界上第一個瀏覽器:美國國家計算機應用中心
Netscape,網景:Netscape(支持框架)
Microsoft微軟:Internet Explorer
第一次瀏覽器大戰:網景公司失敗
Mozilla 基金組織:Firefox 火狐 內核(Gecko內核)(瀏覽器支持內核開始,User-Agent開始逐漸使用)
User-Agent 決定用戶的瀏覽器,為了獲取更好的HTML頁面效果
IE就給自己披著了個Mozilla的外皮
內核:
Mozilla: Firefox (Gecko)
IE: Trident
Opera: Presto
Linux: KHTML (like Gecko)
Apple: Webkit (like KTML)
Google: Chrome (like webkit)
add_header() & get_header()
add_header(): 添加/修改 一個HTTP報頭
get_header(): 獲取一個已有的HTTP報頭值,只能第一個字母大寫,其它的必須小寫
# -*- coding:utf-8 -*- import urllib.request as urllib2 import random url = "http://www.baidu.com/" # 可以是User-Agent列表,也可以是代理列表。 作用:反反爬蟲 ua_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50" ] # 在User-Agent列表里隨機選擇一個User-Agent user_agent = random.choice(ua_list) # 構造一個請求 request = urllib2.Request(url) # add_header()方法,添加/修改 一個HTTP報頭 request.add_header("User-Agent", user_agent) # get_header() 獲取一個已有的HTTP報頭值,只能第一個字母大寫,其它的必須小寫 request.get_header("User-agent") response = urllib2.urlopen(request) html = response.read() print(html)
urllib.urlencode
編碼:
urlencode位置:urllib.parse.urlencode(values)。 其中values所需要編碼的數據,參數只能為字典
解碼:
unquote: urllib.parse.unquote(values)
#conding=utf-8 import urllib.parse test = { "test": "我的" } # 通過urllib.urlencode()方法,將字典鍵值對按URL編碼轉換,從而能被web服務器接受。 enCodeTest = urllib.parse.urlencode(test) # 冒號解析為等號 print(enCodeTest) # test=%E6%88%91%E7%9A%84 # 通過urllib.unquote()方法,把 URL編碼字符串,轉換回原先字符串。 print(urllib.parse.unquote(enCodeTest)) # test=我的
爬取百度貼吧起始頁到結束頁的案例
#conding=utf-8 import urllib.request import urllib.parse def loadPage(url, filename): """ 作用: 根據url發送請求,獲取服務器響應文件 url: 需要爬取的url地址 filename: 處理的文件名 """ print("正在下載" + filename) headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } request = urllib.request.Request(url, headers=headers) return urllib.request.urlopen(request).read() def writePage(html, filenmae): """ 作用:將html內容寫入到本地 html: 服務器響應文件內容 """ print("正在保存" + filenmae) # 文件寫入 with open(filenmae, "w") as f: # with 之后,不需要做文件關閉還有其它上下文處理的操作 等同于 open(), write(), close() f.write(html.decode("utf-8")) print("-" * 30) print("thanks") def tiebaSpider(url, beginPage, endPage): """ 作用: 貼吧爬蟲調度器,負責組合處理 url: 貼吧url的前部分 beginPage: 起始頁 endPage: 結束頁 """ for page in range(beginPage, endPage+1): pn = (page - 1) * 50 filename = "第" + str(page) + "頁.html" fullurl = url + "&pn=" + str(pn) # print(fullurl) html = loadPage(fullurl, filename) writePage(html, filename) # print(html) if __name__ == "__main__": kw = input("請輸入需要爬取的貼吧名:") beginPage = int(input("請輸入起始頁:")) endPage = int(input("請輸入結束頁:")) # https://tieba.baidu.com/f?ie=utf-8&kw=javascirpt&fr=search url = "https://tieba.baidu.com/f?" key = urllib.parse.urlencode({"kw": kw}) fullurl = url + key tiebaSpider(fullurl, beginPage, endPage)
POST請求的模擬
Get和Post請求的區別:
Get請求:查詢參數在QueryString里保存
Post請求:查詢參數在FormData中保存
Post請求:
# -*- coding:utf-8 -*- import urllib.request import urllib.parse url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule" key = input("請輸入查詢翻譯的文字:") # 發送到服務器的表單數據,如果是中文需要轉碼 fromdata = { "i": key, "from": "AUTO", "to": "AUTO", "smartresult": "dict", "client": "fanyideskweb", "salt": "1528127663128", "sign": "c060b56b628f82259225f751c12da59a", "doctype": "json", "version": "2.1", "keyfrom": "fanyi.web", "action": "FY_BY_REALTIME", "typoResult": "false" } data = urllib.parse.urlencode(fromdata).encode("utf-8") headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } request = urllib.request.Request(url, data=data, headers=headers) html = urllib.request.urlopen(request).read().decode() print(html)
獲取AJAX加載的內容
AJAX一般返回的是JSON,直接對AJAX地址進行post或get,就返回JSON數據了。
“作為一名爬蟲工程師,最需要關注的是數據的來源”
# -*- coding:utf-8 -*- import urllib.request import urllib.parse url = "https://movie.douban.com/j/search_subjects?" headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36" } formdata = { "type": "movie", "tag": "熱門", "sort": "recommend", "page_limit": 20, "page_start": 40 } data = urllib.parse.urlencode(formdata).encode("utf-8") request = urllib.request.Request(url, data=data, headers=headers) html = urllib.request.urlopen(request).read().decode() print(html)
處理HTTPS請求 SSL證書驗證
網站的SSL證書是經過CA認證的,則能夠正常訪問
多帶帶處理SSL證書,讓程序忽略SSL證書驗證錯誤
# -*- coding:utf-8 -*- import urllib.request import ssl # 表示忽略未經核實的SSL證書認證 context = ssl._create_unverified_context() url = "https://www.12306.cn/mormhweb/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36" } request = urllib.request.Request(url, headers = headers) # 在urlopen()方法里 指明添加 context 參數 response = urllib.request.urlopen(request, context = context) print(response.read())
CA: 數字證書認證中心的簡稱,是指發放、管理、廢除數字證書的受信任的第三方機構(類似與身份證)
CA的作用: 檢查證書持有者身份的合法性,并簽發證書,以防證書被偽造或篡改,以及對證書和密鑰進行管理
一般正常的網站都會主動出示自己的數字證書,來確保客戶端和網站服務器之間的通信數據是加密安全的.
Handler和Opener的使用
自定義Handler
# -*- coding:utf-8 -*- import urllib.request # 構建一個HTTPHandler處理器對象,支持處理HTTP的請求 # http_hander = urllib.request.HTTPHandler() http_hander = urllib.request.HTTPHandler(debuglevel=1) # 調用build_opener()方法構建一個自定義的opener對象,參數是構建的處理器對象 opener = urllib.request.build_opener(http_hander) req = urllib.request.Request("http://www.baidu.com") res = opener.open(req) print(res.read().decode())
開放代理和私密代理
高匿:無法拿到真正的物理ip,只能獲取代理服務器ip
透明:能看到代理服務器ip,也可以看到物理ip地址
快代理
西刺免費代理
使用代理IP,這是爬蟲/反爬蟲的第二大招,通常也是最好用的。
很多網站會檢測某一段時間某個IP的訪問次數(通過流量統計,系統日志等),如果訪問次數多的不像正常人,它會禁止這個IP的訪問。
可以設置一些代理服務器,每隔一段時間換一個代理,就算IP被禁止,依然可以換個IP繼續爬取。
# -*- coding:utf-8 -*- import urllib.request # 代理開關,是否啟用代理 proxyswitch = True # 公開代理 proxy_ip = { "http": "123.57.217.208:3128" } # 私密代理 授權的賬號密碼 # proxy_ip_auth = { # "http": "user:passwd@ip:prot" # } # 構建一個handler對象,參數是一個字典類型,包括代理類型和代理服務器ip+prot http_proxy_handler = urllib.request.ProxyHandler(proxy_ip) # 構建一個沒有代理對象的處理器對象 null_proxy_headler = urllib.request.ProxyHandler({}) if proxyswitch: opener = urllib.request.build_opener(http_proxy_handler) else: opener = urllib.request.build_opener(null_proxy_headler) # 構建一個全局的opener,之后的所有請求都可以用urlopen()方式發送,也附帶Handler功能 urllib.request.install_opener(opener) request = urllib.request.Request("http://www.baidu.com/") response = urllib.request.urlopen(request) # response = opener.open(request) print(response.read().decode())
ProxyBasicAuthHandler(代理授權驗證):
#conding=utf-8 import urllib.request user = "" passwd = "" proxyserver = "" # 構建一個密碼管理對象,用來保存需要處理的用戶名和密碼 passwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() # 添加賬戶信息,第一個參數realm是與遠程服務器相關的域信息,一般都是寫None,后面三個參數分別是 代理服務器、用戶名、密碼 passwdmgr.add_password(None, proxyserver, user, passwd) # 構建一個代理基礎用戶名/密碼驗證的ProxyBasicAuthHandler處理器對象,參數是創建的密碼管理對象 proxy_auth_handler = urllib.request.ProxyDigestAuthHandler(passwdmgr) # 通過 build_opener()方法使用這些代理Handler對象,創建自定義opener對象,參數包括構建的 proxy_handler 和 proxyauth_handler opener = urllib.request.build_opener(proxy_auth_handler) request = urllib.request.Request("http://www.baidu.com/") response = opener.open(request) print(response.read().decode())Cookie
Cookie 是指某些網站服務器為了辨別用戶身份和進行Session跟蹤,而儲存在用戶瀏覽器上的文本文件,Cookie可以保持登錄信息到用戶下次與服務器的會話。
HTTP是無狀態的面向連接的協議, 為了保持連接狀態, 引入了Cookie機制 Cookie是http消息頭中的一種屬性
Cookie名字(Name) Cookie的值(Value) Cookie的過期時間(Expires/Max-Age) Cookie作用路徑(Path) Cookie所在域名(Domain), 使用Cookie進行安全連接(Secure)。 前兩個參數是Cookie應用的必要條件,另外,還包括Cookie大小(Size,不同瀏覽器對Cookie個數及大小限制是有差異的)。
Cookie由變量名和值組成,根據 Netscape公司的規定,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
在Python處理Cookie,一般是通過cookielib模塊和urllib2模塊的HTTPCookieProcessor處理器類一起使用。
cookielib模塊:主要作用是提供用于存儲cookie的對象
HTTPCookieProcessor處理器:主要作用是處理這些cookie對象,并構建handler對象。
cookielib 庫
該模塊主要的對象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar
一般情況下,只用CookieJar(),如果需要和本地文件交互,就需要使用MozillaCookjar()和LWPCookieJar()
獲取Cookie,并保存到CookieJar()對象中:
#!/usr/local/bin/python import urllib.request import http.cookiejar cookiejar = http.cookiejar.CookieJar() http_handler = urllib.request.HTTPCookieProcessor(cookiejar) opener = urllib.request.build_opener(http_handler) opener.open("http://www.baidu.com") cook_str = "" for item in cookiejar: cook_str = cook_str + item.name + "=" + item.value + ";" print(cook_str[:-1]) # BAIDUID=5DB0FC0C0DC9692BB8EE6EDC93A2EDEA:FG=1;BIDUPSID=5DB0FC0C0DC9692BB8EE6EDC93A2EDEA;H_PS_PSSID=1468_26259_21099_26350_26580;PSTM=1528615563;BDSVRTM=0;BD_HOME=0
訪問網站獲得cookie,并把獲得的cookie保存在cookie文件中:
#!/usr/local/bin/python import http.cookiejar import urllib.request filename = "cookie.txt" # 聲明一個MozillaCookieJar(有save實現)對象實例來保存cookie,之后寫入文件 cookiejar = http.cookiejar.MozillaCookieJar(filename) handler = urllib.request.HTTPCookieProcessor(cookiejar) opener = urllib.request.build_opener(handler) req = opener.open("http://www.baidu.com") # 保存cookie到本地文件 cookiejar.save() print(1)非結構化數據和結構化數據
實際上爬蟲一共就四個主要步驟:
明確目標 (要知道準備在哪個范圍或者網站去搜索)
爬 (將所有的網站的內容全部爬下來)
取 (去掉對沒用處的數據)
處理數據(按照想要的方式存儲和使用)
re模塊
pattern = re.compile(regExp) pattern.match(): 從起始位置開始查找,返回第一個符合規則的,只匹配一次。 pattern.search(): 從任意位置開始查找,返回第一個符合規則的,只匹配一次。 pattern.findall(): 所有的全部匹配,返回列表 pattern.split(): 分割字符串,返回列表 pattern.sub(): 替換
rs.I 忽略大小寫 re.S 全文匹配
match(str, begin, end):
>>> pattern = re.compile(r"([a-z]+) ([a-z]+)", re.I) >>> m = pattern.match("hello world hello python") >>> m.group() "hello world" >>> m.group(1) "hello" >>> m.group(2) "world"
findall(str, begin, end):
>>> pattern = re.compile(r"d+") >>> pattern.findall("hello world 123 456 789") ["123", "456", "789"] >>>
split(str, count):
>>> pattern = re.compile(r"[sd;]+") >>> pattern.split("a ba;m; a ") ["a", "bx07", "m", "a", ""] >>> >>> pattern = re.compile("[sd;]+") >>> pattern.split(r"a ba;m; a ") ["a", "ba", "m", "a", ""] >>>
sub():
>>> pattern = re.compile(r"(w+)(w+)") >>> strs = "hello 123, world 456" >>> pattern.sub("hello world", strs) "hello world hello world, hello world hello world" >>>xpath
chrome插件:XPath Helper
XPath (XML Path Language) 是一門在 XML 文檔中查找信息的語言,可用來在XML文檔中對元素和屬性進行遍歷。
lxml庫:
lxml是 一個HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML數據。
獲取屬性:@src, @title, @class
獲取內容: /text()
模糊查詢: contains(@id, "模糊字符串")
xpath匹配規則:
//div[@class="pic imgcover"]/img/@src
xpath模糊匹配:
//div[contains(@id, "qiushi_tag")]
獲取某個網站的圖片:
#conding=utf-8 import urllib.request import urllib.parse from lxml import etree # 我乃河北,姓氏顏良 class getQdailyImg: def __init__(self, url): self.url = url def loadPage(self): print("正在下載...") headres = { "User-Agent": "ie 的user-Agent" } req = urllib.request.Request(self.url, headers=headres) html = urllib.request.urlopen(req).read().decode() xmlDom = etree.HTML(html) linkList = xmlDom.xpath("http://div[@class="pic imgcover"]/img/@src") print(linkList) self.writePage(linkList) def writePage(self, data): for item in data: with open("img.txt", "w") as f: f.write(item) if __name__ == "__main__": qdI = getQdailyImg("http://www.qdaily.com/") qdI.loadPage()
# -*- coding:utf-8 -*- import urllib.request import json from lxml import etree url = "http://www.qiushibaike.com/8hr/page/1" headers = { "user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)" } req = urllib.request.Request(url, headers=headers) html = urllib.request.urlopen(req).read() text = etree.HTML(html) # 作為根目錄節點 node_list = text.xpath("http://div[contains(@id, "qiushi_tag")]") items = {} for node in node_list: username = node.xpath("./div[@class="author clearfix"]//h2/text()")[0] image = node.xpath(".//div[@class="thumb"]//@src") content = node.xpath(".//div[@class="content"]/span")[0].text zan = node.xpath(".//i")[0].text comment = node.xpath(".//i")[1].text items = { "username": username, "image": image, "content": content, "zan": zan, "comment": comment } with open("qiushi.json", "a") as f: f.write(json.dumps(items, ensure_ascii=False) + " ") print("ok")BeautifulSoup
Beautiful Soup也是一個HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML數據。
BeautifulSoup用來解析HTML比較簡單,API非常人性化,支持CSS選擇器、Python標準庫中的HTML解析器,也支持lxml的 XML解析器。
pip install bs4
beautifulsoup4文檔
tag: BeautifulSoup(html).div
attrs: BeautifulSoup(html).div.name,BeautifulSoup(html).div.attres
content: BeautifulSoup(html).span.string
#conding=utf-8 from bs4 import BeautifulSoup import requests import time def captchaMethod(captcha_data): with open("captcha.jpg", "wb") as f: f.write(captcha_data) return input("請輸入驗證碼:") def getLoginZhihu(): # 構建Session對象,保存cookie值 sess = requests.Session() headers = { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 QQBrowser/4.3.4986.400" } html = sess.post("https://www.zhihu.com/#sigin", headers=headers).text bs = BeautifulSoup(html, "lxml") _xsrf = bs.find("input", attrs={"name": "_xsrf"}).get("value") captcha_url = "https://www.zhihu.com/captcha.gif?r=%d&type=login" % (time.time() * 1000) captcha = sess.get(captcha_url, headers=headers).content # 獲取驗證碼文字 text = captchaMethod(captcha) data = { "_xsrf": _xsrf, "email": "123636374@qq.com", "password": "ALARMCHIME", "captcha": text } # 登錄 獲取cookie res = sess.post("https://www.zhihu.com/login/email", data=data, headers=headers).text res = sess.get("https://www.zhihu.com/people/", headers) if __name__ == "__main__": getLoginZhihu()JSON和JSONPATH
Json和JsonPath應用
json.loads(): 把Json格式字符串解碼轉換成Python對象
json.dumps(): 實現python類型轉化為json字符串,返回一個str對象 把一個Python對象編碼轉換成Json字符串
json.dump(): 將Python內置類型序列化為json對象后寫入文件
json.load(): 讀取文件中json形式的字符串元素 轉化成python類型
dictStr = {"city": "北京", "name": "大貓"} print(json.dumps(dictStr, ensure_ascii=False)) # {"city": "北京", "name": "大劉"} listStr = [{"city": "北京"}, {"name": "大劉"}] json.dump(listStr, open("listStr.json","w"), ensure_ascii=False) strDict = json.load(open("dictStr.json")) print(strDict) # {u"city": u"u5317u4eac", u"name": u"u5927u5218"}
# -*- coding:utf-8 -*- import json import urllib.request import jsonpath headers = { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 QQBrowser/4.3.4986.400" } url = "https://www.lagou.com/lbs/getAllCitySearchLabels.json" request = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(request) html = response.read().decode() unicodeStr = json.loads(html) content = jsonpath.jsonpath(unicodeStr, "$..name") print(content) array = json.dumps(content, ensure_ascii=False) with open("lagoucontent.json", "w") as f: f.write(array)多線程爬蟲
一個進程可能包括多個線程,線程之間執行任務,必須通過加鎖方式控制它們(阻塞)
父線程和子線程都關系,只要父線程執行完,不管子線程如何,都一并結束
計算機的核心是CPU,CPU承擔了所有的計算任務
一個CPU核心一次只能執行一個任務
多個CPU核心同時可以執行多個任務
一個CPU一次只能執行一個進程,其它進程處于非運行
進程里包含的執行單元叫線程
一個進程 可以包含 多個線程
一個進程的內存空間是共享的,每個進程里的線程都可以使用這個共享空間
一個線程在使用這個共享空間的時候,其它線程必須等待它結束
通過“鎖”實現,作用就是防止多個線程使用當前內存空間。
先使用的線程會加鎖,鎖上該空間,其它線程就在等待。
進程:表示程序的一次執行
線程:CPU運算的基本調度單位
GIL: Python里的執行通行證,而且只有唯一個。拿到通行證的線程才會執行
Python 的多線程適用于:大量密集的I/O處理 (多帶帶都任務,一個進程,只能執行一個任務)
Python 的多進程適用于:大量的密集并行計算
#conding=utf-8 import json import threading from queue import Queue import requests from lxml import etree CREAWL_EXIT = False PARSE_EXIT = False """ 采集線程 """ class ThreadCrawl(threading.Thread): def __init__(self, threadName, pageQueue, dataQueue): # threading.Thread.__init__(self) super(ThreadCrawl, self).__init__() # 多個父類,多重繼承 self.threadName = threadName self.pageQueue = pageQueue self.dataQueue = dataQueue self.headers = { "user-agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)" } def run(self): print("start" + self.threadName) while not CREAWL_EXIT: try: page = self.pageQueue.get(False) url = "https://www.qiushibaike.com/8hr/page/" + str(page) + "/" res = requests.get(url, headers=self.headers).text self.dataQueue.put(res) except: pass print("end" + self.threadName) """ 解析線程 """ class ThreadParse(threading.Thread): def __init__(self, threadingName, dataQueue, filename): super(ThreadParse, self).__init__() self.threadingName = threadingName self.dataQueue = dataQueue self.filename = filename def run(self): print("start" + self.threadingName) while not PARSE_EXIT: try: html = self.dataQueue.get(False) self.parse(html) except: pass print("end" + self.threadingName) def parse(self, html): text = etree.HTML(html) node_list = text.xpath("http://div[contains(@id, "qiushi_tag")]") items = {} for node in node_list: username = node.xpath("./div[@class="author clearfix"]//h2/text()")[0] image = node.xpath(".//div[@class="thumb"]//@src") content = node.xpath(".//div[@class="content"]/span")[0].text zan = node.xpath(".//i")[0].text comment = node.xpath(".//i")[1].text items = { "username": username, "image": image, "content": content, "zan": zan, "comment": comment } self.filename.write(json.dumps(items, ensure_ascii=False) + " ") def main(): # 頁碼 pageQueue = Queue(10) # 放入1~10的數字 for i in range(1, 10+1): pageQueue.put(i) # 采集結果(每頁的HTML源碼) dataQueue = Queue() filename = open("duanzi.json", "a") crawlList = ["采集線程1", "采集線程2", "采集線程3"] threadcrawl = [] for threadName in crawlList: thread = ThreadCrawl(threadName, pageQueue, dataQueue) thread.start() threadcrawl.append(thread) parseList = ["解析線程1", "解析線程2", "解析線程3"] threadparse = [] for threadName in parseList: thread = ThreadParse(threadName, dataQueue, filename) thread.start() threadparse.append(thread) # 等待pageQueue隊列為空, 或者 數據隊列為空,也就是等待之前執行的操作執行完畢 while not pageQueue.empty() or not dataQueue.empty(): pass global CREAWL_EXIT CREAWL_EXIT = True print("queue隊列為空") global PARSE_EXIT PARSE_EXIT = True print("data隊列為空") for threadItem in crawlList: threadItem.join("") print("1") if __name__ == "__main__": main()
自動化測試unittest模塊使用和模擬用戶點擊抓取數據(拿去ajax分頁數據)
# -*- coding:utf-8 -*- import unittest from selenium import webdriver from bs4 import BeautifulSoup as bs class Douyu(unittest.TestCase): def setUp(self): self.driver = webdriver.PhantomJS() # unittest測試方法必須有`test`字樣開頭 def testDouyu(self): self.driver.get("https://www.douyu.com/directory/all") while True: soup = bs(self.driver.page_source, "lxml") names = soup.find_all("h3", {"class": "ellipsis"}) viewNums = soup.find_all("span", {"class": "dy-num fr"}) for name, viewNum in zip(names, viewNums): print("房間名" + name.get_text() + "; " + "觀眾人數" + viewNum.get_text()) # 在頁面源碼中找到"下一頁"未隱藏的標簽,就退出循環 if self.driver.page_source.find("shark-pager-disable-next") != -1: break # 一直點擊下一頁 self.driver.find_element_by_class_name("shark-pager-next").click() # 測試結束執行的方法 def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
執行javascript語句:execute_script
#conding=utf-8 from selenium import webdriver import time driver = webdriver.PhantomJS("/Users/linxingzhang/Library/Python/3.6/lib/python/site-packages/selenium/webdriver/phantomjs") driver.get("https://movie.douban.com/typerank?type_name=劇情&type=11&interval_id=100:90&action=") time.sleep(3) # 向下滾動10000像素 js = "document.body.scrollTop=10000" # js="var q=document.documentElement.scrollTop=10000" # 查看頁面快照 driver.save_screenshot("douban.png") # 執行JS語句 driver.execute_script(js) time.sleep(10) # 查看頁面快照 driver.save_screenshot("newdouban.png") driver.quit()
投票
import datetime import sys import threading import time from random import choice # choice() 方法返回一個列表,元組或字符串的隨機項 import requests from lxml import etree from fake_useragent import UserAgent # 引入隨機的UA # 設置user-agent列表,每次請求時,隨機挑選一個user-agent ua_list = UserAgent() def get_ip(): """ 獲取代理ip """ url = "http://www.xicidaili.com/nn" headers = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Host": "www.xicidaili.com", "Referer": "http: // www.xicidaili.com/nn", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400" } ret = requests.get(url, headers=headers) xmlDom = etree.HTML(ret.text) data = xmlDom.xpath("http://table[@id="ip_list"]//tr") z = [] for tr in data: if tr.xpath("td"): ip = tr.xpath("td")[1].text # 獲取所有IP port = tr.xpath("td")[2].text # 獲取所有端口 z.append(ip + ":" + port) return z def get_url(url, code=0, ips=[]): """ 投票 如果因為代理IP已失效造成投票失敗,則會自動換一個代理IP后繼續投票 """ try: ip = choice(ips) print(ip, "ip" * 5) except: return False else: proxies = { "http": ip } headers = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Host": "best.zhaopin.com", "Origin": "https: // best.zhaopin.com", "Referer": "https//best.zhaopin.com/?sid=121128100&site=sou", "User-Agent": ua_list.random } print(ua_list.random, "ua_list" * 5) try: data = {"bestid": "3713", "score": "5,5,5,5,5,5", "source": "best"} # 跳過證書的驗證 verify=False result = requests.post(url, data=data, headers=headers, proxies=proxies) print(result, "result" * 5) except requests.exceptions.ConnectionError: print("ConnectionError") if not ips: print("ip 失效") sys.exit() # 刪除不可用的代理IP if ip in ips: ips.remove(ip) # 重新請求url get_url(url, code=0, ips=[]) else: date = datetime.datetime.now().strftime("%H:%M:%S") # result.text() 投票成功顯示1 失敗顯示0 print("第%s次 [%s] [%s]:投票%s (剩余可用代理IP數:%s)" % (code, date, ip, result.text, len(ips))) def main(): url = "https://best.zhaopin.com/API/ScoreCompany.ashx" # 投票的請求 ips = [] for i in range(6000): if i % 1000 == 0: ips.extend(get_ip()) # print("-" * 100) # print(ips) t = threading.Thread(target=get_url, args=(url, i, ips)) t.start() time.sleep(1) if __name__ == "__main__": main()Tesseract
機器識別中的文字識別
pip install pytesseract
識別圖片中的文字:
#conding=utf-8 import pytesseract from PIL import Image image = Image.open("./mayday.jpg") text = pytesseract.image_to_string(image) print(text)asyncio & aiohttp
通過異步庫aiohttp,asyncio爬取圖片
# -*- coding:utf-8 -*- import asyncio import os import time import aiohttp import requests class Spider(object): def __init__(self): self.num = 1 if "load-img" not in os.listdir("."): os.mkdir("load-img") self.path = os.path.join(os.path.abspath("."), "load-img") os.chdir(self.path) # 進入文件下載路徑 def run(self): start = time.time() for x in range(1, 101): # 爬取100張圖片,更改數值,爬取更多圖片 links = self.__get_img_links(x) tasks = [asyncio.ensure_future(self.__download_img( (link["id"], link["links"]["download"]) )) for link in links] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) # if self.num >= 10: # break end = time.time() print("run %s s" % (end - start)) def __get_img_links(self, page): url = "https://unsplash.com/napi/photos" data = { "page": page, "per_page": 12, "order_by": "latest" } response = requests.get(url, params=data) if response.status_code == 200: return response.json() else: print("request %s" % response.status_code) async def __download_img(self, img): content = await self.__get_content(img[1]) with open(img[0] + ".jpg", "wb") as f: f.write(content) print("load %s page success" % self.num) self.num += 1 async def __get_content(self, link): async with aiohttp.ClientSession() as session: response = await session.get(link) content = await response.read() return content def main(): spider = Spider() spider.run() if __name__ == "__main__": main()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/41772.html
摘要:以下這些項目,你拿來學習學習練練手。當你每個步驟都能做到很優秀的時候,你應該考慮如何組合這四個步驟,使你的爬蟲達到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學習不是一朝一夕的事情,建議多看看一些比較優秀的爬蟲的設計方案,比如說。 (一)如何學習Python 學習Python大致可以分為以下幾個階段: 1.剛上手的時候肯定是先過一遍Python最基本的知識,比如說:變量、數據結構、語法...
摘要:且本小白也親身經歷了整個從小白到爬蟲初入門的過程,因此就斗膽在上開一個欄目,以我的圖片爬蟲全實現過程為例,以期用更簡單清晰詳盡的方式來幫助更多小白應對更大多數的爬蟲實際問題。 前言: 一個月前,博主在學過python(一年前)、會一點網絡(能按F12)的情況下,憑著熱血和興趣,開始了pyth...
摘要:這篇文章的題目有點大,但這并不是說我自覺對爬蟲這塊有多大見解,我只不過是想將自己的一些經驗付諸于筆,對于如何寫一個爬蟲框架,我想一步一步地結合具體代碼來講述如何從零開始編寫一個自己的爬蟲框架年到如今,我花精力比較多的一個開源項目算是了,這是 showImg(https://segmentfault.com/img/remote/1460000018513379); 這篇文章的題目有點大...
?????? ???Hello,大家好我叫是Dream呀,一個有趣的Python博主,小白一枚,多多關照??? ???CSDN Python領域新星創作者,大二在讀,歡迎大家找我合作學習 ?入門須知:這片樂園從不缺乏天才,努力才是你的最終入場券!??? ?最后,愿我們都能在看不到的地方閃閃發光,一起加油進步??? ???一萬次悲傷,依然會有Dream,我一直在最溫暖的地方等你,唱的就是我!哈哈哈~...
摘要:爬蟲架構架構組成管理器管理待爬取的集合和已爬取的集合,傳送待爬取的給網頁下載器。網頁下載器爬取對應的網頁,存儲成字符串,傳送給網頁解析器。從文檔中獲取所有文字內容正則匹配后記爬蟲基礎知識,至此足夠,接下來,在實戰中學習更高級的知識。 前言 Python非常適合用來開發網頁爬蟲,理由如下:1、抓取網頁本身的接口相比與其他靜態編程語言,如java,c#,c++,python抓取網頁文檔的接...
摘要:爬取幣世界標紅快訊內容移動版引入依賴寫你自己的數據庫地址需要自己安裝客戶端數據庫表名偽造成手機寫你自己的文件地址插入了一條新數據無新數據產生寫你自己的文件地址時間不一致宕機使用當前系統時間進行爬取時間一致正常運行主要要求掌握內容語法 爬取幣世界標紅快訊內容(移動版) # 引入依賴 from lxml import etree import requests import pymongo...
閱讀 1090·2021-10-08 10:04
閱讀 3528·2021-08-05 10:01
閱讀 2285·2019-08-30 11:04
閱讀 1804·2019-08-29 15:29
閱讀 852·2019-08-29 15:12
閱讀 1677·2019-08-26 12:11
閱讀 3125·2019-08-26 11:33
閱讀 1170·2019-08-26 10:23