天天看點

python_爬蟲 03 urllib庫一、urllib庫二、cookie 

目錄

一、urllib庫

1.1 urlopen函數

1.2 urlretrieve函數

1.3 urlencode函數

1.4 parse_qs函數

1.5 urlparse和urlsplit

1.6 request.Request類:

1.7 測試:用Request爬取拉勾網資料

1.8 ProxyHandler處理器(代理設定)

二、cookie 

2.1 什麼是cookie

2.2 cookie的格式

2.3 使用cookielib庫和HTTPCookieProcessor模拟登入

http.cookiejar子產品 

登入自己搭建的網站

儲存cookie到本地

從本地加載cookie

一、urllib庫

urllib庫是Python中一個最基本的網絡請求庫。可以模拟浏覽器的行為,向指定的伺服器發送一個請求,并可以儲存伺服器傳回的資料。

1.1 urlopen函數

在Python3的urllib庫中,所有和網絡請求相關的方法,都被集到urllib.request子產品下面了,以先來看下urlopen函數基本的使用:

from urllib import request
resp = request.urlopen('http://www.baidu.com')
print(resp.read())
print(resp.getcode())
           

實際上,使用浏覽器通路百度,右鍵檢視源代碼。你會發現,跟我們剛才列印出來的資料是一模一樣的。也就是說,上面的三行代碼就已經幫我們把百度的首頁的全部代碼爬下來了。一個基本的url請求對應的python代碼真的非常簡單。

以下對urlopen函數的進行詳細講解:

    url:請求的url。

    data:請求的data,如果設定了這個值,那麼将變成post請求。

    傳回值:傳回值是一個http.client.HTTPResponse對象,這個對象是一個類檔案句柄對象。有read(size)、readline、readlines以及getcode等方法。

1.2 urlretrieve函數

這個函數可以友善的将網頁上的一個檔案儲存到本地。以下代碼可以非常友善的将百度的首頁下載下傳到本地:

from urllib import request
request.urlretrieve('http://www.baidu.com', "baidu_bak.html")
           

1.3 urlencode函數

用浏覽器發送請求的時候,如果url中包含了中文或者其他特殊字元,那麼浏覽器會自動的給我們進行編碼。而如果使用代碼發送請求,那麼就必須手動的進行編碼,這時候就應該使用urlencode函數來實作。urlencode可以把字典資料轉換為URL編碼的資料。示例代碼如下:

from  urllib import parse
data = {"name": "爬蟲基礎", "age": 90}
qs = parse.urlencode(data)
print(qs)  # name=%E7%88%AC%E8%99%AB%E5%9F%BA%E7%A1%80&age=90
           

1.4 parse_qs函數

可以将經過編碼後的url參數進行解碼。示例代碼如下:

from  urllib import parse
qs_str = "name=%E7%88%AC%E8%99%AB%E5%9F%BA%E7%A1%80&age=90"
data = parse.parse_qs(qs_str)
print(data)  # {'name': ['爬蟲基礎'], 'age': ['90']}
           

1.5 urlparse和urlsplit

有時候拿到一個url,想要對這個url中的各個組成部分進行分割,那麼這時候就可以使用urlparse或者是urlsplit來進行分割。示例代碼如下:

from urllib import request, parse
url = "http://www.baidu.com/s;hello?usernme=zhiliao#101"
result1 = parse.urlsplit(url)
print(result1)
result2 = parse.urlparse(url)
print(result2)
print("result2-->params :", result2.params)

result = result1
print("scheme:", result.scheme)
print("netloc:", result.netloc)
print("path:", result.path)
print("query:",result.query)
"""
結果:
    SplitResult(scheme='http', netloc='www.baidu.com', path='/s;hello', query='usernme=zhiliao', fragment='101')
    ParseResult(scheme='http', netloc='www.baidu.com', path='/s', params='hello', query='usernme=zhiliao', fragment='101')
    result2-->params : hello
    scheme: http
    netloc: www.baidu.com
    path: /s;hello
    query: usernme=zhiliao
"""
           

urlparse和urlsplit基本上是一模一樣的。唯一不一樣的地方是,urlparse裡面多了一個params屬性,而urlsplit沒有這個params屬性。比如有一個url為:url = 'http://www.baidu.com/s;hello?wd=python&username=abc#1',

那麼urlparse可以擷取到hello,而urlsplit不可以擷取到。url中的params也用得比較少。

1.6 request.Request類:

如果想要在請求的時候增加一些請求頭,那麼就必須使用request.Request類來實作。比如要增加一個User-Agent,示例代碼如下:

from  urllib import request
headers = {
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"
        }
req = request.Request("http://www.baidu.com/", headers=headers)
resp = request.urlopen(req)
print(resp.read())
           

1.7 測試:用Request爬取拉勾網資料

python_爬蟲 03 urllib庫一、urllib庫二、cookie 

測試代碼:

from urllib import request,parse

url = "https://www.lagou.com/jobs/positionAjax.json?city=%E5%B9%BF%E5%B7%9E&needAddtionalResult=false"

headers = {
    "Referer": "https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER",
    'Origin': 'https://www.lagou.com',
    "cookie": "LGUID=20161229121751-c39adc5c-cd7d-11e6-8409-5254005c3644; user_trace_token=20210531172956-5c418892-9a48-4fa1-8377-2c4f7a20b741; LG_HAS_LOGIN=1; hasDeliver=0; privacyPolicyPopup=false; WEBTJ-ID=20210531185241-179c20de542206-0e1348daf26807-2b6f686a-1049088-179c20de543150; sajssdk_2015_cross_new_user=1; RECOMMEND_TIP=true; __SAFETY_CLOSE_TIME__21787087=1; __lg_stoken__=677cc1b348553c3ed5e9cbb7b390a2ff300eb24fefe8a8e97e42e2872fc9543fba2800c9390bbd1d173c49e0c0362f67288bd32b2db49b0ed2db58d21a0b452d975350e4ed22; index_location_city=%E5%B9%BF%E5%B7%9E; login=false; unick=""; _putrc=""; JSESSIONID=ABAAAECABIEACCAAF01E6707FDF7DE8820405BA09C6C439; sensorsdata2015session=%7B%7D; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2221787087%22%2C%22first_id%22%3A%22179c20de60575-03879bc46582c2-2b6f686a-1049088-179c20de606146%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24os%22%3A%22Windows%22%2C%22%24browser%22%3A%22Chrome%22%2C%22%24browser_version%22%3A%2257.0.2987.98%22%7D%2C%22%24device_id%22%3A%22179c20de60575-03879bc46582c2-2b6f686a-1049088-179c20de606146%22%7D; _gid=GA1.2.1174524155.1622458625; X_HTTP_TOKEN=34e72e60c648e0f973806422611a51a83da2b43601; _ga=GA1.2.2125950788.1622453397; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1622453401,1622453584,1622458348,1622460836; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1622460836; LGRID=20210531193420-de9f4106-792c-4b58-ba57-e23c467d0128; TG-TRACK-CODE=index_search; SEARCH_ID=ea9769f0cf34489a97b947fba98efdb0"
}

data = {
    "first": "true",
    "pn": 1,
    "kd": "python"
}

data_bytes = parse.urlencode(data).encode("utf-8")  #
req = request.Request(url, headers=headers, data=data_bytes, method="POST")
resp = request.urlopen(req)
print(resp.read().decode("utf-8"))
'''
如果不添加cookie , 背景将傳回:
{"status":false,"msg":"您操作太頻繁,請稍後再通路","clientIp":"14.23.114.195","state":2402}
被識别出來
'''
           

1.8 ProxyHandler處理器(代理設定)

    很多網站會檢測某一段時間某個IP的通路次數(通過流量統計,系統日志等),如果通路次數多的不像正常人,它會禁止這個IP的通路。

    是以我們可以設定一些代理伺服器,每隔一段時間換一個代理,就算IP被禁止,依然可以換個IP繼續爬取。

    urllib中通過ProxyHandler來設定使用代理伺服器,下面代碼說明如何使用自定義opener來使用代理:

1、代理的原理:在請求目的網站之前,先請求代理伺服器,如何讓代理服務去請求目的網站,
    代理伺服器拿到目的網站的資料後,在轉發給我們的代碼
2、http://httpbin.org:這個網站可以友善的檢視http請求的一下參數
3、在代碼中使用代理:
    (1)使用 urllib.request.ProxyHandler, 傳入一個代理,這個代理是一個字典,
        字典的key依賴于代理伺服器能都接收的類型, 一般是 http 或是 https, 值是 ip:port
    (2)使用上一步建立的 handler, 以及 request.build_opener 建立一個 opener 對象
    (3)使用上一步建立的 opener 調用 open 方法, 發起請求
           
from urllib import request
# 沒有使用代理的情況
# url = "http://httpbin.org/ip"
# resp = request.urlopen(url)
# print(resp.read().decode("utf-8"))

# 使用代理的情況
url = "http://httpbin.org/ip"
# 1、使用ProxyHandler 傳入代理建構一個handler
handler = request.ProxyHandler({"http": "210.61.240.162:8080"})
# 2、使用上面建立的handler建構一個opener
opener = request.build_opener(handler)
# 3、使用opener去發送一個請求
resp = opener.open(url)
print(resp.read().decode("utf-8"))
# 測試過程中可能由于代理伺服器問題,沒能請求成功
           

常用的代理有:

    西刺免費代理IP:http://www.xicidaili.com/

    快代理:http://www.kuaidaili.com/

    代理雲:http://www.dailiyun.com/

下面是使用在快代理上購買服務的測試代碼

from urllib import request, parse
# 為了測試這個功能,在 快代理 上找的免費代理老是通路不了,花了 6 元買了一天的代理測試成功
api_url = "http://kps.kdlapi.com/api/getkps/?orderid=90225131796xxxx&num=1&pt=1&sep=1"
print("api_url = {}".format(api_url))
resp = request.urlopen(api_url)
proxy_ip_port = resp.read().decode("utf-8")
print(proxy_ip_port)
proxy = {
    "http": proxy_ip_port
}

# 使用代理的情況
url = "http://httpbin.org/ip"
# 1、使用ProxyHandler 傳入代理建構一個handler
handler = request.ProxyHandler(proxy)
# 2、使用上面建立的handler建構一個opener
opener = request.build_opener(handler)
# 3、使用opener去發送一個請求
resp = opener.open(url)
print("通過代理傳回的結果:{}".format(resp.read().decode("utf-8")))
           

二、cookie 

2.1 什麼是cookie

在網站中,http請求是無狀态的。也就是說即使第一次和伺服器連接配接後并且登入成功後,第二次請求伺服器依然不能知道目前請求是哪個使用者。cookie的出現就是為了解決這個問題,第一次登入後伺服器傳回一些資料(cookie)給浏覽器,然後浏覽器儲存在本地,當該使用者發送第二次請求的時候,就會自動的把上次請求存儲的cookie資料自動的攜帶給伺服器,伺服器通過浏覽器攜帶的資料就能判斷目前使用者是哪個了。cookie存儲的資料量有限,不同的浏覽器有不同的存儲大小,但一般不超過4KB。是以使用cookie隻能存儲一些小量的資料。

2.2 cookie的格式

Set-Cookie: NAME=VALUE;Expires/Max-age=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE

參數意義:

NAME:cookie的名字。

VALUE:cookie的值。

Expires:cookie的過期時間。

Path:cookie作用的路徑。

Domain:cookie作用的域名。

SECURE:是否隻在https協定下起作用。

2.3 使用cookielib庫和HTTPCookieProcessor模拟登入

Cookie 是指網站伺服器為了辨識使用者身份和進行Session跟蹤,而儲存在使用者浏覽器上的文本檔案,Cookie可以保持登入資訊到使用者下次與伺服器的會話。

這裡以我搭建的一個管理端為例。要通路管理端的内容管理頁面,必須先登入才能通路,登入說白了就是要有cookie資訊。那麼如果我們想要用代碼的方式通路,就必須要有正确的cookie資訊才能通路。解決方案有兩種,第一種是使用浏覽器通路,然後将cookie資訊複制下來,放到headers中。示例代碼如下:

from urllib import request

