1. 程式人生 > 實用技巧 >爬蟲基本原理

爬蟲基本原理

爬蟲是什麼

#1、什麼是網際網路?
    網際網路是由網路裝置(網線,路由器,交換機,防火牆等等)和一臺臺計算機連線而成,像一張網一樣。

#2、網際網路建立的目的?
    網際網路的核心價值在於資料的共享/傳遞:資料是存放於一臺臺計算機上的,而將計算機互聯到一起的目的就是為了能夠方便彼此之間的資料共享/傳遞,否則你只能拿U盤去別人的計算機上拷貝資料了。

#3、什麼是上網?爬蟲要做的是什麼?
    我們所謂的上網便是由使用者端計算機發送請求給目標計算機,將目標計算機的資料下載到本地的過程。
    #3.1 只不過,使用者獲取網路資料的方式是:
      瀏覽器提交請求->下載網頁程式碼->解析/渲染成頁面。

    #3.2 而爬蟲程式要做的就是:
      模擬瀏覽器傳送請求->下載網頁程式碼->只提取有用的資料->存放於資料庫或檔案中
 
    #3.1與3.2的區別在於:
      我們的爬蟲程式只提取網頁程式碼中對我們有用的資料

#4、總結爬蟲
    #4.1 爬蟲的比喻:
      如果我們把網際網路比作一張大的蜘蛛網,那一臺計算機上的資料便是蜘蛛網上的一個獵物,而爬蟲程式就是一隻小蜘蛛,沿著蜘蛛網抓取自己想要的獵物/資料

    #4.2 爬蟲的定義:
      向網站發起請求,獲取資源後分析並提取有用資料的程式 

    #4.3 爬蟲的價值:
      網際網路中最有價值的便是資料,比如天貓商城的商品資訊,鏈家網的租房資訊,雪球網的證券投資資訊等等,這些資料都代表了各個行業的真金白銀,可以說,誰掌握了行業內的第一手資料,誰就成了整個行業的主宰,如果把整個網際網路的資料比喻為一座寶藏,那我們的爬蟲課程就是來教大家如何來高效地挖掘這些寶藏,掌握了爬蟲技能,你就成了所有網際網路資訊公司幕後的老闆,換言之,它們都在免費為你提供有價值的資料。

爬蟲的基本流程

![](_v_images/20200731172925192_7802.png =750x)

#1、發起請求
使用http庫向目標站點發起請求,即傳送一個Request
Request包含:請求頭、請求體等

#2、獲取響應內容
如果伺服器能正常響應,則會得到一個Response
Response包含:html,json,圖片,視訊等

#3、解析內容
解析html資料:正則表示式,第三方解析庫如Beautifulsoup,pyquery等
解析json資料:json模組
解析二進位制資料:以b的方式寫入檔案

#4、儲存資料
資料庫
檔案

請求與響應

#http協議:http://www.cnblogs.com/linhaifeng/articles/8243379.html#Request:使用者將自己的資訊通過瀏覽器(socket client)傳送給伺服器(socket server)

#Response:伺服器接收請求,分析使用者發來的請求資訊,然後返回資料(返回的資料中可能包含其他連結,如:圖片,js,css等)

#ps:瀏覽器在接收Response後,會解析其內容來顯示給使用者,而爬蟲程式在模擬瀏覽器傳送請求然後接收Response後,是要提取其中的有用資料。

Request

#1、請求方式:
    常用的請求方式:GET,POST
    其他請求方式:HEAD,PUT,DELETE,OPTHONS

    ps:用瀏覽器演示get與post的區別,(用登入演示post)

    post與get請求最終都會拼接成這種形式:k1=xxx&k2=yyy&k3=zzz
    post請求的引數放在請求體內:
        可用瀏覽器檢視,存放於form data內
    get請求的引數直接放在url後

#2、請求url
    url全稱統一資源定位符,如一個網頁文件,一張圖片
    一個視訊等都可以用url唯一來確定

    url編碼
    https://www.baidu.com/s?wd=圖片
    圖片會被編碼(看示例程式碼)


    網頁的載入過程是:
    載入一個網頁,通常都是先載入document文件,
    在解析document文件的時候,遇到連結,則針對超連結發起下載圖片的請求

#3、請求頭
    User-agent:請求頭中如果沒有user-agent客戶端配置,
    服務端可能將你當做一個非法使用者
    host
    cookies:cookie用來儲存登入資訊

    一般做爬蟲都會加上請求頭


#4、請求體
    如果是get方式,請求體沒有內容
    如果是post方式,請求體是format data

    ps:
    1、登入視窗,檔案上傳等,資訊都會被附加到請求體內
    2、登入,輸入錯誤的使用者名稱密碼,然後提交,就可以看到post,正確登入後頁面通常會跳轉,無法捕捉到post
