前言
本文可能篇幅較長,但是絕對乾貨滿滿,提供了大量的學習資源和途徑。達到讓讀者獨立自主的編寫基礎網路爬蟲的目標,這也是本文的主旨,輸出有價值能夠真正幫助到讀者的知識,即授人以魚不如授人以漁,讓我們直接立刻開始吧,本文包含以下內容:
- Python環境搭建與基礎知識
- 爬蟲原理概述
- 爬蟲技術概覽
- 貓眼電影排行資料抓取
- Ajax資料爬取貓眼電影票房
- 更多進階,代理、模擬登陸、APP 爬取等…..
Python環境搭建與基礎知識
Python環境搭建
Anaconda安裝
此處筆者並不會介紹Python軟體的安裝,有讀者可能會疑問Python都不安裝,我怎麼學習先進的Python知識呢? 不要著急,此處筆者介紹了一種新的Python快速安裝方式,即直接安裝Anaconda,Anaconda是什麼呢?
Anaconda 是一個Python的發行版,包括了Python和很多常見的Python庫, 和一個包管理器cond,Anaconda是專注於資料分析的Python發行版本,包含了conda、Python等720多個科學包及其依賴項,適用於企業級大資料分析的Python工具。在資料視覺化、機器學習、深度學習等多方面都有涉及。不僅可以做資料分析,甚至可以用在大資料和人工智慧領域。有讀者可能會疑問這和爬蟲有什麼關係呢,當然有關係,在編寫爬蟲程式的過程中需要使用Python庫,而Anaconda就已經包含這些經常使用庫,這對安裝Python庫感到頭疼的讀者再好不過了。當然這一切都是免費的,接下來我們就開始安裝美妙的Anaconda吧。
首先從Anaconda官網下載對應版本的Anaconda,如果下載速度過慢推薦使用國內的清華大學開源軟體映象站選擇對應的Anaconda下載,Anaconda的官網下載頁面如下圖所示:
本文推薦下載Python3.6對應的版本,以筆者為例電腦環境為:Windows-64Bit,下載的對應版本為:Anaconda3-5.2.0-Windows-x86_64,下載完成後開啟安裝包如下圖所示:
點選 next
點選 I Agree
選擇 Just Me ,點選 next
選擇安裝目錄,點選next
勾選 Add Anaconda to my PATH environment variable ,然後點選 install 安裝即可
IDE環境搭建
IDE筆者推薦使用Pycharm,其中免費的社群版已經能夠滿足我們的需求,使用教程可以參考CSDN部落格Pycharm簡單使用教程,或者直接在CSDN搜尋pycharm教程獲取更多知識。此處可以詳細介紹下
1.1Python 基礎技術
我不會介紹過於基礎內容,因為這些內容網際網路上已經大量免費的基礎入門教程了,但是筆者會給大家提供一些網際網路的免費學習資源和方法,讓大家快速學會編寫爬蟲程式所需要的Python基礎知識和進階知識,而對於基礎的爬蟲我們需要掌握的Python知識有以下:
- 資料型別
- 列表
- 迴圈語句
- 判斷語句
- 函式
Python基礎
對於完全沒有Python基礎的讀者,可以學習下面的快速入門
Python官方文件,具有絕對的權威和全面,但是文件本身是英文,所以對大部分初學者來說並不是很友好,下面是國人翻譯的Python版本,對英文感冒的讀者可以選擇該版本學習:
- 《Python程式設計 從入門到實踐》
- 《Python基礎教程(第3版)》 Python進階 對於想要提升自己技術的讀者,接下的書籍和資料應該是很符合你的胃口了 :
- 《流暢的Python》
- 《Python學習手冊(第4版)》
- 《Python核心程式設計(第3版)》
- 《資料結構 Python語言描述》
- 《Python高效能程式設計》
爬蟲是什麼
爬蟲原理
爬蟲是什麼?爬蟲從本質上說就是在模擬HTTP請求,記住這句話,這就是我們後面經常需要做的事情。一般使用者獲取網路資料的方式有兩種:
a. 瀏覽器提交HTTP請求--->下載網頁程式碼--->解析成頁面。
b. 模擬瀏覽器傳送請求(獲取網頁程式碼)->提取有用的資料->存放於資料庫或檔案中。
爬蟲就是在做第二種事情,大致過程如下:
i. 通過HTTP庫向目標站點發起請求,即傳送一個Request,請求可以包含額外的headers等資訊,等待伺服器的響應
ii. 如果伺服器正常響應,會得到一個Response,Response的內容便是所要獲取的頁面內容,型別可能有HTML、JSON、二進位制檔案(如圖片、視訊等型別)。
iii. 得到的內容可能是HTML,可以用正規表示式、網頁解析庫進行解析。可能是JSON,可以直接轉成JOSN物件進行解析,可能是二進位制資料,可以儲存或者進一步處理
iv. 儲存形式多樣,可以儲存成文字,也可以儲存至資料庫,或者儲存成特定格式的檔案。
許多讀者可能不知道上面具體在做什麼,那麼接下來我們通過瀏覽器抓包分析上面的過程,筆者推薦使用Chrome,對開發者很友好,後續我們會經常使用到,Chrome下載,如果下載速度較慢,建議使用國內Chrome映象下載安裝。
首先開啟瀏覽器在位址列輸入 https://www.baidu.com/ (讀者也可以使用其他網頁測試比如我們們的https://gitbook.cn/),回車,百度頁面映面而來,然後按下F12,瀏覽器開發者選項的快捷鍵,選擇Network欄目,開啟介面下圖所示:
按下F5重新整理頁面:
欄目裡面更新了大量的資料包,這些包就是瀏覽器請求的資料,我們想要的資料就在這些請求裡面
- 第一列Name:請求的名稱,一般會將URL的最後一 部分內容當作名稱。
- 第二列Status: 響應的狀態碼,這裡顯示為200,代表響應是正常的。通過狀態碼,我們可 以判斷髮送了請求之後是否得到了正常的響應。
- 第三列Type: 請求的文件型別。這裡為document, 代表我們這次請求的是一個HTML文件,內容就是一些HTML程式碼。
- 第四列initiator: 請求源。用來標記請求是由哪個物件或程式發起的。
- 第五列Size: 從伺服器下載的檔案和請求的資源大小。如果是從快取中取得的資源,則該列會顯示from cache。
- 第六列Time:發起請求到獲取響應所用的總時間。
- 第七列Waterfall:網路請求的視覺化瀑布流。 接下來我們分析請求的詳細組成,比如點第一個請求即Name為www.baidu.com的請求,如下圖所示:
我們看到響應中分General部分,請求頭、響應頭
General一般包含以下部分:
- Request URL為請求的URL
- Request Method為請求的方法
- Status Code為響應狀態碼,
- Remote Address為遠端伺服器的地址和埠
Response Headers一般包含以下部分(響應(服務端->客戶端[response])):
- HTTP/1.1為響應採用的協議和版本號 200 (狀態碼) OK(描述資訊)
- Location為服務端需要客戶端訪問的頁面路徑
- Server為服務端的Web服務端名
- Content-Encoding為服務端能夠傳送壓縮編碼型別
- Content-Length為服務端傳送的壓縮資料的長度
- Content-Language為服務端傳送的語言型別
- Content-Type為服務端傳送的型別及採用的編碼方式
- Last-Modified為服務端對該資源最後修改的時間
- Refresh為服務端要求客戶端1秒鐘後,重新整理,然後訪問指定的頁面路徑
- Content-Disposition為服務端要求客戶端以下載檔案的方式開啟該檔案
- Transfer-Encoding為分塊傳遞資料到客戶端
- Set-Cookie為服務端傳送到客戶端的暫存資料
- Connection為維護客戶端和服務端的連線關係
Request Headers 一般包含以下部分(請求(客戶端->服務端[request])):
- GET(請求的方式) /newcoder/hello.html(請求的目標資源) HTTP/1.1(請求採用的協議和版本號)
- Accept為客戶端能接收的資源型別
- Accept-Language為客戶端接收的語言型別
- Connection為維護客戶端和服務端的連線關係
- Host: localhost為連線的目標主機和埠號
- Referer告訴伺服器我來自於哪裡
- User-Agent為客戶端版本號的名字
- Accept-Encoding為客戶端能接收的壓縮資料的型別
- If-Modified-Since為快取時間
- Cookie為客戶端暫存服務端的資訊
- Date為客戶端請求服務端的時間 而我們需要做的就是模擬瀏覽器提交Requests Headers獲取伺服器的響應資訊,從而得到我們想要的資料,想要深入瞭解的讀者請訪問HTTP | MDN文件瞭解更多資訊。
爬蟲能抓什麼樣的資料
在網頁中我們能看到各種各樣的資訊,最常見的就是使用者能夠看到的網頁頁面,而通過瀏覽器的開發者工具對網頁請求進行抓包時我們可以看見大量的請求,即有些網頁返回的不是HTML程式碼,可能是json字串,各種二級制資料,比如圖片、音訊、視訊等,當然還有些是CSS、JavaScript等檔案。那麼即瀏覽器能夠獲取的資料,爬蟲程式都能獲取到,而瀏覽器的資料是翻譯給使用者看到的資訊,即只要能夠在瀏覽器訪問到的資訊,爬蟲程式就都能夠抓取下來。
爬蟲技術概覽
^_^:本節介紹爬蟲經常使用到的技術,比如請求:requests,資訊提取:Xpath,Re正則,json,儲存:CSV,MySQL, MongoDB,模擬瀏覽器Selenium,保證在專案實戰中涉及的技術讀者都會,也就是這裡需要講清楚這些技術的使用方法,
第一個請求
Requests庫
Requests庫,官方文件是這樣描述:Requests 唯一的一個非轉基因的 Python HTTP 庫,人類可以安全享用。警告:非專業使用其他 HTTP 庫會導致危險的副作用,包括:安全缺陷症、冗餘程式碼症、重新發明輪子症、啃文件症、抑鬱、頭疼、甚至死亡。
Requests 是以 PEP 20 (即著名的Python之禪)的箴言為中心開發的,下面就是Requests的開發哲學,望讀者能夠細細品讀,寫出更加Pythonic的程式碼。
Beautiful is better than ugly.(美麗優於醜陋) Explicit is better than implicit.(直白優於含蓄) Simple is better than complex.(簡單優於複雜) Complex is better than complicated.(複雜優於繁瑣) Readability counts.(可讀性很重要)
在2.1中我們談到爬蟲的原理就是進行HTTP請求然後得到響應,在響應中提取我們想要的資訊並儲存。而Requests庫就是利用Python模擬HTTP請求的利器。如果讀者已經安裝了Anaconda,那麼Requests庫就已經可用了,如果沒有Requests庫,讀者可以在命令列中(win+R 輸入 cmd)pip install requests 安裝requests庫,接下來就開始我們的第一個請求吧!
使用Requests傳送HTTP請求非常簡單,接下來我們就以GitChat為例:
# 匯入requests 模組
import requests
# 發起Get請求並返回Response物件,包含伺服器對HTTP請求的響應
response = requests.get('https://gitbook.cn/')
# 列印 響應狀態碼
print(response.status_code)
# 列印 str型別的響應體,比如一個普通的 HTML 頁面,需要對文字進一步分析時,使用 text
print(response.text)
複製程式碼
部分執行的結果如下圖所示:
Requests不僅支援Get方式請求,比如Post請求:
# 匯入 requests 模組
import requests
# 需要提交的表單資料
data = {
'name': 'ruo', 'age': 22
}
# 發起Post請求
response = requests.post("http://httpbin.org/post", data=data)
# 響應體內容
print(response.text)
複製程式碼
部分執行的結果如下圖所示:
當然Requests還支援更多的請求方式,比如以下請求,筆者就不一一演示了,最常用的請求就是以上Get和Post兩種請求方式。
# PUT請求
requests.put(“http://httpbin.org/put”)
# DELETE請求
requests.delete(“http://httpbin.org/delete”)
# HEAD請求
requests.head(“http://httpbin.org/get”)
# OPTIONS請求
requests.options(“http://httpbin.org/get”)
複製程式碼
由於大多數伺服器都會通過請求頭中的User-Agent識別客戶端使用的作業系統及版本、瀏覽器及版本等資訊,所以爬蟲程式也需要加上此資訊,以此偽裝瀏覽器;如果不加上很可能別識別出為爬蟲,比如當我們不加Headers對知乎進行get請求時:
# 匯入 requests 模組
import requests
# 發起Get請求
response = requests.get("https://www.zhihu.com")
# 狀態碼
print(response.status_code)
# 響應體內容
print(r.text)
複製程式碼
返回的內容如下圖所示:
我們可以看見返回的400的狀態碼,及請求無效,接著我們在請求裡新增Headers,然後新增User-Agent資訊,再次嘗試請求:
# 匯入 requests 模組
import requests
# 在Headers中新增User-Agent欄位資訊
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
# 發起Get請求
response = requests.get("https://www.zhihu.com", headers=headers)
# 狀態碼
print(response.status_code)
# 響應體內容
print(response.text)
複製程式碼
返回的內容如下圖所示:
提取資訊
當我們通過HTTP請求獲取到響應後,加下來就需要提取響應體中的內容,此處筆者介紹兩種常用的提取方法,一個是正規表示式,另一個是Xpath。
正規表示式
正規表示式是一個很強大的字串處理工具,幾乎任何關於字串的操作都可以使用正規表示式來完成,作為一個爬蟲工作者,每天和字串打交道,正規表示式更是不可或缺的技能。有了它,從HTML裡提取想要的資訊就非常方便了。
讀者可以通過正規表示式 | 廖雪峰的官方網站快速入門,也可以通過Python正規表示式 | 菜鳥教程 學習Python中操作正則和使用正則,Python的官方文件中Python標準庫的6.2節也對Re有詳細的介紹和使用教程。
初次接觸正規表示式的讀者可能會覺得有些抽象,有點難入門,因為畢竟正規表示式本身就是一種小型的、高度專業化的程式語言,以上的入門教程瞭解後,這裡給讀者介紹一個提取資訊通用的正則字串 .*?,該規則能夠以非貪婪的方式匹配任意字元,後面我們會經常使用到。
比如我們需要匹配 <H1>Chapter 1 - 介紹正規表示式</H1> 標籤中的內容,我們可以:
# 匯入 re 模組
import re
# 待匹配文字
h1 = '<H1>Chapter 3.2.1 - 介紹正規表示式</H1>'
# 將正則字串編譯成正規表示式物件,方便在後面的匹配中複用
pat = re.compile('<H1>(.*?)</H1>', re.S)
# re.search 掃描整個字串並返回第一個成功的匹配
result = re.search(pat, h1)
# 匹配的整個表示式的字串,group() 可以一次輸入多個組號,在這種情況下它將返回一個包含那些組所對應值的元組。
print(result.group(0))
# 匹配的第一個括號內的字串,group() 可以一次輸入多個組號,在這種情況下它將返回一個包含那些組所對應值的元組。
print(result.group(1))
複製程式碼
以下是匹配結果:
Xpath
XPath即為XML路徑語言(XML Path Language),它是一種用來確定XML文件中某部分位置的語言。
XPath基於XML的樹狀結構,提供在資料結構樹中找尋節點的能力。起初XPath的提出的初衷是將其作為一個通用的、介於XPointer與XSL間的語法模型。但是XPath很快的被開發者採用來當作小型查詢語言,在爬蟲中提取資訊也是不錯的好幫手。
讀者可以通過 Xpath 教程 | 菜鳥教程 學習Xpath的原理及編寫方法,也可以訪問CSDN部落格中搜尋Python Xpath學習更多Python中Xpath的基本操作,接下來介紹編寫“編寫”的技巧和在Python中使用的方法,之所以加上“編寫”,讀者看下面便知。
還記得在2.1爬蟲原理中使用的瀏覽器的開發者工具嗎,我們可以通過這個工具直接獲取對應節點的Xpath規則,從而達到快速利用Xpath提取網頁資訊的目的,例如提取貓眼電影TOP100榜中的電影資訊,首先開啟瀏覽器輸入http://maoyan.com/board/4,將滑鼠移動到需要提取的資訊(電影名稱)上,右鍵選擇檢查,如下圖所示:
接著我們選擇下面的元素,右鍵選擇Copy-->xpath, 如下圖所示:
獲取了該節點的xpath規則了,接下來我們編寫Python程式驗證該規則是否能夠真正提取電影名:
import requests
# 匯入lxml庫的etree模組
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
url = 'http://maoyan.com/board/4'
response = requests.get(url, headers=headers)
html = response.text
# 呼叫HTML類進行初始化
html = etree.HTML(html)
# 貼上我們copy的xpath,提取電影名 “霸王別姬”
result_bawangbieji = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd[1]/div/div/div[1]/p[1]/a')
# 列印節點標籤包含的文字內容
print(result_bawangbieji[0].text)
# 提取該頁面所有電影名,即選擇所有'dd'標籤的電影名
result_all = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd/div/div/div[1]/p[1]/a')
# 列印所有提取出的電影名
print('該頁面全部電影名:')
for one in result_all:
print(one.text)
複製程式碼
結果如下圖所示,我們成功提取了HTML中電影名的資訊:
儲存資訊
TEXT 文字儲存
如果讀者學習了Python的基礎知識,那麼應該比較熟悉這種基本資訊儲存方式,即直接將我們需要儲存的資訊寫入檔案中,比如常見的TEXT檔案,如果不熟悉的讀者可以通過Python檔案讀寫 - Python教程™快速概覽,下面我們就對3.2.2中Xpath提取的電影名進行檔案儲存操作:
import requests
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
url = 'http://maoyan.com/board/4'
response = requests.get(url, headers=headers)
html = response.text
# 呼叫HTML類進行初始化
html = etree.HTML(html)
# 貼上我們copy的xpath,提取電影名 “霸王別姬”
result_bawangbieji = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd[1]/div/div/div[1]/p[1]/a')
# 列印節點標籤包含的文字內容
print(result_bawangbieji[0].text)
# 提取該頁面所有電影名
result_all = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd/div/div/div[1]/p[1]/a')
# 列印所有提取出的電影名
print('該頁面全部電影名:')
for one in result_all:
print(one.text)
# 將這一頁電影名儲存至TEXT檔案中,'a' 指開啟一個檔案進行追加。 如果檔案存在,則檔案指標位於檔案末尾。也就是說,檔案處於追加模式。如果檔案不存在,它將建立一個新檔案進行寫入。
with open('film_name.text', 'a') as f:
for one in result_all:
f.write(one + '\n')
複製程式碼
儲存結果如下圖所示:
CSV儲存
CSV檔案即逗號分隔值(也稱字元分隔值,因為分隔符可以不是逗號),是一種常用的文字格式,以純文字形式儲存表格資料,包括數字或者字元。Python中已經內建CSV檔案操作的模組,只需要匯入就可以進行CSV儲存操作,下面我們就將3.2.2中Xpath提取的電影名進行CSV檔案儲存操作:
import requests
from lxml import etree
# 匯入CSV模組
import csv
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
url = 'http://maoyan.com/board/4'
response = requests.get(url, headers=headers)
html = response.text
html = etree.HTML(html)
result_bawangbieji = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd[1]/div/div/div[1]/p[1]/a')
print(result_bawangbieji[0].text)
result_all = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd/div/div/div[1]/p[1]/a')
print('該頁面全部電影名:')
for one in result_all:
print(one.text)
# 將這一頁電影名儲存至CSV檔案中:
with open('film_name.csv', 'a', newline='') as f:
csv_file = csv.writer(f)
for one in result_all:
csv_file.writerow([one.text])
複製程式碼
CSV檔案儲存結果如下圖所示:
MySQL 儲存
MySQL 是最流行的關係型資料庫管理系統,如果讀者沒有安裝MySQL可以通過phpstudy 2018 下載下載phpstudy快速安裝MySQL
在Python2中,連線MySQL的庫大多是使用MySQLdb,但是此庫的官方並不支援Python3,所以這裡推薦使用的庫是PyMySQL,讀者可以通過Python+MySQL資料庫操作(PyMySQL)| Python教程™學習PyMYSQL操作MySQL的相關方法和例項,接下來我們就嘗試將3.2.2中Xpath提取的電影名儲存到MySQL中,沒有該模組的讀者可以通過(win+R 輸入 cmd)pip install pymysql 安裝pymysql庫。
import requests
from lxml import etree
# 匯入pymysql模組
import pymysql
# 開啟一個資料庫連線
db = pymysql.connect(host='localhost', user='root', password='root', port=3306, db='spider', use_unicode=True, charset="utf8")
# 獲取MySQL的操作遊標,利用遊標來執行SQL語句
cursor = db.cursor()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
url = 'http://maoyan.com/board/4'
response = requests.get(url, headers=headers)
html = response.text
html = etree.HTML(html)
result_bawangbieji = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd[1]/div/div/div[1]/p[1]/a')
print(result_bawangbieji[0].text)
result_all = html.xpath('//*[@id="app"]/div/div/div[1]/dl/dd/div/div/div[1]/p[1]/a')
print('該頁面全部電影名:')
for one in result_all:
print(one.text)
try:
# 插入資料語句
sql = 'INSERT INTO film_infor(film_name) values (%s)'
cursor.execute(sql, (one.text))
db.commit()
except:
db.rollback()
複製程式碼
MySQL儲存結果如下圖所示: