網絡爬蟲之第一章網絡請求

第一章 網絡請求

第一節 爬蟲前奏

爬蟲的實際例子:

  1. 搜索引擎(百度、谷歌、360搜索等)。
  2. 伯樂在線。
  3. 惠惠購物助手。
  4. 數據分析與研究(數據冰山知乎專欄)。
  5. 搶票軟件等。

什麼是網絡爬蟲:

  1. 通俗理解:爬蟲是一個模擬人類請求網站行爲的程序。能夠自動請求網頁、並數據抓取下來,而後使用必定的規則提取有價值的數據。
  2. 專業介紹:百度百科

通用爬蟲和聚焦爬蟲:

  1. 通用爬蟲:通用爬蟲是搜索引擎抓取系統(百度、谷歌、搜狗等)的重要組成部分。主要是將互聯網上的網頁下載到本地,造成一個互聯網內容的鏡像備份。
  2. 聚焦爬蟲:是面向特定需求的一種網絡爬蟲程序,他與通用爬蟲的區別在於:聚焦爬蟲在實施網頁抓取的時候會對內容進行篩選和處理,儘可能保證只抓取與需求相關的網頁信息。

爲何用Python寫爬蟲程序:

  1. PHP:PHP是世界是最好的語言,但他天生不是作這個的,並且對多線程、異步支持不是很好,併發處理能力弱。爬蟲是工具性程序,對速度和效率要求比較高。
  2. Java:生態圈很完善,是Python爬蟲最大的競爭對手。可是Java語言自己很笨重,代碼量很大。重構成本比較高,任何修改會致使代碼大量改動。爬蟲常常要修改採集代碼。
  3. C/C++:運行效率是無敵的。可是學習和開發成本高。寫個小爬蟲程序可能要大半天時間。
  4. Python:語法優美、代碼簡潔、開發效率高、支持的模塊多。相關的HTTP請求模塊和HTML解析模塊很是豐富。還有Scrapy和Scrapy-redis框架讓咱們開發爬蟲變得異常簡單。

準備工具:

  1. Python3.6開發環境。
  2. Pycharm 2017 professional版。
  3. 虛擬環境。`virtualenv/virtualenvwrapper`。

 

第二節  http協議和Chrome抓包工具html

什麼是http和https協議:

HTTP協議:全稱是HyperText Transfer Protocol,中文意思是超文本傳輸協議,是一種發佈和接收HTML頁面的方法。服務器端口號是80端口。 HTTPS協議:是HTTP協議的加密版本,在HTTP下加入了SSL層。服務器端口號是443端口。前端

在瀏覽器中發送一個http請求的過程:

  1. 當用戶在瀏覽器的地址欄中輸入一個URL並按回車鍵以後,瀏覽器會向HTTP服務器發送HTTP請求。HTTP請求主要分爲「Get」和「Post」兩種方法。
  2. 當咱們在瀏覽器輸入URL http://www.baidu.com 的時候,瀏覽器發送一個Request請求去獲取 http://www.baidu.com 的html文件,服務器把Response文件對象發送回給瀏覽器。
  3. 瀏覽器分析Response中的 HTML,發現其中引用了不少其餘文件,好比Images文件,CSS文件,JS文件。 瀏覽器會自動再次發送Request去獲取圖片,CSS文件,或者JS文件。
  4. 當全部的文件都下載成功後,網頁會根據HTML語法結構,完整的顯示出來了。

url詳解:

URLUniform Resource Locator的簡寫,統一資源定位符。 一個URL由如下幾部分組成:python

scheme://host:port/path/?query-string=xxx#anchor
  • scheme:表明的是訪問的協議,通常爲http或者https以及ftp等。
  • host:主機名,域名,好比www.baidu.com
  • port:端口號。當你訪問一個網站的時候,瀏覽器默認使用80端口。
  • path:查找路徑。好比:www.jianshu.com/trending/now,後面的trending/now就是path
  • query-string:查詢字符串,好比:www.baidu.com/s?wd=python,後面的wd=python就是查詢字符串。
  • anchor:錨點,後臺通常不用管,前端用來作頁面定位的。

在瀏覽器中請求一個url,瀏覽器會對這個url進行一個編碼。除英文字母,數字和部分符號外,其餘的所有使用百分號+十六進制碼值進行編碼。git

經常使用的請求方法:

Http協議中,定義了八種請求方法。這裏介紹兩種經常使用的請求方法,分別是get請求和post請求。github

  1. get請求:通常狀況下,只從服務器獲取數據下來,並不會對服務器資源產生任何影響的時候會使用get請求。
  2. post請求:向服務器發送數據(登陸)、上傳文件等,會對服務器資源產生影響的時候會使用post請求。 以上是在網站開發中經常使用的兩種方法。而且通常狀況下都會遵循使用的原則。可是有的網站和服務器爲了作反爬蟲機制,也常常會不按常理出牌,有可能一個應該使用get方法的請求就必定要改爲post請求,這個要視狀況而定。

請求頭常見參數:

http協議中,向服務器發送一個請求,數據分爲三部分,第一個是把數據放在url中,第二個是把數據放在body中(在post請求中),第三個就是把數據放在head中。這裏介紹在網絡爬蟲中常常會用到的一些請求頭參數:web

  1. User-Agent:瀏覽器名稱。這個在網絡爬蟲中常常會被使用到。請求一個網頁的時候,服務器經過這個參數就能夠知道這個請求是由哪一種瀏覽器發送的。若是咱們是經過爬蟲發送請求,那麼咱們的User-Agent就是Python,這對於那些有反爬蟲機制的網站來講,能夠輕易的判斷你這個請求是爬蟲。所以咱們要常常設置這個值爲一些瀏覽器的值,來假裝咱們的爬蟲。
  2. Referer:代表當前這個請求是從哪一個url過來的。這個通常也能夠用來作反爬蟲技術。若是不是從指定頁面過來的,那麼就不作相關的響應。
  3. Cookiehttp協議是無狀態的。也就是同一我的發送了兩次請求,服務器沒有能力知道這兩個請求是否來自同一我的。所以這時候就用cookie來作標識。通常若是想要作登陸後才能訪問的網站,那麼就須要發送cookie信息了。

常見響應狀態碼:

  1. 200:請求正常,服務器正常的返回數據。
  2. 301:永久重定向。好比在訪問www.jingdong.com的時候會重定向到www.jd.com
  3. 302:臨時重定向。好比在訪問一個須要登陸的頁面的時候,而此時沒有登陸,那麼就會重定向到登陸頁面。
  4. 400:請求的url在服務器上找不到。換句話說就是請求url錯誤。
  5. 403:服務器拒絕訪問,權限不夠。
  6. 500:服務器內部錯誤。多是服務器出現bug了。

Chrome抓包工具:

Chrome瀏覽器是一個很是親近開發者的瀏覽器。能夠方便的查看網絡請求以及發送的參數。對着網頁右鍵->檢查。而後就能夠打開開發者選項。redis

      Elements: 構成這個網頁的源代碼。chrome

      Console:這個網頁的控制檯。json

      Sources:組成這個網頁的全部源文件。c#

      Network:加載這個頁面時,瀏覽器發送的全部請求。

 

第三節 urllib庫

urllib庫是Python中一個最基本的網絡請求庫。能夠模擬瀏覽器的行爲,向指定的服務器發送一個請求,並能夠保存服務器返回的數據。

urlopen函數:

Python3urllib庫中,全部和網絡請求相關的方法,都被集到urllib.request模塊下面了,以先來看下urlopen函數基本的使用:

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

實際上,使用瀏覽器訪問百度,右鍵查看源代碼。你會發現,跟咱們剛纔打印出來的數據是如出一轍的。也就是說,上面的三行代碼就已經幫咱們把百度的首頁的所有代碼爬下來了。一個基本的url請求對應的python代碼真的很是簡單。 如下對urlopen函數的進行詳細講解:

  1. url:請求的url。
  2. data:請求的data,若是設置了這個值,那麼將變成post請求。
  3. 返回值:返回值是一個http.client.HTTPResponse對象,這個對象是一個類文件句柄對象。有read(size)readlinereadlines以及getcode等方法。

urlretrieve函數:

這個函數能夠方便的將網頁上的一個文件保存到本地。如下代碼能夠很是方便的將百度的首頁下載到本地:

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

 

urlencode函數:

用瀏覽器發送請求的時候,若是url中包含了中文或者其餘特殊字符,那麼瀏覽器會自動的給咱們進行編碼。而若是使用代碼發送請求,那麼就必須手動的進行編碼,這時候就應該使用urlencode函數來實現。urlencode能夠把字典數據轉換爲URL編碼的數據。示例代碼以下:

from urllib import parse
data = {'name':'爬蟲基礎','greet':'hello world','age':100}
qs = parse.urlencode(data)
print(qs)

 

parse_qs函數:

能夠將通過編碼後的url參數進行解碼。示例代碼以下:

from urllib import parse
qs = "name=%E7%88%AC%E8%99%AB%E5%9F%BA%E7%A1%80&greet=hello+world&age=100"
print(parse.parse_qs(qs))

 

urlparse和urlsplit:

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

from urllib import request,parse

url = 'http://www.baidu.com/s?username=zhiliao'

result = parse.urlsplit(url)
# result = parse.urlparse(url)

print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)
print('query:',result.query)

 

urlparseurlsplit基本上是如出一轍的。惟一不同的地方是,urlparse裏面多了一個params屬性,而urlsplit沒有這個params屬性。好比有一個url爲:url = 'http://www.baidu.com/s;hello?wd=python&username=abc#1', 那麼urlparse能夠獲取到hello,而urlsplit不能夠獲取到。url中的params也用得比較少。

request.Request類:

若是想要在請求的時候增長一些請求頭,那麼就必須使用request.Request類來實現。好比要增長一個User-Agent,示例代碼以下:

from urllib import request

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

 

ProxyHandler處理器(代理設置)

不少網站會檢測某一段時間某個IP的訪問次數(經過流量統計,系統日誌等),若是訪問次數多的不像正常人,它會禁止這個IP的訪問。 因此咱們能夠設置一些代理服務器,每隔一段時間換一個代理,就算IP被禁止,依然能夠換個IP繼續爬取。 urllib中經過ProxyHandler來設置使用代理服務器,下面代碼說明如何使用自定義opener來使用代理:

from urllib import request

# 這個是沒有使用代理的
# resp = request.urlopen('http://httpbin.org/get')
# print(resp.read().decode("utf-8"))

# 這個是使用了代理的
handler = request.ProxyHandler({"http":"218.66.161.88:31769"})

opener = request.build_opener(handler)
req = request.Request("http://httpbin.org/ip")
resp = opener.open(req)
print(resp.read())

 

經常使用的代理有:

什麼是cookie:

在網站中,http請求是無狀態的。也就是說即便第一次和服務器鏈接後而且登陸成功後,第二次請求服務器依然不能知道當前請求是哪一個用戶。cookie的出現就是爲了解決這個問題,第一次登陸後服務器返回一些數據(cookie)給瀏覽器,而後瀏覽器保存在本地,當該用戶發送第二次請求的時候,就會自動的把上次請求存儲的cookie數據自動的攜帶給服務器,服務器經過瀏覽器攜帶的數據就能判斷當前用戶是哪一個了。cookie存儲的數據量有限,不一樣的瀏覽器有不一樣的存儲大小,但通常不超過4KB。所以使用cookie只能存儲一些小量的數據。

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協議下起做用。

使用cookielib庫和HTTPCookieProcessor模擬登陸:

Cookie 是指網站服務器爲了辨別用戶身份和進行Session跟蹤,而儲存在用戶瀏覽器上的文本文件,Cookie能夠保持登陸信息到用戶下次與服務器的會話。 這裏以人人網爲例。人人網中,要訪問某我的的主頁,必須先登陸才能訪問,登陸說白了就是要有cookie信息。那麼若是咱們想要用代碼的方式訪問,就必需要有正確的cookie信息才能訪問。解決方案有兩種,第一種是使用瀏覽器訪問,而後將cookie信息複製下來,放到headers中。示例代碼以下:

from urllib import request

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
    'Cookie': 'anonymid=jacdwz2x-8bjldx; depovince=GW; _r01_=1; _ga=GA1.2.1455063316.1511436360; _gid=GA1.2.862627163.1511436360; wp=1; 
