前端程式設計師學python(爬蟲向)(一文修到築基期) (本文不含知識詛咒)

2022-12-23 18:00:27

我踏馬來辣

還有一件事:

  1. 本教學配合c語言中文網 python爬蟲 教學 食用
  2. 本教學不適用於未成年人
  3. 一定要刷牙
  4. 本教學不存在知識詛咒
  5. 學完本教學即可進入築基期
  6. js 基礎和本教學學習效率成正比
  7. 不要笑
  8. 暫時不要駕駛你的行李箱
  9. 本教學是針對前端程式設計師制定的
  10. vscode是IDE大王,什麼?你說vscode不是IDE?

令我驚喜的是 py和js有很多相似的地方 甚至作為一個前端程式設計師,在看到python程式碼的那一刻,倍感親切

  • 都是指令碼語言
  • 都有弱型別變數
  • 都不用寫分號,當然也可以寫
  • 都有自己的包管理庫 py是 pip , js是 npm 參見 npm 和 pip 介紹

本來我覺得js已經夠自由了,弱型別變數、以回車結束語句、簡潔明瞭又極度自由的語法,而python程式碼則更為簡潔

如果你也是正在學習Python的前端程式設計師,那麼你一定跟我感同身受

py定義變數時甚至不需要 關鍵詞 (js是 let var),這個東西甚至簡潔到讓我懷疑它是不是真的能用

可能寫C++的人看到 js 程式碼也會有這種疑惑

相信看到這裡,你和我有了一個共同的問題:既然簡潔到這種程度,那我想定義一個常數該怎麼辦?牛逼的事情來了,python摒棄了常數這個東西,我覺得是py認為常數太不純粹,我為什麼需要一個不能修改的值?於是,python沒有常數,如果你實在需要一個常數,也可以 通過其他方式實現

如果你的程式設計生涯也是從切圖仔做起的,不懂什麼資料結構、計算機原理、編譯原理,那麼你可能會對下面的程式碼產生疑惑

import urllib.request

和 js 相比較,這個寫法簡直奇怪到姥姥家了,主要的疑問有兩點:1.我從沒有下載過這個包,怎麼引入成功了?2.我怎麼知道哪些包可以參照?參見 Python是如何找包的,Python安裝的包放在哪裡 ,當然的然,沒有下載過一個包,或者py沒有自帶這個包,也就是說,你本地沒有這個包的情況下,也是不能引入的,只是py的直譯器會自動在設定好的路徑中去查詢這個包。

就比如 在標準npm專案中 的使用import引入,會自動去node_modules中查詢一樣

在js中,我想實現某個相較複雜的功能,會先去npm上搜尋有沒有可用包,這是正常操作。在py中,也有類似的操作,且py能幹的事情不再侷限在web的前後端上,所以py的包幾乎涵蓋了所有領域,這也是py崛起的原因之一。作為一個py新手,如果你想實現一個功能,不妨先去pip上搜一下。

我看到的py爬蟲教學,第一個程式是這麼寫的

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

顯然,如果你有一點任何語言的基礎,那麼這幾行程式碼根本不需要註釋,py十分貼近自然語言,這也是我為什麼選擇py作為第二語言的原因。

對與上面的程式碼,需要注意的點有:

  1. 引入時不是以斜槓而是以點表示下級的,而且不需要引號或括號
  2. 定義變數不需要關鍵字 像上面的 response = 和 html = 都是變數的定義(py中可以直接叫賦值,因為其實就是賦值操作,如果想定義一個空變數可以使其 = null)
  3. 還可以這麼寫 from urllib import request (甚至接近中文表達方式,和es6相反)

本節教學:http://c.biancheng.net/python_spider/the-first-spider.html

再多一點點

半路出家的前端人,對網路協定、網路請求也知之甚少,要學習爬蟲,對此要有進一步的瞭解。首先,各大網站都對爬蟲做了一些限制,比如監測到爬蟲頻繁存取的話,有可能對該ip進行短時間的封鎖,比如常見的:「操作頻繁」提示,就是對ip的一種封鎖,在前端的眼裡,這個操作叫節流和防抖,在py爬蟲上,叫做反爬。

