一、什么是爬蟲
1、爬蟲Spider的概念
爬蟲用於爬取數據,又稱之為數據采集程序。
爬取的數據來源於網絡,網絡中的數據可以是由WEB服務器(Nginx/Apache),數據庫服務器(MySQL、Redis),索引庫(ElastichSearch),大數據(Hbase/Hive),視頻/圖片庫(FTP),雲存儲(OSS)等提供。
爬取的數據是公開的,非盈利的。
2、Python爬蟲
使用Python編寫的爬蟲腳本(程序)可以完成定時,定量,指定目標(web站點)的數據爬取,主要使用多(單)線程/進程、網絡請求庫、數據解析、數據存儲、任務調度等相關技術。
Python爬蟲工程師,可以完成接口測試,功能性測試,性能測試,集成測試。
二、爬蟲與WEB后端服務之間的關系
爬蟲使用網絡請求庫,相當於客戶端請求,Web后端服務根據請求響應數據。
爬蟲即向Web服務器發起HTTP請求,正確的接收響應數據,然后根據數據的類型(Content-Type)進行數據的解析及存儲。
爬蟲程序在發起請求前,需要偽造一個瀏覽器(User-Agent指定頭),然后再向服務器發起請求,響應200的成功率高很多。
三、Python爬蟲技術的相關庫
1、網絡請求
- urllib
- requests
- selenium(UI自動測試,動態js渲染)
- appium(手機APP的爬蟲或UI測試)
2、數據解析
- re正則
- xpath
- bs4
- json
3、數據存儲
- pymysql
- mongodb
- elasticsearch
4、多任務
- 多線程(threading)、線程隊列 queue
- 協程(asynio、gevent/eventlet)
5、爬蟲框架
- scrapy
- scrapy-redis分布式(多機爬蟲)
四、常見反爬蟲的策略
- UA(User-Agent)策略
- 登錄限制(Cookie)策略
- 請求頻次(IP代理)策略
- 驗證碼(圖片-雲打碼,點觸驗證、滑塊)策略
- 動態js策略(Selenium/Splash/api接口)策略
五、爬蟲庫urllib
1、urllib.request模塊
1.1、簡單請求
from urllib.request import urlopen # 發起網絡請求 resp = urlopen("http://www.hao123.com") assert resp.code == 200 print("請求成功") # 保存請求的網頁
# f變量接收open()函數返回對象的__enter__()返回的就結果
with open("hao123.html", "wb") as f: f.write(resp.read())
urlopen(url, data=None)可以直接發起url請求,如果data不為空時,默認是POST請求,反之為GET請求。
resp是http.client.HTTPResponse類對象
1.2、帶請求頭的請求
""" 初次使用urllib實現爬蟲的數據請求 urllib.request.urlopen(url) 發起get請求 urllib.parse.quote() 講中文進行url編碼 urllib.request.urlretrieve(url, filename) 下載url保存到filename """ from urllib.request import urlopen, urlretrieve, Request from urllib.parse import quote import ssl ssl._create_default_https_context = ssl._create_unverified_context def search_baidu(wd='李志'): # 網絡請求資源接口(url) url = 'https://www.baidu.com/s?wd=%s' # 生成請求對象,封裝請求的url和頭header request = Request(url % quote(wd), headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36', 'Cookie': 'PSTM=1577115106; BAIDUID=94178A0C4E70392F8094399EE47D06E8:FG=1; BIDUPSID=66B8977AF3BB990218FACD41E237E148; BDUSS=o2amI1SjlZQUZHOTJiTzZoRWVwN051Z2pOSm5hRm12OGd6YUx4VFRaUmtvbkZlRVFBQUFBJCQAAAAAAAAAAAEAAAAi-ccWaHVpeWljaGFubWlhbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQVSl5kFUpeMX; BD_UPN=12314753; BD_HOME=1; H_PS_PSSID=30974_1429_21108_30997_30824_30717; delPer=0; BD_CK_SAM=1; PSINO=6; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; COOKIE_SESSION=93270_0_5_4_10_6_0_0_4_3_0_3_93360_0_4_0_1583674230_0_1583674226%7C9%230_2_1577625718%7C1; sug=3; sugstore=0; ORIGIN=0; bdime=0; H_PS_645EC=ee56%2Bh2%2FYYj4EN%2FXrG%2FdO5beNAv52Z9Yz4fZg21AWgMroxtYWtP17XRR1VM' }) response = urlopen(request) assert response.code == 200 print("請求成功") # 讀取響應的數據 bytes_ = response.read() with open('%s.html' % wd, "wb") as file: file.write(bytes_)
2、urllib.parse模塊
此模塊有兩個核心函數:
quote()僅對中文字符穿進行url編碼
urlencode()可以針對一個字典中所有的values進行編碼,然后轉成key=value&key=value字符串。
from urllib.parse import quote from urllib.parse import urlencode quote("李志") '%E6%9D%8E%E5%BF%97' urlencode({"姓名":"李志"}) '%E5%A7%93%E5%90%8D=%E6%9D%8E%E5%BF%97'
六、百度翻譯的實現
''' 應用:百度翻譯 urllib.request.Request urllib.request.urlopen() urllib.parse.urlencode() 發起post請求 ''' import json from urllib.parse import urlencode from urllib.request import Request, urlopen url = 'https://fanyi.baidu.com/sug' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36', 'x-requested-with': 'XMLHttpRequest', 'cookie': 'PSTM=1577115106; BAIDUID=94178A0C4E70392F8094399EE47D06E8:FG=1; ' 'BIDUPSID=66B8977AF3BB990218FACD41E237E148; BDUSS=o2amI1SjlZQUZHOTJiTzZo' 'RWVwN051Z2pOSm5hRm12OGd6YUx4VFRaUmtvbkZlRVFBQUFBJCQAAAAAAAAAAAEAAAAi-ccWaHVpeWl' 'jaGFubWlhbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQVSl5kFUpeMX;' ' to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22valu' 'e%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; HISTORY_SWITCH=1;' ' FANYI_WORD_SWITCH=1; SOUND_PREFER_SWITCH=1; SOUND_SPD_SWITCH=1; delPer=0; PSINO=6; ' 'BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; ' 'BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; H_PS_PSSID=30974_1429_21108_30997_30824; ' 'Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1581948391,1583753885; ' 'from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22' '%7D%2C%7B%22value%22%3A%22dan%22%2C%22text%22%3A%22%u4E39%u9EA6%u8BED%22%7D%2C%7B%2' '2value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; Hm_lpvt_64ecd82404c51e03dc91cb' '9e8c025574=1583753974; __yjsv5_shitong=1.0_7_f938cb5bed75810c7f54daa26e4d74d416c7_300_1583753976124_' '219.145.32.239_2b22827f;yjs_js_security_passport=afa2db6dc7814c5d0875abbf087495d6b1b9b20f_1583753976_js' } def fanyi(kw): data = { 'kw': kw } # Request()中的data參數是byte類型 # data不為空時,就是post請求 req = Request(url, data=urlencode(data).encode("utf-8")) resp = urlopen(req) assert resp.code == 200 json_data = resp.read() # byte content_encode = resp.getheader("Content-Type") content_encode = "utf-8" if content_encode is None else content_encode.split("=")[-1] return json.loads(json_data.decode(content_encode)) # loads函數將json字串變為字典 if __name__ == '__main__': print(fanyi("orange"))
七、GET請求多個頁面
""" 復雜的get請求,多頁面請求下載 """ import random import time from urllib.request import Request, urlopen from urllib.parse import urlencode url = "https://www.baidu.com/s?" params = { 'wd': '', 'pn': 0 # 0, 10, 20, 30...= (n-1)*10 } headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36', 'x-requested-with': 'XMLHttpRequest', 'cookie': 'PSTM=1577115106; BAIDUID=94178A0C4E70392F8094399EE47D06E8:FG=1; ' 'BIDUPSID=66B8977AF3BB990218FACD41E237E148; BDUSS=o2amI1SjlZQUZHOTJiTzZo' 'RWVwN051Z2pOSm5hRm12OGd6YUx4VFRaUmtvbkZlRVFBQUFBJCQAAAAAAAAAAAEAAAAi-ccWaHVpeWl' 'jaGFubWlhbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQVSl5kFUpeMX;' ' to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22valu' 'e%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; HISTORY_SWITCH=1;' ' FANYI_WORD_SWITCH=1; SOUND_PREFER_SWITCH=1; SOUND_SPD_SWITCH=1; delPer=0; PSINO=6; ' 'BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; ' 'BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; H_PS_PSSID=30974_1429_21108_30997_30824; ' 'Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1581948391,1583753885; ' 'from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22' '%7D%2C%7B%22value%22%3A%22dan%22%2C%22text%22%3A%22%u4E39%u9EA6%u8BED%22%7D%2C%7B%2' '2value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; Hm_lpvt_64ecd82404c51e03dc91cb' '9e8c025574=1583753974; __yjsv5_shitong=1.0_7_f938cb5bed75810c7f54daa26e4d74d416c7_300_1583753976124_' '219.145.32.239_2b22827f;yjs_js_security_passport=afa2db6dc7814c5d0875abbf087495d6b1b9b20f_1583753976_js' } def pages_get(wd): params['wd'] = wd for page in range(1, 101): params['pn'] = (page-1) * 10 page_url = url + urlencode(params) resp = urlopen(Request(page_url, headers=headers)) assert resp.code == 200 filename = "baidu_pages/%s-%s.html" % (wd, page) time.sleep(random.random()) with open(filename, 'wb') as f: bytes_ = resp.read() f.write(bytes_) print('下載%s頁成功' % page) if __name__ == '__main__': pages_get("李志")
八、Handler處理器
request對象不能攜帶cookie,也不能使用代理,所以引入了Handler處理器、自定義Opener。
1、步驟
創建Handler對象
handler = urllib.request.HTTPHandler()
創建opener對象
opener= urllib.request.build_opener(handler)
創建Request對象
request = urllib.request.Request(url=url,headers=headers
發送Reques請求
response = opener.open(request)
九、cookie庫
1、cookie庫能干啥?
自動幫我們保存登陸的cookie信息
2、cookie庫配置
創建一個CookieJar對象
from http.cookiejar import CookieJar cookie = cookiejar.CookieJar()
使用cookiejar對象,創建一個handler對象
handler = urllib.request.HTTPCookieProcessor(cookie)
使用handler創建一個opener
opener = urllib.request.build_opener(handler)
通過opener登錄
handler會自動的保存登錄之后的cookie
import urllib.request import http.cookiejar as cookiejar post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=20182122180' data = { 'rkey':'1c7df63368df7ce73c234de26178ec11', 'password':'19870115', 'origURL':'http://www.renren.com/home', 'key_id':'1', 'icode':'', 'f':'http://www.renren.com/224549540', 'email':'dqsygcz@126.com', 'domain':'renren.com', 'captcha_type':'web_login', } headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36', 'Referer': 'http://www.renren.com/SysHome.do' } # 轉換字節 data = urllib.parse.urlencode(data).encode('utf-8') # 定制請求對象 request = urllib.request.Request(url=post_url, headers=headers, data=data) # 創建cookie對象 cookie = cookiejar.CookieJar() # 創建handler對象 handler = urllib.request.HTTPCookieProcessor(cookie) # 創建opener對象 opener = urllib.request.build_opener(handler) # 使用opener對象發送請求 response = opener.open(request) print(response.getcode())
十、代理服務器
1、什么是代理服務器
一種重要的服務器安全功能,它的工作主要在開放系統互聯(OSI)模型的會話層,從而起到防火牆的作用
FQ,是指繞過相應的IP封鎖、內容過濾、域名劫持、流量限制等。
2、代理的常用功能
突破自身IP訪問限制,訪問國外站點。
訪問一些單位或團體內部資源
提高訪問速度,通常代理服務器都設置一個較大的硬盤緩沖區,當有外界的信息通過時,同時也將其保存到緩沖區中,當其他用戶再訪問相同的信息時, 則直接由緩沖區中取出信息,傳給用戶,以提高訪問速度。
隱藏真實IP。
3、代碼配置
創建Reuqest對象
創建ProxyHandler對象
用handler對象創建opener對象
使用opener.open函數發送請求
# 創建代理handler對象 handler = urllib.request.ProxyHandler(proxies={'http':'114.212.80.2:3128'}) # 創建opener opener = urllib.request.build_opener(handler) # opener.open 替代 urllib.request.urlopen response = opener.open(request) with open('ipp.html','wb') as fp: fp.write(response.read())