爬蟲基礎(理論基礎)--01

世界上80%的爬蟲是基於Python開發的,學好爬蟲技能,可爲後續的大數據分析、挖掘、機器學習等提供重要的數據源。php

整理這個文檔資料但願能對小夥伴有幫助。css

 

什麼是爬蟲?

網絡爬蟲(又被稱爲網頁蜘蛛,網絡機器人,在FOAF社區中間,更常常的稱爲網頁追逐者),是一種按照必定的規則,自動地抓取萬維網信息的程序或者腳本。另一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。html

其實通俗的講就是經過程序去獲取web頁面上本身想要的數據,也就是自動抓取數據python

爬蟲能夠作什麼?

你能夠爬取妹子的圖片,爬取本身想看看的視頻。。等等你想要爬取的數據,只要你能經過瀏覽器訪問的數據均可以經過爬蟲獲取mysql

爬蟲的本質是什麼?

模擬瀏覽器打開網頁,獲取網頁中咱們想要的那部分數據web

瀏覽器打開網頁的過程:
當你在瀏覽器中輸入地址後,通過DNS服務器找到服務器主機,向服務器發送一個請求,服務器通過解析後發送給用戶瀏覽器結果,包括html,js,css等文件內容,瀏覽器解析出來最後呈現給用戶在瀏覽器上看到的結果ajax

因此用戶看到的瀏覽器的結果就是由HTML代碼構成的,咱們爬蟲就是爲了獲取這些內容,經過分析和過濾html代碼,從中獲取咱們想要資源(文本,圖片,視頻.....)正則表達式

在上文中咱們說了:爬蟲就是請求網站並提取數據的自動化程序。其中請求,提取,自動化是爬蟲的關鍵!下面咱們分析爬蟲的基本流程sql

爬蟲的基本流程

發起請求
經過HTTP庫向目標站點發起請求,也就是發送一個Request,請求能夠包含額外的header等信息,等待服務器響應數據庫

獲取響應內容
若是服務器能正常響應,會獲得一個Response,Response的內容即是所要獲取的頁面內容,類型多是HTML,Json字符串,二進制數據(圖片或者視頻)等類型

解析內容
獲得的內容多是HTML,能夠用正則表達式,頁面解析庫進行解析,若是是Json,能夠直接轉換爲Json對象解析,多是二進制數據,能夠作保存或者進一步的處理

保存數據
保存形式多樣,能夠存爲文本,也能夠保存到數據庫,或者保存特定格式的文件

什麼是Request,Response

瀏覽器發送消息給網址所在的服務器,這個過程就叫作HTPP Request

服務器收到瀏覽器發送的消息後,可以根據瀏覽器發送消息的內容,作相應的處理,而後把消息回傳給瀏覽器,這個過程就是HTTP Response

瀏覽器收到服務器的Response信息後,會對信息進行相應的處理,而後展現

Request中包含什麼?

請求方式

主要有:GET/POST兩種類型經常使用,另外還有HEAD/PUT/DELETE/OPTIONS
GET和POST的區別就是:請求的數據GET是在url中,POST則是存放在頭部

GET:向指定的資源發出「顯示」請求。使用GET方法應該只用在讀取數據,而不該當被用於產生「反作用」的操做中,例如在Web Application中。其中一個緣由是GET可能會被網絡蜘蛛等隨意訪問

POST:向指定資源提交數據,請求服務器進行處理(例如提交表單或者上傳文件)。數據被包含在請求本文中。這個請求可能會建立新的資源或修改現有資源,或兩者皆有。

HEAD:與GET方法同樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法能夠在沒必要傳輸所有內容的狀況下,就能夠獲取其中「關於該資源的信息」(元信息或稱元數據)。

PUT:向指定資源位置上傳其最新內容。

OPTIONS:這個方法可以使服務器傳回該資源所支持的全部HTTP請求方法。用'*'來代替資源名稱,向Web服務器發送OPTIONS請求,能夠測試服務器功能是否正常運做。