網站對爬蟲做出限制,其實是很好理解的,因為不同於普通的瀏覽器使用者(真人),爬蟲有能力大量頻繁的存取,可能會對伺服器造成負擔。當然,也不是不希望爬蟲去存取,相反的,很多網站其實是歡迎爬蟲的,因為這有利於在百度等搜尋引擎的關鍵詞排名,也就是常常聽說的SEO。總之就是一句話,想讓你爬,但不想讓你會會的爬,年輕人不要太氣盛。

不氣盛還叫年輕人嗎?

我們知道,一個大型網站的存取的數量級是很大的,爬蟲,多我一個不多,少我一個不少。我如果就想不斷地、頻繁的存取怎麼辦?User-Agent,使用者代理。如果你玩手機時,在手機瀏覽器裡曾經見過一個叫UA的選項,裡面可以選擇PC、Iphone、Android,那麼恭喜你,因為你早就認識它了,網站反爬時,識別UA是一個基本手段。UA是一個字串,可以告知你存取的網站你使用的的軟體基本資訊 參見 User-Agent(使用者代理)是什麼

不斷地變換你的UA就可以達到欺騙反爬程式的效果。

本節教學 http://c.biancheng.net/python_spider/useragent-pool.html

自定義UA代理池,嚴先生(介紹一下,他是C語言中文網的站長,記住了,下面不再介紹了)為我們列舉了幾個常用的UA字串,並把它放在了一個新建檔案裡。

問題來了,引入pip包我會了,但是自己建的檔案我卻不會,這沒什麼可擔心的,這種事情一聽就很基礎,參見 python引入其他資料夾裡的py檔案的方法,引入後,接下來的你就會了。

嚴先生說了,我們要不斷變換我們的UA值,那麼我就想著取一個亂數,然後使用這個亂數作為陣列的下標值,取出資料,於是我上網搜了:python亂數

看到了這樣一個震驚的東西

  • random.choice(sequence):從特定序列中隨機取一個元素,這裡的序列可以是字串,列表,元組等。

解釋一下sequence:我也不懂,搜了一下,叫序列,應該跟陣列是一種東西,注意,這個東西不是任何專業術語,這是個單詞,python中的序列型別包括:list, tuple 和 range 物件。

關於python序列參見 https://blog.csdn.net/weixin_42214654/article/details/114411515

淺看一眼,我得知python中沒有陣列,和陣列最類似的就是 list ,基本上等於js中的Array,但是稍微有點區別

參見 https://blog.csdn.net/weixin_30834019/article/details/97694533

綜上所述,現在來綜合試一下,檔案引入,和隨機取元素

import uaList #這是我自定義的那個檔案
import random #如果你沒有引入這個東西,會自動提示引入的
headers = {
    'User-Agent': random.choice(uaList.ua_list)
}

很顯然,我寫的沒有問題,每次都能取到一個隨機的值。

哎等會,現在發生了一件很奇怪的事情

我乾乾淨淨的目錄出現了一個命名方式令人抓狂的、不認識的、裡邊檔案打不開的資料夾!能不能刪除,我替你們試了,可以刪,但是刪掉之後一執行還會出來。關於這個檔案,參見:pyhton中__pycache__資料夾的產生與作用

我們好像已經做了2178個詞的準備了,但是學python好像不如我想象的那麼迅速,我們寫HTML下一秒就能看到效果,但是python好像不行,耐心點,我們應該馬上可以學會了。

接下來需要了解的是 URL的編碼和解碼。

為什麼URL還需要編碼,編什麼碼,什麼編碼?在我的印象裡,url就是網址,我只知道他是一個網址,或者叫連結,我也不知道怎麼叫才是正規的。

URL 是由一些簡單的元件構成,比如協定、域名、埠號、路徑和查詢字串等,範例如下:

http://www.biancheng.net/index?param=10

顯然 http是網路協定 冒號雙斜槓不知道為什麼要加,但我們都知道要加,後面的問號我們知道是用來分割網址和引數的,我們還知道使用&分割引數與引數。其餘的不知道了。

那麼此時可以聯想到,上述幾個符號:斜槓、冒號、問號、and符號 都是不能被作為正常的字元去解析的,他們被稱為保留字,就像在js中,你不可以把一個變數命名為 let