csdn_url = "http://47.106.134.xx:10086/wp-blog/wp-admin/"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER",
    "Referer": "http://47.106.134.xx:10086/wp-blog/wp-login.php",
    "Cookie": "wordpress_143dbe2bf39634bb00ebe77d685bad79="
              "admin%7C1623728016%7CTizOsc9GjD9BVvku7NSAUUYHdhdsNP3wKCrKj0AurSF%"
              "7C3ebadcce94763d4e004793919da9fabb87431cecb47ea539a9871c067364ec91; "
              "wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_143dbe2bf39634bb00ebe77d685bad79"
              "=admin%7C1623728016%7CTizOsc9GjD9BVvku7NSAUUYHdhdsNP3wKCrKj0AurSF%7C099f9d1ffeb11c6f155feee"
              "c7f7c25e0c0092aa4f789709d2b10c2b04fcccf84; "
              "wp-settings-1=libraryContent%3Dbrowse; wp-settings-time-1=1622518418"
}
req = request.Request(csdn_url, headers=headers)
resp = request.urlopen(req)
# print(resp.read().decode("utf-8"))
with open("test_cookie.html", "wb") as f:
    f.write(resp.read())
'''
在沒有設定 Cookie 的情況下,請求url 擷取到的是登入頁面的資料
設定了有效的 Cookie 資料之後,能正确擷取到頁面資料
'''
           

但是每次在通路需要cookie的頁面都要從浏覽器中複制cookie比較麻煩。在Python處理Cookie,一般是通過http.cookiejar子產品和urllib子產品的HTTPCookieProcessor處理器類一起使用。http.cookiejar子產品主要作用是提供用于存儲cookie的對象。而HTTPCookieProcessor處理器主要作用是處理這些cookie對象,并建構handler對象。

http.cookiejar子產品 

該子產品主要的類有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。這四個類的作用分别如下:

CookieJar:管理HTTP cookie值、存儲HTTP請求生成的cookie、向傳出的HTTP請求添加cookie的對象。整個cookie都存儲在記憶體中,對CookieJar執行個體進行垃圾回收後cookie也将丢失。

FileCookieJar(filename,delayload=None,policy=None):從CookieJar派生而來,用來建立FileCookieJar執行個體,檢索cookie資訊并将cookie存儲到檔案中。filename是存儲cookie的檔案名。delayload為True時支援延遲通路通路檔案,即隻有在需要時才讀取檔案或在檔案中存儲資料。

MozillaCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與Mozilla浏覽器 cookies.txt相容的FileCookieJar執行個體。

LWPCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與libwww-perl标準的 Set-Cookie3 檔案格式相容的FileCookieJar執行個體。

登入自己搭建的網站

利用http.cookiejar和request.HTTPCookieProcessor登入。相關示例代碼如下:

from urllib import request, parse
from http.cookiejar import CookieJar
# 1、登入
# 1.1、建立一個 cookiejar 對象
cookiejar = CookieJar()
# 1.2、使用 cookiejar 建立一個 HTTPCookieProcess 對象
handler = request.HTTPCookieProcessor(cookiejar)
# 1.3、使用上一步建立的 handler 建立一個 opener
opener = request.build_opener(handler)
# 1.4、使用 opener 發送登陸的請求(賬号和密碼)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}
data = {
    "log":"[email protected]",
    "pwd":"abc182355",
    "wp-submit":"Log In",
    "redirect_to":"http://47.106.134.xx:10086/wp-blog/wp-admin/",
    "testcookie":"1"
}
login_url = "http://47.106.134.xx:10086/wp-blog/wp-login.php"
data_bytes = parse.urlencode(data).encode("utf-8")
req = request.Request(url=login_url, headers=headers, data=data_bytes)
opener.open(req) # 這裡為了獲得 Cookie 不用接受傳回,Cookie 會存在 cookiejar

#2、通路個人首頁
admin_url = "http://47.106.134.xx:10086/wp-blog/wp-admin/"
# 獲得個人首頁的頁面的時候,不用建立一個opener
# 而應該使用之前的那個 opener, 因為之前的那個 opener 已經包含了登陸需要的 cookie 資訊
req = request.Request(admin_url, headers=headers)
resp =opener.open(req)
with open("admin.html", "wb") as f:
    f.write(resp.read())
           