DELETE:請求服務器刪除Request-URI所標識的資源。

請求URL

URL,即統一資源定位符,也就是咱們說的網址,統一資源定位符是對能夠從互聯網上獲得的資源的位置和訪問方法的一種簡潔的表示,是互聯網上標準資源的地址。互聯網上的每一個文件都有一個惟一的URL,它包含的信息指出文件的位置以及瀏覽器應該怎麼處理它。

URL的格式由三個部分組成:
第一部分是協議(或稱爲服務方式)。
第二部分是存有該資源的主機IP地址(有時也包括端口號)。
第三部分是主機資源的具體地址,如目錄和文件名等。

爬蟲爬取數據時必需要有一個目標的URL才能夠獲取數據,所以,它是爬蟲獲取數據的基本依據。

請求頭

包含請求時的頭部信息,如User-Agent,Host,Cookies等信息,下圖是請求請求百度時,全部的請求頭部信息參數

請求體
請求是攜帶的數據,如提交表單數據時候的表單數據(POST)

Response中包含了什麼

全部HTTP響應的第一行都是狀態行,依次是當前HTTP版本號,3位數字組成的狀態代碼,以及描述狀態的短語,彼此由空格分隔。

響應狀態

有多種響應狀態,如:200表明成功,301跳轉,404找不到頁面,502服務器錯誤

  • 1xx消息——請求已被服務器接收,繼續處理
  • 2xx成功——請求已成功被服務器接收、理解、並接受
  • 3xx重定向——須要後續操做才能完成這一請求
  • 4xx請求錯誤——請求含有詞法錯誤或者沒法被執行
  • 5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤
  • 常見代碼:

200 OK 請求成功

400 Bad Request 客戶端請求有語法錯誤,不能被服務器所理解

401 Unauthorized 請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用

403 Forbidden 服務器收到請求,可是拒絕提供服務

404 Not Found 請求資源不存在,eg:輸入了錯誤的URL

500 Internal Server Error 服務器發生不可預期的錯誤

503 Server Unavailable 服務器當前不能處理客戶端的請求,一段時間後可能恢復正常

301 目標永久性轉移 

302 目標暫時性轉移

響應頭

如內容類型,類型的長度,服務器信息,設置Cookie,以下圖

響應體

最主要的部分,包含請求資源的內容,如網頁HTMl,圖片,二進制數據等

能爬取什麼樣的數據

網頁文本:如HTML文檔,Json格式化文本等
圖片:獲取到的是二進制文件,保存爲圖片格式
視頻:一樣是二進制文件
其餘:只要請求到的,均可以獲取

如何解析數據

  1. 直接處理
  2. Json解析
  3. 正則表達式處理
  4. BeautifulSoup解析處理
  5. PyQuery解析處理
  6. XPath解析處理

關於抓取的頁面數據和瀏覽器裏看到的不同的問題

出現這種狀況是由於,不少網站中的數據都是經過js,ajax動態加載的,因此直接經過get請求獲取的頁面和瀏覽器顯示的不一樣。

如何解決js渲染的問題?

分析ajax
Selenium/webdriver
Splash
PyV8,Ghost.py

怎樣保存數據

文本:純文本,Json,Xml等

關係型數據庫:如mysql,oracle,sql server等結構化數據庫

非關係型數據庫:MongoDB,Redis等key-value形式存儲

官方文檔地址:https://docs.python.org/3/library/urllib.html

什麼是Urllib

Urllib是python內置的HTTP請求庫
包括如下模塊
urllib.request 請求模塊
urllib.error 異常處理模塊
urllib.parse url解析模塊
urllib.robotparser robots.txt解析模塊

urlopen

關於urllib.request.urlopen參數的介紹:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

url參數的使用

先寫一個簡單的例子:

import urllib.request

response = urllib.request.urlopen('http://www.baidu.com')
print(response.read().decode('utf-8'))