在url中,保留字需要轉碼,其次,就是不安全字元,像方括號、尖括號、雙引號、大括號、分隔符等,還有,url是用的ASCII碼,也就是說它不支援中文(我們知道支援中文的編碼有 utf-8或者GBK,其他的不知道了),中文也需要轉碼!

等等,我是說,等一下,太多了,我怎麼記得住?

沒關係,要是我們在學別的語言,那確實太多了,但我們學的是python,python啊大哥,python的標準庫中有自動編碼的東西,他媽的,上邊一通白說了,我們要快速麵向業務!

Python實現編碼與解碼

Python 的標準庫urllib.parse模組中提供了用來編碼和解碼的方法,分別是 urlencode() 與 unquote() 方法。

#匯入parse模組
from urllib import parse
#構建查詢字串字典
query_string = {
'wd' : '爬蟲'
}
#呼叫parse模組的urlencode()進行編碼
result = parse.urlencode(query_string)
#使用format函數格式化字串,拼接url地址
url = 'http://www.baidu.com/s?{}'.format(result)
print(url)
#wd=%E7%88%AC%E8%99%AB
#http://www.baidu.com/s?wd=%E7%88%AC%E8%99%AB
result = parse.unquote(url)
print(result)
#爬蟲

好小子,白讓我看這麼多,有自動的我還學什麼,現用現搜。

本節教學:URL編碼/解碼詳解

開始辣

做了2869個詞的準備之後,我們終於要開始爬了。

明確一下,爬蟲程式的三個步驟:

  1. 拼接正確的url(有中文或者特殊字元需要編碼)
  2. 傳送請求
  3. 將獲取到的東西儲存

本節教學:http://c.biancheng.net/python_spider/crawl-webpage.html

根據教學寫完,我們又發現了一些新的東西

py可以很容易的和使用者交換資訊,比如它不必寫一個輸入框或者文字域來讓使用者輸入資訊,py要獲取使用者輸入的字元,只需寫:

input("請輸入內容:")

即可,這個函數會返回使用者輸入的值,咱可以拿一個變數去接受,即 userInput = input("請輸入內容:")

py定義函數很特別,靠縮排來辨別函數體,沒有大括號,所以py的函數長這樣

def getUserInput():
    # 使用者輸入
    global userInput
    userInput = input("請輸入內容:")
    return userInput

一旦縮排出現錯誤,那麼此處也將不屬於這個函數體,你可以自己試一下。

細心的你也許已經發現,上述函數中出現了關鍵字 global ,這是定義全域性變數的方法,雖然定義在函數內部,但是其作用域是全域性的,因為我們後續要對使用者輸入的中文字元進行轉碼,所以這裡做一個全域性變數來儲存它,以便於最後給檔案命名。

細心的你也許又發現,py定義函數的關鍵字是 def ,且因為沒有大括號包裹函數體,它必須以return結束,否則程式無法知道這個函數是否結束,我是這麼認為的。

本節教學寫的程式碼,以及嚴先生整理的,函數式的修改,我都沒看。

所以我寫了如下,跟教學完全不同風格的程式碼,我自認為比較符合 js 思想

import uaList
import random
from urllib import request
from urllib import parse


def getUserInput():
    # 使用者輸入
    global userInput
    userInput = input("請輸入內容:")
    return userInput


def buildHeader():
    # 製作請求頭
    headers = {
        # 隨機在序列中取元素,這個usList是我自定義的檔案
        'User-Agent': random.choice(uaList.ua_list)
    }
    return headers


def makeUrl(url, param):
    # parse.urlencode 和 parse.quote的區別是
    # 前者可以把物件(指js物件的形式,python好像叫集合)的鍵值對變成get請求的鍵值對,也就是 wd=爬蟲 這種形式
    return url.format(parse.urlencode(param))


def getHtmlDoc(url, param):
    req = request.Request(
        url=makeUrl(url, param),
        headers=buildHeader()
    )  # 建立請求
    res = request.urlopen(req)  # 傳送請求
    html = res.read().decode('utf-8')  # 編碼為utf-8,我不知道為什麼
    writeFiles(userInput, html)  # 這裡使用了全域性變數 在定義是使用關鍵字 global (@line:9)
    return