封裝成函數版本

"""
通過python 登陸擷取 cookie :封裝成方法 
"""
from urllib import request, parse
from http.cookiejar import CookieJar

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}
def get_opener():
    # 1、登入
    # 1.1、建立一個 cookiejar 對象
    cookiejar = CookieJar()
    # 1.2、使用 cookiejar 建立一個 HTTPCookieProcess 對象
    handler = request.HTTPCookieProcessor(cookiejar)
    # 1.3、使用上一步建立的 handler 建立一個 opener
    opener = request.build_opener(handler)
    return opener


def login(opener):
    # 1.4、使用 opener 發送登陸的請求(賬号和密碼)
    data = {
        "log":"[email protected]",
        "pwd":"abc102256",
        "wp-submit":"Log In",
        "redirect_to":"http://47.106.134.xx:10086/wp-blog/wp-admin/",
        "testcookie":"1"
    }
    login_url = "http://47.106.134.xx:10086/wp-blog/wp-login.php"
    data_bytes = parse.urlencode(data).encode("utf-8")
    req = request.Request(url=login_url, headers=headers, data=data_bytes)
    opener.open(req) # 這裡為了獲得 Cookie 不用接受傳回,Cookie 會存在 cookiejar

def visit_profile(opener):
    #2、通路個人首頁
    admin_url = "http://47.106.134.xx:10086/wp-blog/wp-admin/"
    # 獲得個人首頁的頁面的時候,不用建立一個opener
    # 而應該使用之前的那個 opener, 因為之前的那個 opener 已經包含了登陸需要的 cookie 資訊
    req = request.Request(admin_url, headers=headers)
    resp =opener.open(req)
    with open("admin.html", "wb") as f:
        f.write(resp.read())

if __name__ == '__main__':
    opener = get_opener()
    login(opener)
    visit_profile(opener)
    
           

儲存cookie到本地

儲存cookie到本地,可以使用cookiejar的save方法,并且需要指定一個檔案名:

cookiejar.save(ignore_discard=True, ignore_expires=True)

    ignore_discard = True : 即使cookies将被丢棄也将它儲存下來

    ignore_expires = True : 如果cookies已經過期也将它儲存并且檔案已存在時将覆寫

"""
本文通過 httpbin.org 提供的接口送出一個 cookie,設定成功後伺服器會傳回設定的 cookie 讓浏覽器儲存
"""
from urllib import request
from http.cookiejar import MozillaCookieJar

cookiejar = MozillaCookieJar("cookie.txt")
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)

headers = {
     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}
req = request.Request("http://httpbin.org/cookies/set?myCookie=A0001", headers=headers)

resp =opener.open(req)
print(resp.read().decode("utf-8"))
# 設定 廢棄 和 過期 的 cookie 都儲存下來
cookiejar.save(ignore_discard=True, ignore_expires=True)

"""
結果 cookit.txt 檔案中儲存内容如下
    # Netscape HTTP Cookie File
    # http://curl.haxx.se/rfc/cookie_spec.html
    # This is a generated file!  Do not edit.
    
    httpbin.org	FALSE	/	FALSE		myCookie	A0001
"""
           

從本地加載cookie

從本地加載cookie,需要使用cookiejar的load方法,并且也需要指定方法:

from urllib import request
from http.cookiejar import MozillaCookieJar

cookiejar2 = MozillaCookieJar("cookie2.txt")
cookiejar2.load(ignore_discard=True, ignore_expires=True)
handler2 = request.HTTPCookieProcessor(cookiejar2)
opener2 = request.build_opener(handler2)

# 列印現有儲存的 cookie
for c in cookiejar2:
    print(c)

"""
cookie2.txt 檔案
    # Netscape HTTP Cookie File
    # http://curl.haxx.se/rfc/cookie_spec.html
    # This is a generated file!  Do not edit.
    
    httpbin.org	FALSE	/	FALSE		myCookie1	A0001
    httpbin.org	FALSE	/	FALSE		myCookie2	A0002
    
列印結果:
    <Cookie myCookie1=A0001 for httpbin.org/>
    <Cookie myCookie2=A0002 for httpbin.org/>
"""
           

目錄