from urllib.parse import urlencode
import requests

headers={
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Cookie':'cookie',
'Host':'www.baidu.com',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}

# response=requests.get('https://www.baidu.com/s?'+urlencode({'wd':'美女'}),headers=headers)
response=requests.get('https://www.baidu.com/s',params={'wd':'美女'},headers=headers) #params內部就是呼叫urlencode
print(response.text)

Response

#1、響應狀態
    200:代表成功
    301:代表跳轉
    404:檔案不存在
    403:許可權
    502:伺服器錯誤

#2、Respone header
    set-cookie:可能有多個,是來告訴瀏覽器,把cookie儲存下來
    
#3、preview就是網頁原始碼
    最主要的部分,包含了請求資源的內容
    如網頁html,圖片
    二進位制資料等

總結

#1、總結爬蟲流程:
    爬取--->解析--->儲存

#2、爬蟲所需工具:
    請求庫:requests,selenium
    解析庫:正則,beautifulsoup,pyquery
    儲存庫:檔案,MySQL,Mongodb,Redis

#3、爬蟲常用框架:
    scrapy

校花網爬取(已廢,僅供參考)

import requests
import re
import time
import hashlib

def get_page(url):
    print('GET %s' %url)
    try:
        response=requests.get(url)
        if response.status_code == 200:
            return response.content
    except Exception:
        pass

def parse_index(res):
    obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
    detail_urls=obj.findall(res.decode('gbk'))
    for detail_url in detail_urls:
        if not detail_url.startswith('http'):
            detail_url='http://www.xiaohuar.com'+detail_url
        yield detail_url

def parse_detail(res):
    obj=re.compile('id="media".*?src="(.*?)"',re.S)
    res=obj.findall(res.decode('gbk'))
    if len(res) > 0:
        movie_url=res[0]
        return movie_url


def save(movie_url):
    response=requests.get(movie_url,stream=False)
    if response.status_code == 200:
        m=hashlib.md5()
        m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
        filename=m.hexdigest()
        with open(r'./movies/%s.mp4' %filename,'wb') as f:
            f.write(response.content)
            f.flush()


def main():
    index_url='http://www.xiaohuar.com/list-3-{0}.html'
    for i in range(5):
        print('*'*50,i)
        #爬取主頁面
        index_page=get_page(index_url.format(i,))
        #解析主頁面,拿到視訊所在的地址列表
        detail_urls=parse_index(index_page)
        #迴圈爬取視訊頁
        for detail_url in detail_urls:
            #爬取視訊頁
            detail_page=get_page(detail_url)
            #拿到視訊的url
            movie_url=parse_detail(detail_page)
            if movie_url:
                #儲存視訊
                save(movie_url)


if __name__ == '__main__':
    main()


#併發爬取
from concurrent.futures import ThreadPoolExecutor
import queue
import requests
import re
import time
import hashlib
from threading import current_thread

p=ThreadPoolExecutor(50)

def get_page(url):
    print('%s GET %s' %(current_thread().getName(),url))
    try:
        response=requests.get(url)
        if response.status_code == 200:
            return response.content
    except Exception as e:
        print(e)

def parse_index(res):
    print('%s parse index ' %current_thread().getName())
    res=res.result()
    obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
    detail_urls=obj.findall(res.decode('gbk'))
    for detail_url in detail_urls:
        if not detail_url.startswith('http'):
            detail_url='http://www.xiaohuar.com'+detail_url
        p.submit(get_page,detail_url).add_done_callback(parse_detail)

def parse_detail(res):
    print('%s parse detail ' %current_thread().getName())
    res=res.result()
    obj=re.compile('id="media".*?src="(.*?)"',re.S)
    res=obj.findall(res.decode('gbk'))
    if len(res) > 0:
        movie_url=res[0]
        print('MOVIE_URL: ',movie_url)
        with open('db.txt','a') as f:
            f.write('%s\n' %movie_url)
        # save(movie_url)
        p.submit(save,movie_url)
        print('%s下載任務已經提交' %movie_url)
def save(movie_url):
    print('%s SAVE: %s' %(current_thread().getName(),movie_url))
    try:
        response=requests.get(movie_url,stream=False)
        if response.status_code == 200:
            m=hashlib.md5()
            m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
            filename=m.hexdigest()
            with open(r'./movies/%s.mp4' %filename,'wb') as f:
                f.write(response.content)
                f.flush()
    except Exception as e:
        print(e)

def main():
    index_url='http://www.xiaohuar.com/list-3-{0}.html'
    for i in range(5):
        p.submit(get_page,index_url.format(i,)).add_done_callback(parse_index)


if __name__ == '__main__':
    main()

爬取校花網視訊