def writeFiles(fileNameIn, data):
    filename = parse.unquote(fileNameIn) + '.html'  # 拼接檔名
    with open(filename, 'w', encoding='utf-8') as f:  # 寫入檔案,這裡我也不懂,好像是py自帶的東西
        f.write(data)
    return


getHtmlDoc("http://www.baidu.com/s?{}", {
    "wd": getUserInput()
})

這次你可能沒這麼細心,我告訴你,倒數第三行在字串中出現了一個大括號,這個大括號在字串被讀取時會被忽略(我認為),其作用是告訴 .format方法,後面拼接的東西該放在哪(我認為).format好像也是py自帶的一個字串處理方法。用法類似 c 語言中的 字串變數格式化

例如

 printf("%s\n", str);

其中 %s 含義為 此處為字串,對應後面的str。

參見:Python format 格式化函數C語言字串的輸入和輸出

其實 ,這種寫法在 c、js、python中都可以使用 參見 JavaScript console.log %c %o %s %d %f

至此,我們已經可以把一個頁面完整的爬下來,上面的一小段程式會將 百度搜尋結果頁生成一個html檔案,在專案根目錄,恭喜各位。

更進一步

本節教學:http://c.biancheng.net/python_spider/case01.html

嚴先生上來就教我們判斷頁面型別,笑話,我們用你教?

尋找URL變化規律,笑話,用你教?

編寫爬蟲程式,笑話...等等,這個跟前面的不太一樣

出現了一個我聽過,都不知道是什麼的東西 class(類),看到這,相信很多大佬要指責我了,js也有類(es6),沒錯,但是我真沒用過,剛剛 看了一下,這個東西還是個物件而已,要不怎麼說js是個基於物件的語言呢?關於js的類,參見 終於,JavaScript也有了類(class)的概念

相信在座各位在公司都是一個被UI牽著鼻子,又被後端打著屁股的美麗小夥。我們只會切圖和調介面啊,什麼類啊,真不懂啊!

其實類沒有什麼複雜的,之所以不懂,是因為適用場景不多,因為沒有很多的邏輯需要類來加持,有時使用一個物件就足夠了,雖然類能很方便的管理變數和方法,但是我們一般還是習慣使用自定義物件來實現業務,對吧?

好的,既然你跟我想的一樣,那麼這節就基本結束了,因為基本邏輯和前面講的是一樣的,在本章程式碼的最後,出現了一個令人迷惑的東西,而嚴先生沒有對此進行解釋(知識詛咒)

if __name__=='__main__': 

對此,請參見: Python中「if name=='main':」理解與總結 ,這個人講的真好,建議給他點個贊。

最後的最後,嚴先生介紹了程式隨機休眠的重要性

爬蟲程式存取網站會非常快,這與正常人類的點選行為非常不符。因此,通過隨機休眠可以使爬蟲程式模仿成人類的樣子點選網站,從而讓網站不易察覺是爬蟲存取網站,但這樣做的代價就是影響程式的執行效率。

聚焦爬蟲是一種執行效率較低的程式,提升其效能,是業界一直關注的問題,由此也誕生了效率較高的 Python 爬蟲框架 Scrapy。

深入

嚴先生寫的 正規表示式基本語法 非常精煉易懂,作為前端程式設計師,我們用到正規表示式的機會還是很多的,所以相信大家多多少少都對正規表示式有一定的認識,在w3school或者菜鳥教學的教學亂的一批,建議換成嚴先生的教學。

菜鳥教學提供線上的正則測試 正規表示式線上測試

Python re模組用法詳解 是對re模組(正則相關)的一些解讀,大部分在講正規表示式,可以略過,等用到時再搜

Python csv模組(讀寫檔案) 介紹了csv檔案的讀寫,可能很多同學對csv也比較熟悉,且在前端有過實踐經驗,這裡主要介紹python的讀寫。

上面程式碼提到過寫入檔案,但是沒有具體解釋,因為那會我也不懂,就是這個 with open()

參見:https://blog.csdn.net/m0_48936146/article/details/124360734

應用以上所學的知識,我們就可以編寫一個稍微複雜的爬蟲,並在其中找出我們需要的資料了,我相信你會解決編寫過程中遇到的問題!

接下來,我們略過課程:

[範例]抓取貓眼電影排行榜 主要講正則的應用