urlopen通常經常使用的有三個參數,它的參數以下:
urllib.requeset.urlopen(url,data,timeout)
response.read()能夠獲取到網頁的內容,若是沒有read(),將返回以下內容

data參數的使用

上述的例子是經過請求百度的get請求得到百度,下面使用urllib的post請求
這裏經過http://httpbin.org/post網站演示(該網站能夠做爲練習使用urllib的一個站點使用,能夠
模擬各類請求操做)。

複製代碼
 
import urllib.parse
import urllib.request

data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')
print(data)
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read())
 
複製代碼

這裏就用到urllib.parse,經過bytes(urllib.parse.urlencode())能夠將post數據進行轉換放到urllib.request.urlopen的data參數中。這樣就完成了一次post請求。
因此若是咱們添加data參數的時候就是以post請求方式請求,若是沒有data參數就是get請求方式

timeout參數的使用
在某些網絡狀況很差或者服務器端異常的狀況會出現請求慢的狀況,或者請求異常,因此這個時候咱們須要給
請求設置一個超時時間,而不是讓程序一直在等待結果。例子以下:

import urllib.request

response = urllib.request.urlopen('http://httpbin.org/get', timeout=1)
print(response.read())

運行以後咱們看到能夠正常的返回結果,接着咱們將timeout時間設置爲0.1
運行程序會提示以下錯誤

因此咱們須要對異常進行抓取,代碼更改成

複製代碼
 
import socket
import urllib.request
import urllib.error

try:
    response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
    if isinstance(e.reason, socket.timeout):
        print('TIME OUT')
 
複製代碼

響應

響應類型、狀態碼、響應頭

import urllib.request

response = urllib.request.urlopen('https://www.python.org')
print(type(response))

能夠看到結果爲:<class 'http.client.httpresponse'="">
咱們能夠經過response.status、response.getheaders().response.getheader("server"),獲取狀態碼以及頭部信息
response.read()得到的是響應體的內容

固然上述的urlopen只能用於一些簡單的請求,由於它沒法添加一些header信息,若是後面寫爬蟲咱們能夠知道,不少狀況下咱們是須要添加頭部信息去訪問目標站的,這個時候就用到了urllib.request

request

設置Headers
有不少網站爲了防止程序爬蟲爬網站形成網站癱瘓,會須要攜帶一些headers頭部信息才能訪問,最長見的有user-agent參數

寫一個簡單的例子:

import urllib.request

request = urllib.request.Request('https://python.org')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

給請求添加頭部信息,從而定製本身請求網站是時的頭部信息

複製代碼
 
from urllib import request, parse

url = 'http://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
    'Host': 'httpbin.org'
}
dict = {
    'name': 'alex'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
 
複製代碼

添加請求頭的第二種方式

複製代碼
 
from urllib import request, parse

url = 'http://httpbin.org/post'
dict = {
    'name': 'Germey'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
 
複製代碼

這種添加方式有個好處是本身能夠定義一個請求頭字典,而後循環進行添加

高級用法各類handler

代理,ProxyHandler

經過rulllib.request.ProxyHandler()能夠設置代理,網站它會檢測某一段時間某個IP 的訪問次數,若是訪問次數過多,它會禁止你的訪問,因此這個時候須要經過設置代理來爬取數據

複製代碼
 
import urllib.request

proxy_handler = urllib.request.ProxyHandler({
    'http': 'http://127.0.0.1:9743',
    'https': 'https://127.0.0.1:9743'
})
opener = urllib.request.build_opener(proxy_handler)
response = opener.open('http://httpbin.org/get')
print(response.read())
 
複製代碼

cookie,HTTPCookiProcessor

cookie中保存中咱們常見的登陸信息,有時候爬取網站須要攜帶cookie信息訪問,這裏用到了http.cookijar,用於獲取cookie以及存儲cookie

複製代碼
 
import http.cookiejar, urllib.request
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
    print(item.name+"="+item.value)
 
複製代碼

同時cookie能夠寫入到文件中保存,有兩種方式http.cookiejar.MozillaCookieJar和http.cookiejar.LWPCookieJar(),固然你本身用哪一種方式均可以

具體代碼例子以下:
http.cookiejar.MozillaCookieJar()方式

複製代碼
 
import http.cookiejar, urllib.request
filename = "cookie.txt"
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)
 
複製代碼

http.cookiejar.LWPCookieJar()方式

複製代碼
 
import http.cookiejar, urllib.request
filename = 'cookie.txt'
cookie = http.cookiejar.LWPCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)
 
