1. 程式人生 > >初級爬蟲第三天

初級爬蟲第三天

遠程服務器 encode exce () cau esp pre inf 1.8

主要內容:

  • 付費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 16
for 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)為了能讓程序自動登錄,我們需要查看,登錄過程中,瀏覽器都向服務器提交了哪些數據

由於登錄過程為:

  1. 瀏覽器向服務器提交登錄數據
  2. 服務器驗證數據
  3. 服務器向瀏覽器發送新的跳轉頁面的鏈接
  4. 瀏覽器按照跳轉頁面的鏈接,向服務器重新發送請求

所以,在登錄頁面,點擊“登錄”按鈕之後,頁面會跳轉到新的頁面。此時瀏覽器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進制數據轉換為字符串格式,靠猜。

初級爬蟲第三天