Python Pymysql儲存資料 主要描述在python中操作mysql資料庫,如果你不會mysql,那你沒必要學這章

[範例]抓取多級頁面資料 主要講正則應用的變體,和增量爬蟲,增量爬蟲就是隻抓更新的部分

看到這三個標題,如果你不能快速的整理思路,那麼你可以先檢視一下這三節(我反正沒看)

Requests 庫

不知道你在做上面的例子的時候,有沒有遇到過爬取頁面要求你進行人機驗證的,Requests 庫有時可以有效地規避這個問題,同時,它是在python中應用最廣泛的http請求庫

方法 說明
requests.request() 構造一個請求物件,該方法是實現以下各個方法的基礎。
requests.get() 獲取HTML網頁的主要方法,對應於 HTTP 的 GET 方法。
requests.head() 獲取HTML網頁頭資訊的方法,對應於 HTTP 的 HEAD 方法。
requests.post() 獲取 HTML 網頁提交 POST請求方法,對應於 HTTP 的 POST。
requests.put() 獲取HTML網頁提交PUT請求方法,對應於 HTTP 的 PUT。
requests.patch() 獲取HTML網頁提交區域性修改請求,對應於 HTTP 的 PATCH。
requests.delete() 獲取HTML頁面提交刪除請求,對應於 HTTP 的 DELETE。

由此可見,此庫囊括了所有http請求方法,基於此,我們甚至可以寫一個自己的介面測試工具

代理IP-proxies引數

一些網站為了限制爬蟲從而設定了很多反爬策略,其中一項就是針對 IP 地址設定的。比如,存取網站超過規定次數導致流量異常,或者某個時間段內頻繁地更換瀏覽器存取,存在上述行為的 IP 極有可能被網站封殺掉。可以使用瀏覽器外掛:Proxy SwitchyOmega,可以便捷的更換ip代理

本節課程:Requests庫常用方法及引數介紹

xpath的認識

接下來介紹一個你可能用過的東西 Xpath ,這個東西在前端也有應用,有一個js外掛叫做 jsonpath,不知道你有沒有聽說過,簡單說,jsonpath可以用特定的語法在複雜的json資料中查詢

例如,我們現在有如下 json 資料

{
    "objectTop":[
        {
            name:"data1",
            id:1,
            status:"on"
        },
        {
            name:"data2",
            id:2,
            status:"off"
        },
        {
            name:"data3",
            id:3,
            status:"on"
        }
    ]
}

我們要查詢 status == on 的資料,按照正常思路,我們首先要遍歷 objectTop ,大概這麼寫

function getStatusOn(){
    let res = []
    for(let item of objectTop){
        if(item.status == "on")
           res.push(item) 
    }
}

或者使用 Object.x 去遍歷,陣列還可以使用filter或者map去遍歷,當然這麼寫已經很方便了,但是jsonpath一行程式碼就可以搞定了,大概這麼寫

let res = jsonpath.query(objectTop,"$..[?(@.status == 'on')]")

更多關於 jsonpath 的教學 ,參見 JsonPath基本用法 npm上也有jsonpath的包可供下載。

解釋一下上面表示式的大概含義

$:根節點 即 objectTop

.. : 遞迴查詢(貪婪模式,查詢到最後一級)

[?()] :裡面包含條件語句

@. : 當前key(或者當前節點)

status == 'on' 是條件語句

這種寫法好像一種 sql 語句,它其實大部分語法就遵循了Xpath

XPath(全稱:XML Path Language)即 XML 路徑語言,它是一門在 XML 檔案中查詢資訊的語言,最初被用來搜尋 XML 檔案,同時它也適用於搜尋 HTML 檔案。因此,在爬蟲過程中可以使用 XPath 來提取相應的資料。

如果說 jsonpath 可以快速查詢json節點,那麼xpath就可以快速查詢dom節點

有興趣的還可以看一下xpath在前端的應用,參見: js用xpath定位獲取元素

本節教學:Xpath簡明教學(十分鐘入門)

Xpath Helper是一款瀏覽器外掛,可以便捷的自動生成查詢語句

媽的

據我所知,咱們上邊學了正則的方式查詢元素,弄了半天有xpath這麼個玩意,那上邊的不白學了