JSESSIONID=abczwY8ecd4xz8RJcyP-v; jebecookies=d4497791-9d41-4269-9e2b-3858d4989785|||||; ick_login=884e75d4-f361-4cff-94bb-81fe6c42b220;
_de=EA5778F44555C091303554EBBEB4676C696BF75400CE19CC; p=61a3c7d0d4b2d1e991095353f83fa2141; first_login_flag=1; ln_uact=970138074@qq.com;
ln_hurl=http://hdn.xnimg.cn/photos/hdn121/20170428/1700/main_nhiB_aebd0000854a1986.jpg; t=3dd84a3117737e819dd2c32f1cdb91d01;
societyguester=3dd84a3117737e819dd2c32f1cdb91d01; id=443362311; xnsid=169efdc0; loginfrom=syshome; ch_id=10016;
jebe_key=9c062f5a-4335-4a91-bf7a-970f8b86a64e%7Ca022c303305d1b2ab6b5089643e4b5de%7C1511449232839%7C1; wp_fold=0
' } url = 'http://www.renren.com/880151247/profile' req = request.Request(url,headers=headers) resp = request.urlopen(req) with open('renren.html','w') as fp: fp.write(resp.read().decode('utf-8'))

 

可是每次在訪問須要cookie的頁面都要從瀏覽器中複製cookie比較麻煩。在Python處理Cookie,通常是經過http.cookiejar模塊和urllib模塊的HTTPCookieProcessor處理器類一塊兒使用。http.cookiejar模塊主要做用是提供用於存儲cookie的對象。而HTTPCookieProcessor處理器主要做用是處理這些cookie對象,並構建handler對象。

http.cookiejar模塊:

該模塊主要的類有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。這四個類的做用分別以下:

  1. CookieJar:管理HTTP cookie值、存儲HTTP請求生成的cookie、向傳出的HTTP請求添加cookie的對象。整個cookie都存儲在內存中,對CookieJar實例進行垃圾回收後cookie也將丟失。
  2. FileCookieJar (filename,delayload=None,policy=None):從CookieJar派生而來,用來建立FileCookieJar實例,檢索cookie信息並將cookie存儲到文件中。filename是存儲cookie的文件名。delayload爲True時支持延遲訪問訪問文件,即只有在須要時纔讀取文件或在文件中存儲數據。
  3. MozillaCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與Mozilla瀏覽器 cookies.txt兼容的FileCookieJar實例。
  4. LWPCookieJar (filename,delayload=None,policy=None):從FileCookieJar派生而來,建立與libwww-perl標準的 Set-Cookie3 文件格式兼容的FileCookieJar實例。

登陸人人網:

利用http.cookiejarrequest.HTTPCookieProcessor登陸人人網。相關示例代碼以下:

from urllib import request,parse
from http.cookiejar import CookieJar

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}

def get_opener():
    cookiejar = CookieJar()
    handler = request.HTTPCookieProcessor(cookiejar)
    opener = request.build_opener(handler)
    return opener

def login_renren(opener):
    data = {"email": "970138074@qq.com", "password": "pythonspider"}
    data = parse.urlencode(data).encode('utf-8')
    login_url = "http://www.renren.com/PLogin.do"
    req = request.Request(login_url, headers=headers, data=data)
    opener.open(req)

def visit_profile(opener):
    url = 'http://www.renren.com/880151247/profile'
    req = request.Request(url,headers=headers)
    resp = opener.open(req)
    with open('renren.html','w') as fp:
        fp.write(resp.read().decode("utf-8"))

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

 

保存cookie到本地:

保存cookie到本地,可使用cookiejarsave方法,而且須要指定一個文件名:

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; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
req = request.Request('http://httpbin.org/cookies',headers=headers)

resp = opener.open(req)
print(resp.read())
cookiejar.save(ignore_discard=True,ignore_expires=True)

 

從本地加載cookie:

從本地加載cookie,須要使用cookiejarload方法,而且也須要指定方法:

from urllib import request
from http.cookiejar import MozillaCookieJar

cookiejar = MozillaCookieJar("cookie.txt")
cookiejar.load(ignore_expires=True,ignore_discard=True)
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
req = request.Request('http://httpbin.org/cookies',headers=headers)

resp = opener.open(req)
print(resp.read())

 

第四節 requests庫

雖然Python的標準庫中 urllib模塊已經包含了日常咱們使用的大多數功能,可是它的 API 使用起來讓人感受不太好,而 Requests宣傳是 「HTTP for Humans」,說明使用更簡潔方便。

安裝和文檔地址:

利用pip能夠很是方便的安裝:

pip install requests

 

中文文檔:http://docs.python-requests.org/zh_CN/latest/index.html

github地址:https://github.com/requests/requests