複製代碼

一樣的若是想要經過獲取文件中的cookie獲取的話能夠經過load方式,固然用哪一種方式寫入的,就用哪一種方式讀取。

複製代碼
 
import http.cookiejar, urllib.request
cookie = http.cookiejar.LWPCookieJar()
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))
 
複製代碼

異常處理

在不少時候咱們經過程序訪問頁面的時候,有的頁面可能會出現錯誤,相似404,500等錯誤
這個時候就須要咱們捕捉異常,下面先寫一個簡單的例子

複製代碼
 
from urllib import request,error

try:
    response = request.urlopen("http://pythonsite.com/1111.html")
except error.URLError as e:
    print(e.reason)
 
複製代碼

上述代碼訪問的是一個不存在的頁面,經過捕捉異常,咱們能夠打印異常錯誤

這裏咱們須要知道的是在urllb異常這裏有兩個個異常錯誤:
URLError,HTTPError,HTTPError是URLError的子類

URLError裏只有一個屬性:reason,即抓異常的時候只能打印錯誤信息,相似上面的例子

HTTPError裏有三個屬性:code,reason,headers,即抓異常的時候能夠得到code,reson,headers三個信息,例子以下:

複製代碼
 
from urllib import request,error
try:
    response = request.urlopen("http://pythonsite.com/1111.html")
except error.HTTPError as e:
    print(e.reason)
    print(e.code)
    print(e.headers)
except error.URLError as e:
    print(e.reason)

else:
    print("reqeust successfully")
 
複製代碼

同時,e.reason其實也能夠在作深刻的判斷,例子以下:

複製代碼
 
import socket

from urllib import error,request

try:
    response = request.urlopen("http://www.pythonsite.com/",timeout=0.001)
except error.URLError as e:
    print(type(e.reason))
    if isinstance(e.reason,socket.timeout):
        print("time out")
 
複製代碼

URL解析

urlparse
The URL parsing functions focus on splitting a URL string into its components, or on combining URL components into a URL string.

urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

功能一:

from urllib.parse import urlparse

result = urlparse("http://www.baidu.com/index.html;user?id=5#comment")
print(result)

結果爲:

這裏就是能夠對你傳入的url地址進行拆分
同時咱們是能夠指定協議類型:
result = urlparse("www.baidu.com/index.html;user?id=5#comment",scheme="https")
這樣拆分的時候協議類型部分就會是你指定的部分,固然若是你的url裏面已經帶了協議,你再經過scheme指定的協議就不會生效

urlunpars

其實功能和urlparse的功能相反,它是用於拼接,例子以下:

from urllib.parse import urlunparse

data = ['http','www.baidu.com','index.html','user','a=123','commit']
print(urlunparse(data))

結果以下

urljoin

這個的功能實際上是作拼接的,例子以下:

複製代碼
 
from urllib.parse import urljoin

print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com', 'https://pythonsite.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://pythonsite.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://pythonsite.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://pythonsite.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))
 
複製代碼

結果爲:

從拼接的結果咱們能夠看出,拼接的時候後面的優先級高於前面的url

urlencode
這個方法能夠將字典轉換爲url參數,例子以下

複製代碼
 
from urllib.parse import urlencode

params = {
    "name":"alex",
    "age":23,
}
base_url = "http://www.baidu.com?"

url = base_url+urlencode(params)
print(url)
 
複製代碼

結果爲:

相關文章
相關標籤/搜索