瀏覽器實現抓包

這章我看了,我以為學個爬蟲,還順便教我們駭客技術,所謂的抓包,就是在瀏覽器的開發者工具看網路請求記錄,看不起誰呢,我們是前端。下一章。

Python爬蟲破解有道翻譯

這章我看了,就是調了別人的介面,不用看。這章提到了一個術語,可能有的同學不知道:加鹽,簡單來說加鹽就是加密,是一種很初級的加密手段。老王曾經這麼跟我說的,加鹽,就是加點佐料,吃著就不一樣了。給一個明文加點佐料(鹽),也就是加鹽,這個明文就成了密文了。

Python爬蟲抓取動態載入資料

這章我看了,還是調了別人的介面,不用看

Python json模組常用方法

import json,就完了

方法 說明
json.dumps() 將 Python 物件轉換成 JSON 字串。
json.loads() 將 JSON 字串轉換成 Python 物件。
json.dump() 將 Python 中的物件轉化成 JSON 字串儲存到檔案中。
json.load() 將檔案中的 JSON 字串轉化成 Python 物件提取出來。

類似js中的 JSON.parse() 和 JSON.stringfy()

Python爬蟲實現Cookie模擬登入

簡單來說,有些網站登入後使用cookie儲存登入狀態,並且在向伺服器請求時也攜帶cookie資訊,相信大家或多或少也做過類似的業務,有時我們使用 Authorization 或者 攜帶 token 的方式(大多數時候)。cookie和ua一樣,可以直接以字串形式填寫在header裡,

就是這樣

    headers = {
        "cookie": 'appmsglist_action_3228059900=card; RK=IC8o9cFlOq; ptcz=c4a3603f5d15ebc78f805034e793aaff8840473c18e1a6e595470b56b78ae125; ',
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54"
    }

如果一個網站必須登入才能爬到有用的資料,那麼你可以試一下這個方法,你會快速的收穫成就感(我學到這已經幫公司寫了一個公眾號爬蟲了)

Python多執行緒爬蟲詳解

執行緒這個概念對我們來說太複雜了,我不學了,不過跟著這章教學可以快速做出一個多執行緒爬蟲

本節教學 Python多執行緒爬蟲詳解

Python BS4解析庫用法詳解

美味濃湯:Beautiful Soup 簡稱 BS4(其中 4 表示版本號)是一個 Python 第三方庫

Beautiful Soup 將 HTML 檔案轉換成一個樹形結構,比xpath好用一點點,各有千秋

bs4還提供了直接的css選擇器和標籤選擇器,是真正比較直觀的一個解析器

本節教學 Python BS4解析庫用法詳解

Python Selenium基本用法

Selenium 作為一款 Web 自動化測試框架,提供了諸多操作瀏覽器的方法。基本上可以實現任何 瀏覽器操作,模擬人手操作,例如:點選、輸入、拖動、調整瀏覽器視窗大小(是的)、各種鍵盤事件。這章提到了「無介面瀏覽器」,也就是常說的(我不常說,不知道誰常說)無頭瀏覽器

Python Scrapy爬蟲框架詳解

這章我看了,屬於結丹期的功法,我也不會,就不講了,靈根好的同學可以自行修煉。

學完了

至此,你已經達到了python爬蟲築基期修為,現在你可以 爬普通的網頁和帶登入驗證的網頁(對於凡人來說,你已經無所不能了)

總結:python好,真好,寫出來第一個有用的爬蟲程式時,幸福感滋兒一下就上來了(乾淨又衛生)。

學完這些,我們甚至還可以寫介面測試了,後端甩給你一個圖片AI識別的介面,讓你把這 兩千張圖 測一下,我反手一個python指令碼,跑了一個小時,完事。

老闆問我能不能把公眾號的文章同步到公司官網,沒問題,一個小時寫了個爬蟲,十秒同步完成。

老闆還是不滿意,能不能每更新一次就自動同步到官網,沒問題,這個叫定時增量爬蟲,一個半小時足夠了,部署到伺服器,定時執行。

python真是太美好啦,我感覺我已經無所不能了。

俗話說的好:爬蟲學的好,牢飯吃到飽。所以大家沒事別老研究爬蟲,影響自己的仕途。