發送GET請求:

  1. 最簡單的發送get請求就是經過requests.get來調用:

    response = requests.get("http://www.baidu.com/")

     

  2. 添加headers和查詢參數: 若是想添加 headers,能夠傳入headers參數來增長請求頭中的headers信息。若是要將參數放在url中傳遞,能夠利用 params 參數。相關示例代碼以下:

     import requests
    
     kw = {'wd':'中國'}
    
     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"}
    
     # params 接收一個字典或者字符串的查詢參數,字典類型自動轉換爲url編碼,不須要urlencode()
     response = requests.get("http://www.baidu.com/s", params = kw, headers = headers)
    
     # 查看響應內容,response.text 返回的是Unicode格式的數據
     print(response.text)
    
     # 查看響應內容,response.content返回的字節流數據
     print(response.content)
    
     # 查看完整url地址
     print(response.url)
    
     # 查看響應頭部字符編碼
     print(response.encoding)
    
     # 查看響應碼
     print(response.status_code)

    註釋:response.text和response.content的區別:                                                                                                                                     1.response.text:這是個str的數據類型,是requests庫將response.content進行解碼的字符串,解碼須要制定一個編碼方式,requests會根據本身的猜想來判斷編碼的方式。因此有時候可能會有猜想錯誤,就會致使解碼產生亂碼,這時候就應該使用response.content.decode('utf-8')進行手動解碼。                                                                                                                                                                            2.response.content:這個是直接從網絡上面抓取的數據。沒有通過任何解碼。因此是一個bytes類型。其實在硬盤上和網絡上傳輸的字符串都是bytes類型。

發送POST請求:

  1. 最基本的POST請求可使用post方法:

    response = requests.post("http://www.baidu.com/",data=data)

     

  2. 傳入data數據: 這時候就不要再使用urlencode進行編碼了,直接傳入一個字典進去就能夠了。好比請求拉勾網的數據的代碼:

    import requests
    
     url = "https://www.lagou.com/jobs/positionAjax.json?city=%E6%B7%B1%E5%9C%B3&needAddtionalResult=false&isSchoolJob=0"
    
     headers = {
         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
         'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='
     }
    
     data = {
         'first': 'true',
         'pn': 1,
         'kd': 'python'
     }
    
     resp = requests.post(url,headers=headers,data=data)
     # 若是是json數據,直接能夠調用json方法
     print(resp.json())

    註釋:發送post請求很是簡單,直接調用「requests.post」方法就好了。若是返回的是json數據,那麼能夠調用「response.json()」來將json字符串轉換爲字典或者列表。 

使用代理:

使用requests添加代理也很是簡單,只要在請求的方法中(好比get或者post)傳遞proxies參數就能夠了。示例代碼以下:

import requests

url = "http://httpbin.org/get"

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
}

proxy = {
    'http': '171.14.209.180:27829'
}

resp = requests.get(url,headers=headers,proxies=proxy)
with open('xx.html','w',encoding='utf-8') as fp:
    fp.write(resp.text)

 註釋:在請求方法中,傳遞「proxies」參數就能夠了。

cookie:

若是在一個響應中包含了cookie,那麼能夠利用cookies屬性拿到這個返回的cookie值:

import requests

url = "http://www.renren.com/PLogin.do"
data = {"email":"970138074@qq.com",'password':"pythonspider"}
resp = requests.get('http://www.baidu.com/')
print(resp.cookies)
print(resp.cookies.get_dict())

 註釋:若是想要在屢次請求中共享cookie,那麼應該使用session。

session:

以前使用urllib庫,是可使用opener發送多個請求,多個請求之間是能夠共享cookie的。那麼若是使用requests,也要達到共享cookie的目的,那麼可使用requests庫給咱們提供的session對象。注意,這裏的session不是web開發中的那個session,這個地方只是一個會話的對象而已。仍是以登陸人人網爲例,使用requests來實現。示例代碼以下:

import requests

url = "http://www.renren.com/PLogin.do"
data = {"email":"970138074@qq.com",'password':"pythonspider"}
headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
}

# 登陸
session = requests.session()
session.post(url,data=data,headers=headers)

# 訪問大鵬我的中心
resp = session.get('http://www.renren.com/880151247/profile')

print(resp.text)

 

處理不信任的SSL證書:

對於那些已經被信任的SSL整數的網站,好比https://www.baidu.com/,那麼使用requests直接就能夠正常的返回響應。示例代碼以下:

resp = requests.get('http://www.12306.cn/mormhweb/',verify=False)
print(resp.content.decode('utf-8'))
相關文章
相關標籤/搜索