初級爬蟲第三天
主要內容:
- 付費IP的使用方式
- Auth認證
- cookie登錄驗證
- requests模塊
一、付費IP使用方式:
1.1 無論是免費IP還是付費IP,在使用之前,都需要測試一下,如果好使,再去使用IP爬取數據。
1.2 IP池:列表套字典
eg:[{"https": "IP1:端口1"}, {"http": "IP2: 端口2"}, {"https": "IP3: 端口3"}]
1.3 遍歷IP池,利用遍歷出來的IP創建IP處理器,再利用處理創建發送網絡請求的opener對象
1.4 opener.open()中有一個參數timeout=x,即:x秒之後如果程序還沒有反應,就算做超時,報超時,x默認為30
1.5 利用異常處理IP值不好用的報錯或者超時
代碼:
1 import urllib.request 2 3 爬取百度首頁"https://www.baidu.com/" 4 def proxy_user(): 5 #1.目標網頁URL 6 url = "https://www.baidu.com/" 7 #2. User-Agent 8 user_agent = ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"] 9 request = urllib.request.Request(url) 10 request.add_header("User-Agent", user_agent[0]) 11 #3. 代理IP池 12 #3.1 創建代理IP池 13 #IP池結構是:列表套字典 14 proxy_ip_list = [{"HTTP": "58.253.152.23:9999"}, {"HTTP": "112.87.68.242:9999"}, {"HTTP": "60.13.42.99:9999"}] 15 #3.2 遍歷IP 16for choose__proxy_ip in proxy_ip_list: 17 #3.3 利用遍歷出來的IP創建IP處理器 18 proxy_ip_handler = urllib.request.ProxyHandler(choose__proxy_ip) 19 #3.4 利用處理器創建opener對象 20 opener = urllib.request.build_opener(proxy_ip_handler) 21 #3.5 發送網絡請求 22 #利用異常處理+timeout參數,處理IP失效問題 23 #如果IP使用異常,或者超過timeout設置時間, 24 #則拋出異常,暫停使用當前IP,接著使用下一個IP 25 try: 26 #使用opener.open()發送網絡請求, 27 #超時參數timeout設置為10,超過10秒未響應,視為超時 28 response = opener.open(request, timeout = 10) 29 except Exception as e: 30 #如果IP使用異常,或者超時,打印異常信息 31 print(e) 32 33 34 proxy_user()
查看結果,發現當上一個IP不好用時,程序會自動使用下一個IP
2. 付費IP的兩種寫法:
2.1 寫法1:發送付費的代理IP請求
(1)創建代理IP池
(2)創建handler處理器
(3)創建opener對象
(4)發送網絡請求
註意:付費代理IP需要有:1. 購買時的用戶名 2. 購買時的密碼
代理IP池結構:列表套字典
其中字典結構:{"協議類型": "username: [email protected]地址:端口號"}
1 import urllib.request
2
3 #付費代理IP第一種寫法:
4 def money_proxy_user():
5 #url = "http://www.baidu.com/"
6
7 #1. IP池
8 #結構:{"http/https": "username:[email protected]:端口號"}
9 money_proxy_list = [{"http": "abc:[email protected]:9999"},
10 {"http": "abc:[email protected]:9999"},
11 {"http": "abc:[email protected]:9999"}]
12 #遍歷IP
13 for proxy_ip in money_proxy_list:
14 #2. 創建代理IP處理器handler
15 ProxyHandler = urllib.request.ProxyHandler(proxy_ip)
16 #3. 創建opener對象
17 opener = urllib.request.build_open(ProxyHandler)
18 #4. 發送網絡請求
19 response = opener.open(url)
2.2 付費IP第二種寫法:使用密碼管理器(推薦)
(1)創建IP池
(2)創建密碼管理器
(3)向密碼管理器中,添加用戶名和密碼
(4)創建處理器
(5)創建opener對象
(6)opener對象發送網絡請求
1 import urllib.request
2
3 #付費代理IP第二種寫法:
4 def money_proxy_user():
5 url = "http://www.baidu.com/"
6
7 #1. 創建IP池
8 #結構:{"http/https": "IP值:端口號"}
9 money_proxy_list = [{"http": "58.253.152.23:9999"},
10 {"http": "112.87.68.242:9999"},
11 {"http": "60.13.42.99:9999"}]
12 #遍歷IP
13 for proxy_ip in money_proxy_list:
14 #2. 創建密碼管理器
15 #密碼管理器需要購買IP的用戶名和密碼
16 user_name = "abc"
17 password = "123"
18 pass_word_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
19 #3. 向密碼管理器添加用戶名和密碼
20 pass_word_manager.add_password(None, proxy_ip, user_name, password)
21 #4. 創建handler處理器
22 money_proxy_handler = urllib.request.ProxyBasicAuthHandler(pass_word_manager)
23 #5. 創建opener對象
24 opener =urllib.request.build_opener(money_proxy_handler)
25 #6. 發送請求
26 response = opener.open(url)
27
註意:
1. 密碼管理器對象,添加用戶名和密碼的方法:‘
add_password(Realm, uri, user_name, password)
第一個參數realm是與遠程服務器相關的域信息,一般沒人管它都是寫None
參數uri:填寫代理IP!!!
user_name:付費IP用戶名
password:付費IP密碼
2. 本方式中,如果程序中提供的代理IP全部都不能使用,程序會使用本地IP
二、Auth認證:
使用範圍:
(1)爬取公司內網數據
(2)通過第三方認證的方式(如:微信賬號登錄),進行爬取
方法:使用密碼管理器
代碼:
1 import urllib.request
2
3 def auth_user():
4 #1. 給出內網賬號和密碼
5 user_name = "abc"
6 pwd = "123"
7 #2. 內網的URL地址
8 nei_url = "xxx"
9
10 #3. 創建密碼管理器
11 pwd_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
12 #4. 向密碼管理器中,添加用戶名和密碼
13 #註意此時,add_password()的第二個參數要訪問的目標網頁URL地址
14 pwd_manager.add_password(None, nei_url, user_name, pwd)
15
16 #5. 創建處理器Handler
17 auth_handler = urllib.request.HTTPBasicAuthHandler(pwd_manager)
18 #6. 創建opener對象
19 opener = urllib.request.build_opener(auth_handler)
20
21 #7. 發送網絡請求:
22 response = opener.open(nei_url)
2.5:SSL爬蟲報錯:SSL: CERTIFICATE_VERIFY_FAILED
當使用urllib.request.urlopen()向https網頁發送網絡請求時,有時會報這個錯誤:
urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:581)
解決方法:導入ssl模塊和如下代碼,即可解決
1 import ssl
2
3 ssl._create_default_https_context = ssl._create_unverified_context
三、Cookie與登錄信息:
目標:爬取藥智網的會員中心頁面數據
URL:https://www.yaozh.com/member/
目標網頁截圖:
3.1 第一次爬取:
1 import urllib.request
2 import ssl
3
4 #忽略SSL驗證
5 ssl._create_default_https_context = ssl._create_unverified_context
6
7 #1. 目標網頁URL
8 url = "https://www.yaozh.com/member/"
9
10 #2. User-Agent
11 #2.1 創建request對象
12 request = urllib.request.Request(url)
13 #2.2 User-Agent池
14 user_agent_headers = ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"]
15 #2.3 向request對象添加User-Agent
16 request.add_header("User-Agent", user_agent_headers[0])
17
18 #也可以直接在創建request對象時,添加User-Agent
19 #request = urllib.request.Request(url, headers=user_agent_headers[0])
20
21
22 #3. 代理IP
23 #3.1 代理IP池
24 proxy_ip = {"https": "60.13.42.101:9999"}
25 #3.2 創建自定義處理器handler
26 proxy_handler = urllib.request.ProxyHandler(proxy_ip)
27 #3.3 創建自定義opener對象
28 opener = urllib.request.build_opener(proxy_handler)
29
30
31 #4. 發送網絡請求
32 response = opener.open(request)
33
34 #5. 讀取返回的數據
35 data = response.read().decode(‘utf-8‘)
36
37 #6. 數據持久化
38 with open("yaozh01.html", ‘w‘, encoding=‘utf-8‘) as f:
39 f.write(data)
得到結果:
結果,個人中心頁面未登錄
——(1)原因:登錄需要賬號信息:用戶名和密碼
但是,爬蟲代碼中,沒有這兩個信息
——(2)解決方法:向請求頭中,添加cookie
因為,所有的個人信息,包括:賬號和密碼都保存在cookie中。
一旦用戶登錄成功,其賬號和密碼就會自動保存在cookie中。
而cookie位於請求頭中。
——(3)所以,我們需要先登錄成功一次,獲取cookie,然後,將cookie添加到請求頭中。這樣,以後爬蟲代碼發送網絡請求登錄時,就可以自動使用第一次登陸成功後,保存下來的cookie,使用它裏面的賬號信息。
3.2 方式1:手動獲取添加cookie:
(1)手動登錄賬號,進入個人中心頁面
(2)f12,進入network,查看請求頭request_headers
(3)如圖所示,將cookie後面的所有信息全部復制粘貼,添加進請求頭
(4)發送網絡請求
1 import urllib.request
2 import ssl
3
4
5 #爬取藥智網會員中心界面
6 #"https://www.yaozh.com/member/"
7
8 #忽略SSL驗證
9 ssl._create_default_https_context = ssl._create_unverified_context
10
11 #1. 目標網頁URL
12 url = "https://www.yaozh.com/member/"
13
14 #2. 請求頭信息
15 #User-Agent、Cookie
16 #手動填寫用戶名和密碼,進行登錄,登陸成功之後,在會員中心頁面,f12查看請求頭信息,復制User-Agent和Cookie信息
17 user_agent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
18 "Cookie": "acw_tc=2f624a1c15572983332095835e56ab374f19bbfa04320ed34624c8cb736211; PHPSESSID=db04icmo08qlth57e283e1jug6; Hm_lvt_65968db3ac154c3089d7f9a4cbb98c94=1557298333; MEIQIA_VISIT_ID=1KwMiirKV6lBEDTorlxXFpIQYZ2; MEIQIA_EXTRA_TRACK_ID=1KwMiirgbfupj7bs3kZzgBM0zWb; Hm_lpvt_65968db3ac154c3089d7f9a4cbb98c94=1557298612; yaozh_logintime=1557298624; yaozh_user=743973%09wtf1234; yaozh_userId=743973; db_w_auth=666998%09wtf1234; UtzD_f52b_saltkey=NQ3FQNjM; UtzD_f52b_lastvisit=1557295025; UtzD_f52b_lastact=1557298625%09uc.php%09; UtzD_f52b_auth=760dhxbJO6R21izVhpr2PAlqsjjpHEtuxu9QWpf%2FdzfvGFnBP6V66TEdhcVOWltYjPEmVh4tVSLLQTPKM12K%2BlK55Ls; yaozh_uidhas=1; yaozh_mylogin=1557298627; acw_tc=2f624a1c15572983332095835e56ab374f19bbfa04320ed34624c8cb736211; MEIQIA_VISIT_ID=1KwMiirKV6lBEDTorlxXFpIQYZ2; MEIQIA_EXTRA_TRACK_ID=1KwMiirgbfupj7bs3kZzgBM0zWb"
19 }
20 #2.2 創建request對象,並添加User-Agent
21 request = urllib.request.Request(url, headers=user_agent)
22
23
24 #3. 代理IP
25 #3.1 代理IP,IP池
26 proxy_ip = {"https": "115.159.155.60:8118"}
27 #3.2 創建自定義處理器對象handler
28 proxy_handler = urllib.request.ProxyHandler(proxy_ip)
29 #3.3 創建自定義opener對象
30 opener = urllib.request.build_opener(proxy_handler)
31
32
33 #4. 發送網絡請求,登錄
34 response = opener.open(request)
35 #5. 讀取返回數據
36 data = response.read().decode(‘utf-8‘)
37
38 #6. 數據持久化
39 with open("yaozhi02.html", "w", encoding=‘utf-8‘) as f:
40 f.write(data)
結果:
可以看到,在本機啟動的網頁會員中心,這次有了用戶名,說明登錄成功。
———手動獲取cookie的缺點:(1)麻煩;(2)獲取的cookie有時效性
3.3 方式2:使用代碼自動獲取cookie(重點):
目標:自動獲取(登陸後的)會員中心頁面
難點:獲取cookie,即:獲取登錄所需用戶名和密碼。由於在登錄成功之後,Cookie會被自動保存,所以,要想獲取Cookie,先要登錄成功。
登錄頁面login和會員中心頁面member是兩個不同的頁面,所以需要我們現在login頁面登錄成功,然後拿著cookie,去請求member頁面
步驟:
(1)自動登錄(但需要自己手動設置用戶名和密碼),登錄成功之後,會自動保存Cookie
(2)代碼自動獲取Cookie
(3)帶著cookie,代碼請求會員中心頁面,獲取會員中心數據
———————————分割線—————————————————————
(1)為了能讓程序自動登錄,我們需要查看,登錄過程中,瀏覽器都向服務器提交了哪些數據
由於登錄過程為:
- 瀏覽器向服務器提交登錄數據
- 服務器驗證數據
- 服務器向瀏覽器發送新的跳轉頁面的鏈接
- 瀏覽器按照跳轉頁面的鏈接,向服務器重新發送請求
所以,在登錄頁面,點擊“登錄”按鈕之後,頁面會跳轉到新的頁面。此時瀏覽器network抓取到的數據,是第4步:瀏覽器向服務器按照跳轉頁面URL,向服務器發送請求時,的數據;而不是第1步:瀏覽器向服務器提交的登錄數據。因為谷歌瀏覽器會只保留最新一次請求的數據,而將之前的請求抹除了。
所以,在f12的network中,點擊preserve log按鈕,這樣瀏覽器就會保留之前發送過的請求日誌。
點擊preserve log按鈕,在https://www.yaozh.com/login/操作登錄,發現瀏覽器進行登錄時,發送的是一個叫做“login”的網絡請求。
點擊login請求,發現:(1)login發送的是POST請求!!!;(2)Form Data:Form Data為需要發送POST請求時,要發送的參數
1 import urllib.request
2 import urllib.parse
3 import http.cookiejar
4 import ssl
5
6 #忽略SSL驗證
7 ssl._create_default_https_context = ssl._create_unverified_context
8
9 #目標:爬取會員中心網頁
10
11 #1. 會員登錄,獲取cookie
12 #1.1 登錄網頁URL
13 login_url = "https://www.yaozh.com/login/"
14
15 #1.2 請求頭信息User-Agent
16 user_agent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"}
17
18 #1.3 構造請求參數
19 #構造POST請求參數,格式必須是字典
20 #發現登錄請求中,發送的是POST請求,POST請求有請求參數
21 #POST請求中需要用到的參數:位於瀏覽器請求頭的Form Data中,將其中的數據全部復制下來
22 """
23 username: wtf1234
24 pwd: 123456
25 formhash: 3210139E64
26 backurl: https%3A%2F%2Fwww.yaozh.com%2F
27 """
28 login_request_data = {"username": "wtf1234",
29 "pwd": "123456",
30 "formhash": "3210139E64",
31 "backurl": "https%3A%2F%2Fwww.yaozh.com%2F"}
32
33
34 #1.4 轉換POST請求參數格式
35 #POST請求中,上傳的請求參數必須是2進制格式
36 login_request_data_bytes = urllib.parse.urlencode(login_request_data).encode("utf-8")
37
38 #1.5 向request對象中,添加URL地址、User-Agent、POST請求參數
39 #get請求中,參數拼接到URL地址中;POST請求中,Request對象裏面專門有個data參數,用於接收POST請求參數
40 login_request = urllib.request.Request(login_url, headers=user_agent, data=login_request_data_bytes)
41
42 #1.6 構造CookieJar對象——用於保存管理cookie
43 my_cookiejar = http.cookiejar.CookieJar()
44
45 #1.7 構造自定義的可以添加cookie的處理器handler
46 #handler處理器的參數是構造的cookiejar對象
47 #因為urlopen()中不能添加cookie,所以我們需要找一個能添加cookie的處理器,然後再創建opener,再發送網絡請求
48 cookie_handler = urllib.request.HTTPCookieProcessor(my_cookiejar)
49
50 #1.8 使用處理器handler對象,構造自定義opener對象
51 opener = urllib.request.build_opener(cookie_handler)
52
53 #1.9 使用opener對象,發送網絡請求
54 #此時的login_request,既包含請求頭信息,又包含請求參數
55 #此時,如果請求發送成功,則cookiejar對象會自動將cookie信息保存到opener對象中
56 opener.open(login_request) #由於我們這裏只是為了登錄成功,獲取cookie,所以不需要response對象接收返回數據
57
58
59 #2. 使用cookie,訪問會員中心網頁
60 #2.1 會員中心URL
61 member_url = "https://www.yaozh.com/member/"
62
63 #2.2 構造請求頭,添加User-Agent信息
64 user_agent2 = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"}
65 member_request = urllib.request.Request(member_url, headers=user_agent2)
66
67 #2.3 拿著剛才獲得的cookie,發送網絡請求
68 #由於opener對象中已經保存有cookie信息,所以直接用opener對象發送網絡請求即可
69 response = opener.open(member_request)
70
71 #3. 讀取數據
72 data = response.read().decode("utf-8")
73
74 #4. 持久化
75 with open("yaozhi_day03.html", ‘w‘, encoding=‘utf-8‘) as f:
76 f.write(data)
爬取結果:
註:目前為止學過的反爬機制:
(1)User-Agent:模擬真實用戶
同一個瀏覽器,短時間內頻繁訪問,反爬
(2)IP地址:
同一個IP地址,短時間內頻繁訪問,反爬
(3)賬號:
同一個賬號,短時間內在不同地點(使用不同IP地址)訪問,反爬
三、初學requests模塊:
理論:
(1)requests模塊為第三方模塊
(2)使用方法:import requests 註意末尾多一個s
(3)優點:
- 簡單易用
- URL會自動轉譯
- python2、python3的方法名字一樣
- 有官方中文文檔
1 import requests
2
3 #爬取百度首頁"https://www.baidu.com/"
4
5 #1. 目標網頁URL
6 url = "https://www.baidu.com/"
7
8 #2. 發送get請求
9 response = requests.get(url)
10 #返回的response為狀態碼
11
12 #3.解析數據
13 #3.1 方式1:使用content屬性解析,返回的是2進制數據
14 data = response.content.decode(‘utf-8‘)
15
16 #3.2 方式2:使用text屬性解析,返回的是字符串格式數據
17 str_data = response.text
18 #但是不建議,使用此種方式解析數據,因為text內部,將2進制數據轉換為字符串格式,靠猜。
初級爬蟲第三天