Python爬虫开发从入门到实战
Python爬蟲開發從入門到實戰(微課版)
第1章 緒論
爬蟲的主要目的是獲取網頁內容并解析。只要能達到這個目的,用什么方法都沒有問題。
關于獲取網頁,本書主要介紹了Python的兩個第三方模塊,一個是requests,另一個是爬蟲框架Scrapy。
關于解析網頁內容,本書主要介紹了3種方式——正則表達式、XPath和BeautifulSoup。兩種網頁獲取方式和3種網頁解析方式可以自由搭配,隨意使用。
第2章 Python基礎
知識點
- Python開發環境的搭建。
- Python的基本知識、數據類型。
- Python的條件語句和循環語句。
- Python函數的定義和使用。
- 基于Python的面向對象編程代碼。
第3章 正則表達式與文件操作
知識點
- 正則表達式的基本符號。
- 如何在Python中使用正則表達式。
- 正則表達式的提取技巧。
- Python讀寫文本文件和CSV文件。
第4章 簡單的網頁爬蟲開發
知識點
- requests的安裝和使用。
- 多線程爬蟲的開發。
- 爬蟲的常見算法。
多線程爬蟲的開發
在掌握了requests與正則表達式以后,就可以開始實戰爬取一些簡單的網址了。
但是,此時的爬蟲只有一個進程、一個線程,因此稱為單線程爬蟲。單線程爬蟲每次只訪問一個頁面,不能充分利用計算機的網絡帶寬。一個頁面最多也就幾百KB,所以爬蟲在爬取一個頁面的時候,多出來的網速和從發起請求到得到源代碼中間的時間都被浪費了。
如果可以讓爬蟲同時訪問10個頁面,就相當于爬取速度提高了10倍。為了達到這個目的,就需要使用多線程技術了。
微觀上的單線程,在宏觀上就像同時在做幾件事。這種機制在I/O(Input/Output,輸入/輸出)密集型的操作上影響不大,但是在CPU計算密集型的操作上面,由于只能使用CPU的一個核,就會對性能產生非常大的影響。所以涉及計算密集型的程序,就需要使用多進程,Python的多進程不受GIL的影響。
由于爬蟲是I/O密集型的操作,特別是在請求網頁源代碼的時候,如果使用單線程來開發,會浪費大量的時間來等待網頁返回,所以把多線程技術應用到爬蟲中,可以大大提高爬蟲的運行效率。
多進程庫(multiprocessing)
multiprocessing本身是Python的多進程庫,用來處理與多進程相關的操作。但是由于進程與進程之間不能直接共享內存和堆棧資源,而且啟動新的進程開銷也比線程大得多,因此使用多線程來爬取比使用多進程有更多的優勢。multiprocessing下面有一個dummy模塊,它可以讓Python的線程使用multiprocessing的各種方法。
dummy下面有一個Pool類,它用來實現線程池。這個線程池有一個map()方法,可以讓線程池里面的所有線程都“同時”執行一個函數。
from multiprocessing.dummy import Pool as ThreadPool# 使用map實現多線程爬蟲 pool = ThreadPool(4) pool.map(crawler_func, data_list) pool.close() pool.join()常見搜索算法
- DFS
- BFS
在爬蟲開發的過程中,應該選擇深度優先還是廣度優先呢?這就需要根據被爬取的數據來進行選擇了。
小結
本章講解了requests的安裝和使用,以及如何使用Python的多進程庫multiprocessing來實現多線程爬蟲。
第5章 高性能HTML內容解析
知識點
- HTML基礎結構。
- 使用XPath從HTML源代碼中提取有用信息。
- 使用Beautiful Soup4從HTML源代碼中提取有用信息。
Beautiful Soup4
Beautiful Soup4(BS4)是Python的一個第三方庫,用來從HTML和XML中提取數據。
pip install beautifulsoup4小結
從網頁中提取需要的信息,是爬蟲開發中最重要但卻最基本的操作。只有掌握并能自由運用正則表達式、XPath與Beautiful Soup4從網頁中提取信息,爬蟲的學習才算是入門。
XPath是一門查詢語言,它由C語言開發而來,因此速度非常快。但是XPath需要經過一段時間的練習才能靈活應用。
Beautiful Soup4是一個從網頁中提取數據的工具,它入門很容易,功能很強大,但是由于是基于Python開發的,因此速度比XPath要慢。讀者可以自行選擇喜歡的一項來作為自己主要的數據提取方式。本書選擇使用XPath,所以后面的內容都會以XPath來進行講解。
第6章 Python與數據庫
數據庫
本章將會講解MongoDB和Redis這兩個數據庫。其中MongoDB用來保存大量數據,Redis用于作為緩存和隊列保存臨時數據。
知識點
- MongoDB與Redis的安裝。
- MongoDB的增刪改查操作。
- Redis的列表與集合的操作。
在Mac OS下安裝MongoDB
brew update brew install mongodb #啟動MongoDB mongod --config /usr/local/etc/mongod.conf圖形化管理工具—RoboMongo
RoboMongo是一個跨平臺的MongoDB管理工具,可以在圖形界面中查詢或者修改MongoDB。
數據在MongoDB中是按照“庫(Database)”—“集合(Collections)”—“文檔(Document)”的層級關系來存儲的。如果使用Python的數據結構來做類比的話,文檔相當于一個字典,集合相當于一個包含了很多字典的列表,庫相當于一個大字典,大字典里面的每一個鍵值對都對應了一個集合,Key為集合的名字,Value就是一個集合。
PyMongo的安裝
PyMongo模塊是Python對MongoDB操作的接口包,能夠實現對MongoDB的增刪改查及排序等操作。
pip install pymongoPyMongo的使用
(1)使用PyMongo初始化數據庫
要使用PyMongo操作MongoDB,首先需要初始化數據庫連接。如果MongoDB運行在本地計算機上,而且也沒有修改端口或者添加用戶名及密碼,那么初始化MongoClient的實例的時候就不需要帶參數,直接寫為:
from pymongo import MongoClient client = MongoClient()如果MongoDB是運行在其他服務器上面的,那么就需要使用“URI(Uniform Resource Identifier,統一資源標志符)”來指定連接地址。MongoDB URI的格式為:
mongodb://用戶名:密碼@服務器IP或域名:端口PyMongo初始化數據庫與集合有兩種方式。
# 方式1: from pymongo import MongoClient client = MongoClient() database= client.Chapter6 collection = database.spider # 需要注意,使用方式1的時候,代碼中的“Chapter6”和“spider”都不是變量名,它們直接就是庫的名字和集合的名字。# 方式2: from pymongo import MongoClient client = MongoClient() database = client['Chapter6'] collection = database['spider'] # 使用方式2時,在方括號中指定庫名和集合名。這種情況下,方括號里除了直接寫普通的字符串以外,還可以寫一個變量。默認情況下,MongoDB只允許本機訪問數據庫。這是因為MongoDB默認沒有訪問密碼,出于安全性的考慮,不允許外網訪問。
如果需要從外網訪問數據庫,那么需要修改安裝MongoDB時用到的配置文件mongod.conf。
(2)插入數據
MongoDB的插入操作非常簡單。用到的方法為insert(參數),插入的參數就是Python的字典。插入一條數據的代碼如下。
from pymongo import MongoClient client = MongoClient() database = client['Chapter6'] collection = database['spider'] data = {'id': 123, 'name': 'kingname', 'age': 20, 'salary': 999999} collection.insert(data)# MongoDB會自動添加一列“_id”,這一列里面的數據叫作ObjectId,ObjectId是在數據被插入MongoDB的瞬間,通過一定的算法計算出來的。因此,_id這一列就代表了數據插入的時間,它不重復,而且始終遞增。通過一定的算法,可以把ObjectId反向恢復為時間。將多個字典放入列表中,并將列表作為insert()方法的參數,即可實現批量插入數據,代碼如下。
from pymongo import MongoClient client = MongoClient() database = client['Chapter6'] collection = database['spider'] more_data = [{'id': 2, 'name': '張三', 'age': 10, 'salary': 0},{'id': 3, 'name': '李四', 'age': 30, 'salary': -100},{'id': 4, 'name': '王五', 'age': 40, 'salary': 1000},{'id': 5, 'name': '外國人', 'age': 50, 'salary': '未知'}, ] collection.insert(more_data)(3)普通查找
MongoDB的查找功能對應的方法是:
find(查詢條件, 返回字段) find_one(查詢條件,返回字段)普通查詢方法有以下3種寫法。 content = collection.find() content = collection.find({'age': 29}) content = collection.find({'age': 29}, {'_id': 0, 'name': 1, 'salary': 1})(4)邏輯查詢
PyMongo也支持大于、小于、大于等于、小于等于、等于、不等于這類邏輯查詢。
collection.find({'age': {'$gt': 29}}) #查詢所有age > 29的記錄 collection.find({'age': {'$gte': 29, '$lte': 40}}) #查詢29 ≤ age ≤ 40的記錄 collection.find({'salary': {'$ne: 29}}) #查詢所有salary不等于29的記錄(5)對查詢結果排序
# MongoDB支持對查詢到的結果進行排序。排序的方法為sort()。它的格式為: handler.find().sort('列名', 1或-1)# 查詢一般和find()配合在一起使用。例如: collection.find({'age': {'$gte': 29, '$lte': 40}}).sort('age', -1) collection.find({'age': {'$gte': 29, '$lte': 40}}).sort('age', 1)(6)更新記錄
更新可使用update_one()和update_many()方法。它們的格式為:
collection.update_one(參數1, 參數2) collection.update_many(參數1, 參數2)(7)刪除記錄
刪除可使用delete_one()和delete_many()方法。它們的格式為:
collection.delete_one(參數) collection.delete_many(參數)(8)對查詢結果去重
去重使用distinct()方法,其格式為:
collection.distinct('列名')設計一個開關
思考一個問題:如何設計一個開關,實現在不結束程序進程的情況下,從全世界任何一個有網絡的地方既能隨時暫停程序,又能隨時恢復程序的運行。
最簡單的方法就是用數據庫來實現。在能被程序和控制者訪問的服務器中創建一個數據庫,數據庫名為“Switch_DB”。數據庫里面創建一個集合“Switch”,這個集合里面只有一個記錄,就是“Status”,它只有兩個值,“On”和“Off”,
在Mac OS下安裝Redis
brew update brew install redis #運行Redis redis-server /usr/local/etc/redis.confRedis交互環境的使用
redis-cli常見操作
keys *可以查看當前有多少的“Key”。
在爬蟲開發的過程中主要會用到Redis的列表與集合
(1)列表
Redis的列表是一個可讀可寫的雙向隊列
lpush key value1 value2 value 3…如果想查看一個列表的長度,可使用關鍵字為“llen”。這個關鍵字的第1個“l”對應的是英文“list”(列表)的首字母。
如果不刪除列表中的數據,又要把數據讀出來,就需要使用關鍵字“lrange”,這里的“l”對應的是英文“list”的首字母。”lrange”的使用格式為:
lrange key start end # 其中,start為起始位置,end為結束位置。例如: lrange chapter_6 0 3# 需要特別注意的是,在Python中,切片是左閉右開區間,例如,test[0:3]表示讀列表的第0、1、2個共3個值。但是lrange的參數是一個閉區間,包括開始,也包括結束,因此在圖6-35中會包含下標為0、1、2、3的4個值。(2)集合
Redis的集合與Python的集合一樣,沒有順序,值不重復。往集合中添加數據,使用的關鍵字為“sadd”。這里的“s”對應的是英文單詞“set”(集合)。使用格式為:
sadd key value1 value2 value3安裝Redis-py
pip install redisMongoDB的優化建議
少讀少寫少更新
- 建議把要插入到MongoDB中的數據先統一放到一個列表中,等積累到一定量再一次性插入。
- 對于讀數據,在內存允許的情況下,應該一次性把數據讀入內存,盡量減少對MongoDB的讀取操作。
- 在某些情況下,更新操作不得不逐條進行。建議,把更新這個動作改為插入。這樣就可以實現批量更新的效果了。具體來說,就是把數據批量插入到一個新的MongoDB集合中,再把原來的集合刪除,最后將新的集合改為原來集合的名字。
能用Redis就不用MongoDB
為了提高效率,就需要引入Redis。由于Redis是基于內存的數據庫,因此即使頻繁對其讀/寫,對性能的影響也遠遠小于頻繁讀/寫MongoDB。在Redis中創建一個集合“crawled_url”,爬蟲在爬一個網址之前,先把這個網址sadd到這個集合中。如果返回為1,那么表示這個網址之前沒有爬過,爬蟲需要去爬取詳情頁。如果返回0,表示這個網址之前已經爬過了,就不需要再爬了。示例代碼片段如下:
for url in url_list: #url_list為在貼吧列表頁得到的每一個帖子的詳情頁網址列表if client.sadd('crawled_url', url) == 1:crawl(url)練習
目標網站:http://dongyeguiwu.zuopinj.com/5525/。
目標內容:小說《白夜行》第一章到第十三章的正文內容。
任務要求:編寫兩個爬蟲,爬蟲1從http://dongyeguiwu.zuopinj.com/ 5525/獲取小說《白夜行》第一章到第十三章的網址,并將網址添加到Redis里名為url_queue的列表中。爬蟲2從Redis里名為url_queue的列表中讀出網址,進入網址爬取每一章的具體內容,再將內容保存到MongoDB中。
# 1 使用XPath獲取每一章的網址,再將它們添加到Redis中。其核心代碼如下: url_list = selector.xpath('//div[@class="book_list"]/ul/li/a/@href') for url in url_list:client.lpush('url_queue', url)# 2 對于爬取正文的爬蟲,只要發現Redis里的url_queue這個列表不為空,就要從里面讀出網址,并爬取數據。因此,其代碼如下: content_list = [] while client.llen('url_queue') > 0:url = client.lpop('url_queue').decode()source = requests.get(url).contentselector = html.fromstring(source) chapter_name = selector.xpath('//div[@class="h1title"]/h1/text()')[0] content = selector.xpath('//div[@id="htmlContent"]/p/text()') content_list.append({'title': chapter_name, 'content': '\n'.join(content)}) handler.insert(content_list)調試與運行
爬蟲1運行結束以后,Redis中應該會出現一個名為url_queue的列表,執行以下代碼:
llen url_queue爬蟲2運行結束以后,Redis中的url_queue會消失,同時MongoDB中會保存小說每一章的內容。
小結
本章主要講解了MongoDB與Redis的使用。其中,MongoDB主要用來存放爬蟲爬到的各種需要持久化保存的數據,而Redis則用來存放各種中間數據。
通過減少頻繁讀/寫MongoDB,并使用Redis來彌補MongoDB的一些不足,可以顯著提高爬蟲的運行效率。
動手實踐
如果爬蟲1把10000個網址添加到url_queue中,爬蟲2同時運行在3臺計算機上,請觀察能實現什么效果。
第7章 異步加載與請求頭
知識點
- 抓取異步加載的數據。
- 偽造HTTP請求頭。
- 模擬瀏覽器獲取網站數據。
AJAX版登錄頁面的爬取
通過POST提交請求解決了AJAX版登錄頁面的爬取
小結
本章主要介紹了使用爬蟲獲取異步加載網頁的各種方法。對于普通的異步加載,可以使用requests直接發送AJAX請求來獲取被加載的內容。
發送的請求中可能包含一些特殊的值,這些值來自網頁源代碼或者另一個AJAX請求。
在發送請求時需要注意,應保持requests提交的請求頭與瀏覽器的請求頭一致,這樣才能更好地騙過網站服務器達到獲取數據的目的。
對于比較復雜的異步加載,現階段可以先使用Selenium和ChromeDriver來直接加載網頁,然后就能從被加載的網頁中直接獲取到需要的內容。
第8章 模擬登錄與驗證碼
知識點
- 使用Selenium操作瀏覽器實現自動登錄網站。
- 使用Cookies登錄網站。
- 模擬表單登錄網站。
- 爬蟲識別簡單的驗證碼。
模擬登錄有多種實現方法
- 使用Selenium操作瀏覽器登錄
- 使用Cookies登錄雖然簡單粗暴
- 使用模擬提交表單登錄雖然較為麻煩,但可以實現自動化
使用Cookies登錄
Cookie是用戶使用瀏覽器訪問網站的時候網站存放在瀏覽器中的一小段數據。Cookie的復數形式Cookies用來表示各種各樣的Cookie。它們有些用來記錄用戶的狀態信息;有些用來記錄用戶的操作行為;還有一些,具有現代網絡最重要的功能:記錄授權信息——用戶是否登錄以及用戶登錄哪個賬號。
為了不讓用戶每次訪問網站都進行登錄操作,瀏覽器會在用戶第一次登錄成功以后放一段加密的信息在Cookies中。下次用戶訪問,網站先檢查Cookies有沒有這個加密信息,如果有并且合法,那么就跳過登錄操作,直接進入登錄后的頁面。
使用Cookie來登錄網頁,不僅可以繞過登錄步驟,還可以繞過網站的驗證碼。
使用了requests的Session模塊。
所謂Session,是指一段會話。網站會把每一個會話的ID(Session ID)保存在瀏覽器的Cookies中用來標識用戶的身份。requests的Session模塊可以自動保存網站返回的一些信息。其實在前面章節中使用的requests.get(),在底層還是會先創建一個Session,然后用Session去訪問。
對于HTTPS的網站,在requests發送請求的時候需要帶上verify=False這個參數,否則爬蟲會報錯。
帶上這個參數以后,爬蟲依然會報一個警告,這是因為沒有HTTPS的證書。
對于HTTPS的網站,在requests發送請求的時候需要帶上verify=False這個參數,否則爬蟲會報錯。
模擬表單登錄
- 通過POST提交請求解決了AJAX版登錄頁面的爬取。
- 但是在現實中,有更多的網站是使用表單提交的方式來進行登錄的。
使用requests的Session模塊來模擬這個登錄
驗證碼 - 肉眼打碼
1.借助瀏覽器
在模擬登錄中講到過Cookies,通過Cookies能實現繞過登錄,從而直接訪問需要登錄的網站。因此,對于需要輸入驗證碼才能進行登錄的網站,可以手動在瀏覽器登錄網站,并通過Chrome獲取Cookies,然后使用Cookies來訪問網站。這樣就可以實現人工輸入一次驗證碼,然后很長時間不再登錄。
2.不借助瀏覽器
對于僅僅需要識別圖片的驗證碼,可以使用這種方式——先把驗證碼下載到本地,然后肉眼去識別并手動輸入給爬蟲。
驗證碼 - 自動打碼
1.Python圖像識別
Python的強大,在于它有非常多的第三方庫。 對于驗證碼識別,Python也有現成的庫來使用。開源的OCR庫pytesseract配合圖像識別引擎tesseract,可以用來將圖片中的文字轉換為文本。這種方式在爬蟲中的應用并不多見。因為現在大部分的驗證碼都加上了干擾的紋理,已經很少能用單機版的圖片識別方式來識別了。所以如果使用這種方式,只有兩種情況:網站的驗證碼極其簡單工整,使用大量的驗證碼來訓練tesseract。
(1)安裝tesseract
brew install tesseract(2)安裝Python庫
要使用tesseract來進行圖像識別,需要安裝兩個第三方庫:
pip install Pillowpip install pytesseract# 其中,Pillow是Python中專門用來處理圖像的第三方庫,pytesseract是專門用來操作tesseract的第三方庫。(3)tesseract的使用
① 導入pytesseract和Pillow。
② 打開圖片。
③ 識別。
import pytesseract from PIL import Image image = Image.open('驗證碼.png') code = pytesseract.image_to_string(image) print(code)2.打碼網站
(1)打碼網站介紹
在線驗證碼識別的網站,簡稱打碼網站。這些網站有一些是使用深度學習技術識別驗證碼,有一些是雇傭了很多人來人肉識別驗證碼。
網站提供了接口來實現驗證碼識別服務。使用打碼網站理論上可以識別任何使用輸入方式來驗證的驗證碼。
(2)使用在線打碼
在百度或者谷歌上面搜索“驗證碼在線識別”,就可以找到很多提供在線打碼的網站。但是由于一般這種打碼網站是需要交費才能使用的,所以要注意財產安全。
云打碼
練習:自動登錄果殼網
目標網站:https://www.guokr.com。
目標內容:個人資料設置界面源代碼。
使用模擬登錄與驗證碼識別的技術實現自動登錄果殼網。 果殼網的登錄界面有驗證碼,請使用人工或者在線打碼的方式識別驗證碼,并讓爬蟲登錄。登錄以后可以正確顯示“個人資料設置”界面的源代碼。
涉及的知識點:
- 爬蟲識別驗證碼。
- 爬蟲模擬登錄。
小結
本章主要講授了模擬登錄與驗證碼識別。使用Selenium實現模擬登錄最為簡單。但是這種方式的弊端是運行速度慢。
使用Cookies登錄可以實現一次手動、長期自動的目的。
而模擬表單登錄本質就是發起POST請求來進行登錄,需要使用Session模塊來保存登錄信息。
驗證碼識別主要是使需輸入的驗證碼實現自動化。包括手動輸入與在線打碼。對于單擊、拖動的驗證碼,建議使用Cookies來進行登錄。
第9章 抓包與中間人爬蟲
知識點
- 使用Charles抓取App和微信小程序的數據包。
- 使用mitmproxy開發中間人爬蟲。
數據抓包
所謂抓包(Package Capture),簡單來說,就是在網絡數據傳輸的過程中對數據包進行截獲、查看、修改或轉發的過程。
如果把網絡上發送與接收的數據包理解為快遞包裹,那么在快遞運輸的過程中查看里面的內容,這就是抓包。
Charles
要簡化尋找數據的過程,就需要設法直接全局搜索網頁的所有請求的返回數據。
為了實現這個目的,就需要使用Charles。Charles是一個跨平臺的HTTP抓包工具。使用它可以像Chrome一樣截取HTTP或者HTTPS請求的數據包。
抓取HTTPS數據包
用Charles抓取HTTPS數據包時的請求會大量失敗。出現這種情況,是因為沒有安裝SSL證書導致的。
第一步,安裝好證書:要安裝SSL證書,可選擇菜單欄的“Help”- “SSL Proxying”-“Install Charles Root Certificate”命令。
第二步,設置SSL代理:安裝好證書以后,選擇菜單欄中的“Proxy”-“SSL Proxying Settings”命令打開SSL代理設置對話框。
iOS系統的配置和使用
對于蘋果設備,首先要保證計算機和蘋果設備聯在同一個Wi-Fi上。選擇Charles菜單欄中的“Help”-“Local IP Address”命令,此時彈出一個對話框,顯示當前計算機的內網IP地址。
接下來設置手機。進入系統設置,選擇“無線局域網”,然后單擊已經連接的這個Wi-Fi熱點右側的圓圈包圍的字母i的圖標。
第一步,在手機上設置HTTP代理。
第二步,使用iOS系統自帶的Safari瀏覽器訪問https://chls.pro/ssl。安裝證書。
第三步,證書信任設置
Android的配置和使用
將Charles的證書保存到計算機桌面
微信小程序爬蟲
小程序的請求極其簡單,基本上沒有驗證信息,即便有驗證信息也非常脆弱。
用Python來請求小程序的后臺接口從而獲取數據,比請求異步加載網頁的后臺接口要容易很多。
在爬蟲開發過程中,如果目標網站有微信小程序,那么一定要優先調查能否通過小程序的接口來抓取數據。
小程序的反爬蟲能力比網頁版的低很多。使用小程序的接口來爬數據,能極大提高爬蟲的開發效率。
Charles的局限
- Charles只能截獲HTTP和HTTPS的數據包,如果網站使用的是websocket或者是flashsocket,那么Charles就無能為力。
- 有一些App會自帶證書,使用其他證書都無法正常訪問后臺接口。在這種情況下,Charles自帶的證書就不能正常使用,也就沒有辦法抓取這種App的數據。
- 有一些App的數據經過加密,App接收到數據以后在其內部進行解密。
- 對于這種情況,Charles只能抓取到經過加密的數據。如果無法知道數據的具體加密方法,就沒有辦法解讀Charles抓取到的數據。
中間人爬蟲
中間人(Man-in-the-Middle,MITM)攻擊是指攻擊者與通信的兩端分別創建獨立的聯系,并交換其所收到的數據,使通信的兩端認為其正在通過一個私密的連接與對方直接對話,但事實上整個會話都被攻擊者完全控制。
在中間人攻擊中,攻擊者可以攔截通信雙方的通話,并插入新的內容或者修改原有內容。
例如:上課傳紙條
中間人爬蟲就是利用了中間人攻擊的原理來實現數據抓取的一種爬蟲技術。
數據抓包就是中間人爬蟲的一個簡單應用。所以使用Charles也是一種中間人攻擊。
mitmproxy的介紹和安裝
要安裝mitmproxy,首先需要保證系統的Python為Python 3.5或者更高版本
brew install mitmproxymitmproxy使用
- 運行mitmproxy會彈出對話框詢問
- mitmproxy的端口為8080端口,在瀏覽器或者在手機上設置代理,代理IP為計算機IP,端口為8080端口。
- 設置好代理以后,在手機上打開一個App或者打開一個網頁,可以看到mitmproxy上面有數據滾動。
- 此時只能訪問HTTP網站,要訪問HTTPS網站,還需要安裝mitmproxy的證書。在手機設置了mitmproxy的代理以后,通過手機瀏覽器訪問http://mitm.it/這個網址。
使用Python定制mitmproxy
mitmproxy的強大之處在于它還自帶一個mitmdump命令。這個命令可以用來運行符合一定規則的Python腳本,并在Python腳本里面直接操作HTTP和HTTPS的請求,以及返回的數據包。
為了自動化地監控網站或者手機發出的請求頭部信息和Body信息,并接收網站返回的頭部信息和Body信息,就需要掌握如何在Python腳本中獲得請求和返回的數據包。
mitmdump的使用場景
- 網站返回的Headers中經常有Cookies。
- mitmdump的腳本使用print()函數把Cookies打印出來,然后通過管道傳遞給另一個普通的正常的Python腳本。
- 在另一個腳本里面,得到管道傳遞進來的Cookies,再把它放進Redis里面。
需求分析
目標App:Keep。
目標內容:Keep是當下熱門的健身App,本次案例的目的是要使用抓包的方式爬取Keep的熱門動態。
涉及的知識點:
- 使用Charles或者mitmproxy抓包。
- 開發App爬蟲。
小結
- 抓包是爬蟲開發過程中非常有用的一個技巧。使用Charles,可以把爬蟲的爬取范圍從網頁瞬間擴展到手機App和微信小程序。
- 由于微信小程序的反爬蟲機制在大多數情況下都非常脆弱,所以如果目標網站有微信小程序,那么可以大大簡化爬蟲的開發難度。
- 當然,網站有可能會對接口的數據進行加密,App得到密文以后,使用內置的算法進行解密。對于這種情況,單純使用抓包就沒有辦法處理了,就需要使用下一章所要講到的技術來解決。
- 使用mitmproxy可以實現爬蟲的全自動化操作。
- 對于擁有復雜參數的網站,使用這種先抓包再提交的方式可以在一定程度上繞過網站的反爬蟲機制,從而實現數據抓取。
第10章 Android原生App爬蟲
知識點
那么有沒有什么辦法可以做到幾乎毫無痕跡地爬取數據呢?答案是有。當然可能有讀者會認為可以使用Selenium + ChromeDriver。這種方式只能操作網頁。本章將要介紹針對Android原生App的爬蟲。
- Android測試環境的搭建。
- 使用Python操作Android手機。
- 使用Python操作Android手機實現爬蟲。
實現原理
目前,Android App主要有兩種實現形式。第一種是Android原生App。這種App的全部或者大部分內容使用Android提供的各個接口來開發,例如Android版的微信就是一個Android原生的App。第二種是基于網頁的App。這種App本質上就是一個瀏覽器,里面的所有內容實際上都是網頁。例如,12306的App就是這樣一種基于網頁的App。
Android原生App爬蟲(以下簡稱App爬蟲)可以直接讀取Android原生App上面的文本信息。
UI Automator Viewer
設置好環境變量以后,在終端窗口輸入“uiautomatorviewer”并按Enter鍵,如果彈出UI Automator Viewer窗口,表明環境設置成功。
Android手機連接到計算機上,保持手機屏幕為亮起狀態,單擊UI Automator Viewer左上角文件夾右側的手機圖標,如果能夠看到手機屏幕出現在窗口中,則表示一切順利,環境搭建成功完成。如果在這個過程中手機彈出了任何警告窗口,都選擇“運行”或者“確定”。
使用Python操縱手機
pip install uiautomator有一點需要特別說明,UI Automator Viewer與Python uiautomator不能同時使用。
與Selenium一樣,要操作手機上面的元素,首先要找到被操作的東西。以打開微信為例,首先翻到有微信的那一頁
from uiautomator import Device device = Device() device(text='微信').click()如果計算機上面只連接了一臺Android手機,那么初始化設備連接只需要使用device = Device()即可。那么如果計算機上連接了很多臺手機,該怎么辦呢?此時就需要指定手機的串號。要查看手機串號,需要在終端輸入以下命令:
adb devices -l從輸出的內容可以看到手機的串號
選擇器
如何知道有哪些選擇器可供使用呢?請執行以下代碼:
from uiautomator import Device device = Device() print(device.dump())此時終端會以XML輸出當前手機屏幕顯示的窗口布局信息。
這里的XML就相當于網頁中的HTML,用來描述窗口上面各個部分的布局信息。
XML的格式與HTML非常像,格式為:<標簽 屬性1=“屬性值1” 屬性2=“屬性值2”>文本</標簽>
操作
- 獲得屏幕文字;
- 滾動屏幕;
- 滑動屏幕;
- 點擊屏幕;
- 輸入文字;
- 判斷元素是否存在;
- 點亮關閉屏幕;
- 操作實體按鍵;
- watcher。
多設備應用(群控)
使用uiautomator來做爬蟲開發,最主要的瓶頸在于速度。因為屏幕上的元素加載是需要時間的,這個時間受到手機性能和網速的多重限制。因此比較好的辦法是使用多臺Android手機實現分布式抓取。使用USBHub擴展計算機的USB口以后,一臺計算機控制30臺Android手機是完全沒有問題的。只要能實現良好的調度和任務派分邏輯,就可以大大提高數據的抓取效率。
App爬蟲系統架構的形式
練習:BOSS直聘爬蟲
任務目標:BOSS直聘App。
BOSS直聘是一個招聘App,在上面可以看到很多的工作。 App職位列表如圖10-44所示。
使用uiautomator開發一個爬蟲,從手機上爬取每一個職位的名稱、薪資、招聘公司、公司地址、工作經驗要求和學歷。
小結
- 本章主要講解了如何通過Python操作手機來獲取Android原生App中的文字內容。Python使用uiautomator這個第三方庫來操作Android手機的UiAutomator,從而實現模擬人們對手機屏幕的任何操作行為,并直接讀取屏幕上的文字。
- 使用uiautomator來開發爬蟲,要打通流程非常簡單。但是需要特別注意處理各種異常的情況。同時,由于手機速度的問題,應該使用多臺手機構成一個集群來提高抓取的速率。
- 最后,如果使用手機集群來進行數據抓取,并且需要抓取的App數據來自網絡,那么需要考慮無線路由器的負荷。當同時連接無線路由器的設備超過一定數量時,有可能導致部分甚至所有設備都無法上網。這就需要使用工業級路由器來緩解。
- 無線信號相互干擾也是一個比較麻煩的問題。使用5G信道能緩解,但一般便宜的Android手機不支持5G的Wi-Fi信道,此時能做的就是把手機盡量分開,不要放在一起。使用電磁屏蔽網,將每10個手機和一個無線路由器為一組包裹起來,也能起到一定的阻隔Wi-Fi信號的作用。
練習
使用Android手機來爬取一個原生App的數據。
第11章 Scrapy
知識點
- 在Windows、Mac OS和Linux下搭建Scrapy環境。
- 使用Scrapy獲取網絡源代碼。
- 在Scrapy中通過XPath解析數據。
- 在Scrapy中使用MongoDB。
- 在Scrapy中使用Redis。
在Mac OS下安裝Scrapy
pip install scrapy創建項目
$ scrapy startproject offcn # offcn 是項目名$ cd offcn $ scrapy genspider jobbank zw.offcn.com # jobbank 是爬蟲名 # zw.offcn.com 是爬取的網址# 運行 $ scrapy crawl jobbank那么如何使用PyCharm來運行或者調試Scrapy的爬蟲呢?
為了實現這個目的,需要創建另外一個Python文件。文件名可以取任意合法的文件名。這里以“main.py”為例。
main.py文件內容如下:
from scrapy import cmdline cmdline.execute("scrapy crawl jobbank".split())Scrapy的工程結構
- spiders文件夾:存放爬蟲文件的文件夾。
- items.py:定義需要抓取的數據。
- pipelines.py:負責數據抓取以后的處理工作。
- settings.py:爬蟲的各種配置信息。
但是為什么還有items.py和pipelines.py這兩個文件呢?這是由于Scrapy的理念是將數據爬取和數據處理分開。
items.py文件用于定義需要爬取哪些內容。
pipelines.py文件用于對數據做初步的處理,包括但不限于初步清洗數據、存儲數據等。在pipelines中可以將數據保存到MongoDB。
Scrapy與MongoDB
一個Scrapy工程可以有多個爬蟲;再看items.py文件,可以發現,在一個items.py里面可以對不同的爬蟲定義不同的抓取內容Item。
接下來設置pipelines.py。在這個文件中,需要寫出將數據保存到MongoDB的代碼。而這里的代碼,就是最簡單的初始化MongoDB的連接,保存數據。
Scrapy與Redis
Scrapy是一個分布式爬蟲的框架,如果把它像普通的爬蟲一樣單機運行,它的優勢將不會被體現出來。
因此,要讓Scrapy往分布式爬蟲方向發展,就需要學習Scrapy與Redis的結合使用。Redis在Scrapy的爬蟲中作為一個隊列存在。
使用Redis緩存網頁并自動去重
pip install scrapy_redis小結
本章主要講了Python分布式爬蟲框架Scrapy的安裝和使用。
Scrapy在Windows中的安裝最為煩瑣,在Mac OS中的安裝最為簡單。
由于Scrapy需要依賴非常多的第三方庫文件,因此建議無論使用哪個系統安裝,都要將Scrapy安裝到Virtualenv創建的虛擬Python環境中,從而避免影響系統的Python環境。
使用Scrapy爬取網頁,最核心的部分是構建XPath。而其他的各種配置都是一次配好、終生使用的。由于Scrapy的理念是將數據抓取的動作和數據處理的動作分開,因此對于數據處理的各種邏輯應該讓pipeline來處理。數據抓取的部分只需要關注如何使用XPath提取數據。數據提取完成以后,提交給pipeline處理即可。
由于Scrapy爬蟲爬取數據是異步操作,所以從一個頁面跳到另一個頁面是異步的過程,需要使用回調函數。
Scrapy爬取到的數據量非常大,所以應該使用數據庫來保存。使用MongoDB會讓數據保存工作變得非常簡單。
要讓Scrapy使用MongoDB,只需要在pipeline中配置好數據保存的流程,再在settings.py中配置好ITEM_PIPELINES和MongoDB的信息即可。
使用Redis做緩存是從爬蟲邁向分布式爬蟲的一個起點。
Scrapy安裝scrapy_redis組件以后,就可以具備使用Redis的能力。
在Scrapy中使用Redis需要修改爬蟲的父類,需要在settings.py中設置好爬蟲的調度和去重。
同時對于Python 3,需要修改scrapy_redis的一行代碼,才能讓爬蟲正常運行。
練習
請完成網站爬蟲開發,并實現爬蟲的翻頁功能,從而可以爬到1~5頁所有的文章。下一頁的URL請使用爬蟲從當前頁面獲取,切勿根據URL的規律手動構造。
提示,對于翻頁功能,實際上相當于將回調函數的函數名寫成它自己的: callback=self.parse。parse方法可自己調用自己,不過傳入的URL是下一頁的URL。這有點像遞歸,不過遞歸用的是return,而這里用的是yield。
請思考一個問題,在請求文章詳情頁的時候,設置了請求頭,但是Scrapy請求文章列表頁的時候,在哪里設置請求頭?請求列表頁的時候,爬蟲直接從Redis得到網址,自動發起了請求,完全沒讓開發者自己設置請求頭。這其實是一個非常大的隱患,因為不設置請求頭,網站立刻就能知道這個請求來自Scrapy,這是非常危險的。
請讀者查詢scrapy_redis的文檔,查看如何使用make_requests_ from_url(self, url)這個方法。
第12章 Scrapy高級應用
知識點
- 開發Scrapy中間件。
- 使用Scrapyd部署爬蟲。
- Nginx的安裝和反向代理。
- 了解爬蟲的分布式框架。
Middleware
中間件是Scrapy里面的一個核心概念。使用中間件可以在爬蟲的請求發起之前或者請求返回之后對數據進行定制化修改,從而開發出適應不同情況的爬蟲。
“中間件”這個中文名字和前面章節講到的“中間人”只有一字之差。它們做的事情確實也非常相似。中間件和中間人都能在中途劫持數據,做一些修改再把數據傳遞出去。不同點在于,中間件是開發者主動加進去的組件,而中間人是被動的,一般是惡意地加進去的環節。
中間件主要用來輔助開發,而中間人卻多被用來進行數據的竊取、偽造甚至攻擊。
在Scrapy中有兩種中間件:下載器中間件(Downloader Middleware)和爬蟲中間件(Spider Middleware)。
Downloader Middleware
更換代理IP,更換Cookies,更換User-Agent,自動重試。
加入中間件以后的爬蟲流程
開發代理中間件
代理中間件的可用代理列表不一定非要寫在settings.py里面,也可以將它們寫到數據庫或者Redis中。一個可行的自動更換代理的爬蟲系統,應該有如下的3個功能。
- 有一個小爬蟲ProxySpider去各大代理網站爬取免費代理并驗證,將可以使用的代理IP保存到數據庫中。
- 在ProxyMiddlerware的process_request中,每次從數據庫里面隨機選擇一條代理IP地址使用。
- 周期性驗證數據庫中的無效代理,及時將其刪除。
激活中間件
中間件寫好以后,需要去settings.py中啟動。
Scrapy自帶中間件及其順序編號
開發UA中間件
從settings.py配置好的UA列表中隨機選擇一項,加入到請求頭中
開發Cookies中間件
對于需要登錄的網站,可以使用Cookies來保持登錄狀態。那么如果單獨寫一個小程序,用Selenium持續不斷地用不同的賬號登錄網站,就可以得到很多不同的Cookies。由于Cookies本質上就是一段文本,所以可以把這段文本放在Redis里面。這樣一來,當Scrapy爬蟲請求網頁時,可以從Redis中讀取Cookies并給爬蟲換上。這樣爬蟲就可以一直保持登錄狀態。
在中間件中集成Selenium
對于一些很麻煩的異步加載頁面,手動尋找它的后臺API代價可能太大。這種情況下可以使用Selenium和ChromeDriver或者Selenium和PhantomJS來實現渲染網頁。
在中間件中集成了Selenium以后,就可以像爬取普通網頁一樣爬取異步加載的頁面
在中間件里重試
在爬蟲的運行過程中,可能會因為網絡問題或者是網站反爬蟲機制生效等原因,導致一些請求失敗。在某些情況下,少量的數據丟失是無關緊要的,例如在幾億次請求里面失敗了十幾次,損失微乎其微,沒有必要重試。但還有一些情況,每一條請求都至關重要,容不得有一次失敗。此時就需要使用中間件來進行重試。
在中間件里處理異常
在默認情況下,一次請求失敗了,Scrapy會立刻原地重試,再失敗再重試,如此3次。如果3次都失敗了,就放棄這個請求。這種重試邏輯存在一些缺陷。以代理IP為例,代理存在不穩定性,特別是免費的代理,差不多10個里面只有3個能用。而現在市面上有一些收費代理IP提供商,購買他們的服務以后,會直接提供一個固定的網址。
把這個網址設為Scrapy的代理,就能實現每分鐘自動以不同的IP訪問網站。如果其中一個IP出現了故障,那么需要等一分鐘以后才會更換新的IP。在這種場景下,Scrapy自帶的重試邏輯就會導致3次重試都失敗。
這種場景下,如果能立刻更換代理就立刻更換;如果不能立刻更換代理,比較好的處理方法是延遲重試。而使用Scrapy_redis就能實現這一點。
爬蟲的請求來自于Redis,請求失敗以后的URL又放回Redis的末尾。
一旦一個請求原地重試3次還是失敗,那么就把它放到Redis的末尾,這樣Scrapy需要把Redis列表前面的請求都消費以后才會重試之前的失敗請求。這就為更換IP帶來了足夠的時間。
下載器中間件功能總結
能在中間件中實現的功能,都能通過直接把代碼寫到爬蟲中實現。使用中間件的好處在于,它可以把數據爬取和其他操作分開。在爬蟲的代碼里面專心寫數據爬取的代碼;在中間件里面專心寫突破反爬蟲、登錄、重試和渲染AJAX等操作。
對團隊來說,這種寫法能實現多人同時開發,提高開發效率;對個人來說,寫爬蟲的時候不用考慮反爬蟲、登錄、驗證碼和異步加載等操作。另外,寫中間件的時候不用考慮數據怎樣提取。一段時間只做一件事,思路更清晰。
爬蟲中間件
爬蟲中間件的用法與下載器中間件非常相似,只是它們的作用對象不同。
下載器中間件的作用對象是請求request和返回response;爬蟲中間鍵的作用對象是爬蟲,更具體地來說,就是寫在spiders文件夾下面的各個文件。
Scrapy的數據流圖
其中,4、5表示下載器中間件,6、7表示爬蟲中間件。爬蟲中間件會在以下幾種情況被調用。
- 當運行到yield scrapy.Request()或者yield item的時候,爬蟲中間件的process_spider_output()方法被調用。
- 當爬蟲本身的代碼出現了Exception的時候,爬蟲中間件的process_spider_exception()方法被調用。
- 當爬蟲里面的某一個回調函數parse_xxx()被調用之前,爬蟲中間件的process_spider_input()方法被調用。
- 當運行到start_requests()的時候,爬蟲中間件的process_start_ requests()方法被調用。
在中間件處理爬蟲本身的異常
在爬蟲中間件里面可以處理爬蟲本身的異常。
下載器中間件里面的報錯一般是由于外部原因引起的,和代碼層面無關。而現在的這種報錯是由于代碼本身的問題導致的,是代碼寫得不夠周全引起的。
激活爬蟲中間件
在settings.py中,在下載器中間件配置項的上面就是爬蟲中間件的配置項,它默認也是被注釋了的,解除注釋,并把自定義的爬蟲中間件添加進去即可
幾個自帶的爬蟲中間件
爬蟲中間件輸入/輸出
在爬蟲中間件里面還有兩個不太常用的方法,分別為process_ spider_input(response, spider)和process_spider_output(response, result, spider)。其中,process_spider_input(response, spider)在下載器中間件處理完成后,馬上要進入某個回調函數parse_xxx()前調用。
process_spider_output(response, result, output)是在爬蟲運行yield item或者yield scrapy.Request()的時候調用。
在這個方法處理完成以后,數據如果是item,就會被交給pipeline;如果是請求,就會被交給調度器,然后下載器中間件才會開始運行。
所以在這個方法里面可以進一步對item或者請求做一些修改。這個方法的參數result就是爬蟲爬出來的item或者scrapy.Request()。由于yield得到的是一個生成器,生成器是可以迭代的,所以result也是可以迭代的,可以使用for循環來把它展開。
爬蟲的部署
一般情況下,爬蟲會使用云服務器來運行,這樣可以保證爬蟲24h不間斷運行。
FTP:使用FTP來上傳代碼,不僅非常不方便,而且經常出現把方向搞反,導致本地最新的代碼被服務器代碼覆蓋的問題。
Git:好處是可以進行版本管理,不會出現代碼丟失的問題。但操作步驟多,需要先在本地提交,然后登錄服務器,再從服務器上面把代碼下載下來。如果有很多服務器的話,每個服務器都登錄并下載一遍代碼是非常浪費時間的事情。
Docker:好處是可以做到所有服務器都有相同的環境,部署非常方便。但需要對Linux有比較深入的了解,對新人不友好,上手難度比較大。
為了克服上面的種種問題,本書將會使用Scrapy官方開發的爬蟲部署、運行、管理工具:Scrapyd。
Scrapyd
Scrapyd是Scrapy官方開發的,用來部署、運行和管理Scrapy爬蟲的工具。使用Scrapyd,可以實現一鍵部署Scrapy爬蟲,訪問一個網址就啟動/停止爬蟲。Scrapyd自帶一個簡陋網頁,可以通過瀏覽器看到爬蟲當前運行狀態或者查閱爬蟲Log。Scrapyd提供了官方API,從而可以通過二次開發實現更多更加復雜的功能。
Scrapyd可以同時管理多個Scrapy工程里面的多個爬蟲的多個版本。如果在多個云服務器上安裝Scrapyd,可以通過Python寫一個小程序,來實現批量部署爬蟲、批量啟動爬蟲和批量停止爬蟲。
pip install scrapyd上傳Scrapy爬蟲的工具
Scrapyd需要安裝到云服務器上,如果讀者沒有云服務器,或者想在本地測試,那么可以在本地也安裝一個。
接下來需要安裝scrapyd-client,這是用來上傳Scrapy爬蟲的工具,也是Python的一個第三方庫,使用pip安裝即可:
pip install scrapyd-client這個工具只需要安裝到本地計算機上,不需要安裝到云服務器上
啟動Scrapyd
接下來需要在云服務器上啟動Scrapyd。在默認情況下,Scrapyd不能從外網訪問,為了讓它能夠被外網訪問,需要創建一個配置文件。除了bind_address這一項外,其他都可以保持默認。bind_address這一項的值設定為當前這臺云服務器的外網IP地址。配置文件放好以后,在終端或者CMD中輸入scrapyd并按Enter鍵,這樣Scrapyd就啟動了。
scrapyd此時打開瀏覽器,輸入“http://云服務器IP地址:6800”格式的地址就可以打開Scrapyd自帶的簡陋網頁
部署爬蟲
Scrapyd啟動以后,就可以開始部署爬蟲了。打開任意一個Scrapy的工程文件,可以看到在工程的根目錄中,Scrapy已經自動創建了一個scrapy.cfg文件,打開這個文件。現在需要把第10行的注釋符號去掉,并將IP地址改為Scrapyd所在云服務器的IP地址。
最后,使用終端或者CMD進入這個Scrapy工程的根目錄,執行下面這一行命令部署爬蟲:
scrapyd-deployMac OS和Linux系統,啟動/停止爬蟲
curl這個發起網絡請求的自帶工具
在上傳了爬蟲以后,就可以啟動爬蟲了。對于Mac OS和Linux系統,啟動和停止爬蟲非常簡單。要啟動爬蟲,需要在終端輸入下面這一行格式的代碼。
curl http://云服務器IP地址:6800/schedule.json -d project=爬蟲工程名 -d spider=爬蟲名如果爬蟲的運行時間太長,希望提前結束爬蟲,那么可以使用下面格式的命令來實現:
curl http://爬蟲服務器IP地址:6800/cancel.json -d project=工程名 -d job=爬蟲JOBID(在網頁上可以查詢到)運行以后,相當于對爬蟲按下了Ctrl+C組合鍵的效果
Windows系統,啟動/停止爬蟲
沒有像Mac OS和Linux的終端一樣擁有curl這個發起網絡請求的自帶工具。但既然是發起網絡請求,那么只需要借助Python和requests就可以完成。
- 使用requests發送請求啟動爬蟲
- 使用requests發送請求關閉爬蟲
在Python中執行命令部署爬蟲
由于部署爬蟲的時候直接執行scrapyd-deploy命令,所以如何使用Python來自動化部署爬蟲呢?其實也非常容易。在Python里面使用os這個自帶模塊就可以執行系統命令
批量部署
使用Python與requests的好處不僅在于可以幫助Windows實現控制爬蟲,還可以用來實現批量部署、批量控制爬蟲。
假設有一百臺云服務器,只要每一臺上面都安裝了Scrapyd,那么使用Python來批量部署并啟動爬蟲所需要的時間不超過1min。
這里給出一個批量部署并啟動爬蟲的例子,首先在爬蟲的根目錄下創建一個scrapy_template.cfg文件。
權限管理
為了彌補Scrapyd沒有權限管理系統這一短板,就需要借助其他方式來對網絡請求進行管控。帶權限管理的反向代理就是一種解決辦法。
Nginx的介紹
Nginx讀作Engine X,是一個輕量級的高性能網絡服務器、負載均衡器和反向代理。
為了解決Scrapyd的問題,需要用Nginx做反向代理。
正向代理與反向代理
所謂“反向代理”,是相對于“正向代理”而言的。前面章節所用到的代理是正向代理。正向代理幫助請求者(爬蟲)隱藏身份,爬蟲通過代理訪問服務器,服務器只能看到代理IP,看不到爬蟲;
反向代理幫助服務器隱藏身份。用戶只知道服務器返回的內容,但不知道也不需要知道這個內容是在哪里生成的。
使用Nginx來保護Scrapyd
使用Nginx反向代理到Scrapyd以后,Scrapyd本身只需要開通內網訪問即可。
用戶訪問Nginx,Nginx再訪問Scrapyd,然后Scrapyd把結果返回給Nginx,Nginx再把結果返回給用戶。這樣只要在Nginx上設置登錄密碼,就可以間接實現Scrapyd的訪問管控了
Nginx的安裝
sudo apt-get install nginx安裝好以后,需要考慮以下兩個問題。
- 服務器是否有其他的程序占用了80端口。
- 服務器是否有防火墻。
/etc/nginx/sites-available/default文件,將80全部改成81并保存
sudo systemctl restart nginx配置反向代理
首先打開/etc/scrapyd/scrapyd.conf,把bind_address這一項重新改為127.0.0.1,并把http_port這一項改為6801。把Scrapyd設置為只允許內網訪問,端口為6801
接下來配置Nginx,在/etc/nginx/sites-available文件夾下創建一個scrapyd.conf,其內容為:
server {listen 6800;location / {proxy_pass http://127.0.0.1:6801/;auth_basic "Restricted";auth_basic_user_file /etc/nginx/conf.d/.htpasswd;}}使用basic auth權限管理方法,對于通過授權的用戶,將它對6800端口的請求轉到服務器本地的6801端口。需要注意配置里面的記錄授權文件的路徑這一項:auth_basic_user_file /etc/nginx/conf.d/.htpasswd
在后面會將授權信息的記錄文件放在/etc/nginx/conf.d/.htpasswd這個文件中。寫好這個配置以后,保存。
接下來,執行下面的命令,在/etc/nginx/sites-enabled文件夾下創建一個軟連接:
sudo ln -s /etc/nginx/sites-available/scrapyd.conf /etc/nginx/sites-enabled/軟連接創建好以后,需要生成賬號和密碼的配置文件。首先安裝apache2-utils軟件包:
sudo apt-get install apache2-utils安裝完成apache2-utils以后,cd進入/etc/nginx/conf.d文件夾,并執行命令為用戶kingname生成密碼文件:
sudo htpasswd -c .htpasswd kingname上面的命令會在/etc/nginx/conf.d文件夾下生成一個.htpasswd的隱藏文件。有了這個文件,Nginx就能進行權限驗證了。
接下來重啟Nginx:
sudo systemctl restart nginx重啟完成以后,啟動Scrapyd,再在瀏覽器上訪問格式為“http://服務器IP:6800”的網址
配置Scrapy工程
由于為Scrapyd添加了權限管控,因此在12.2.1小節中涉及的部署爬蟲、啟動/停止爬蟲的地方都要做一些小修改。
首先是部署爬蟲,為了讓scrapyd-deploy能成功地輸入密碼,需要修改爬蟲根目錄的scrapy.cfg文件,添加username和password兩項,其中username對應賬號,password對應密碼。
配置好scrapy.cfg以后,部署爬蟲的命令不變,還是進入這個Scrapy工程的根目錄,執行以下代碼即可:
scrapyd-deploy使用curl啟動/關閉爬蟲,只需要在命令上加上賬號參數即可。賬號參數為“-u 用戶名:密碼”。
所以,啟動爬蟲的命令為:
curl http://45.76.110.210:6800/schedule.json -d project=工程名 -d spider=爬蟲名 -u kingname:genius停止爬蟲的命令為:```shell curl http://45.76.110.210:6800/cancel.json -d project=工程名 -d job=爬蟲JOBID-u kingname:genius如果使用Python與requests編寫腳本來控制爬蟲,那么賬號信息可以作為POST方法的一個參數,參數名為auth,值為一個元組,元組第0項為賬號,第1項為密碼:
result = requests.post(start_url, data=start_data, auth=('kingname', 'genius')).text result = requests.post(end_url, data=end_data, auth=('kingname', 'genius')).text分布式架構介紹
在前面章節中已經講到了把目標放到Redis里面,然后讓多個爬蟲從Redis中讀取目標再爬取的架構,這其實就是一種主—從式的分布式架構。使用Scrapy,配合scrapy_redis,再加上Redis,也就實現了一個所謂的分布式爬蟲。
實際上,這種分布式爬蟲的核心概念就是一個中心結點,也叫Master。它上面跑著一個Redis,所有的待爬網站的網址都在里面。其他云服務器上面的爬蟲(Slave)就從這個共同的Redis中讀取待爬網址。
分布式爬蟲架構圖
只要能訪問這個Master服務器,并讀取Redis,那么其他服務器使用什么系統什么提供商都沒有關系。
例如,使用Ubuntu作為爬蟲的Master,用來做任務的派分。
使用樹莓派、Windows 10 PC和Mac來作為分布式爬蟲的Slave,用來爬取網站,并將結果保存到Mac上面運行的MongoDB中。
其中,作為Master的Ubuntu服務器僅需要安裝Redis即可,它的作用僅僅是作為一個待爬網址的臨時中轉,所以甚至不需要安裝Python。
在Mac、樹莓派和Windows PC中,需要安裝好Scrapy,并通過Scrapyd管理爬蟲。由于爬蟲會一直監控Master的Redis,所以在Redis沒有數據的時候爬蟲處于待命狀態。
當目標被放進了Redis以后,爬蟲就能開始運行了。由于Redis是一個單線程的數據庫,因此不會出現多個爬蟲拿到同一個網址的情況。
如何選擇Master
嚴格來講,Master只需要能運行Redis并且能被其他爬蟲訪問即可。但是如果能擁有一個公網IP則更好。這樣可以從世界上任何一個能訪問互聯網的地方訪問Master。但如果實在沒有云服務器,也并不是說一定得花錢買一個,如果自己有很多臺計算機,完全可以用一臺計算機來作為Master,其他計算機來做Slave。
Master也可以同時是Slave。在第11章的例子中,Scrapy和Redis是安裝在同一臺計算機中的。這臺計算機既是Master又是Slave。
Master一定要能夠被其他所有的Slave訪問。所以,如果所有計算機不在同一個局域網,那么就需要想辦法弄到一臺具有公網IP的計算機或者云服務器。
在中國,大部分情況下,電信運營商分配到的IP是內網IP。在這種情況下,即使知道了IP地址,也沒有辦法從外面連進來。
在局域網里面,因為局域網共用一個出口,局域網內的所有共用同一個公網IP。對網站來說,這個IP地址訪問頻率太高了,肯定不是人在訪問,從而被網站封鎖的可能性增大。而使用分布式爬蟲,不僅僅是為了提高訪問抓取速度,更重要的是降低每一個IP的訪問頻率,使網站誤認為這是人在訪問。所以,如果所有的爬蟲確實都在同一個局域網共用一個出口的話,建議為每個爬蟲加上代理。
在實際生產環境中,最理想的情況是每一個Slave的公網IP都不一樣,這樣才能做到既能快速抓取,又能減小被反爬蟲機制封鎖的機會。
小結
本章主要介紹了Scrapy中間件的高級用法和爬蟲的批量部署。
使用中間件可以讓爬蟲專注于提取數據,而像更換IP、獲取登錄Session等事情全部都交給中間件來做。這樣可以讓爬蟲本身的代碼更加簡潔,也便于協同開發。
使用Scrapyd來部署爬蟲,可以實現遠程開關爬蟲和批量部署爬蟲,從而實現分布式爬蟲。
第13章 爬蟲開發中的法律和道德問題
小結
- 在爬蟲開發和數據采集的過程中,閱讀網站的協議可以有效發現并規避潛在的法律風險。
- 爬蟲在爬取網站的時候控制頻率,善待網站,才能讓爬蟲運行得更長久。
總結
以上是生活随笔為你收集整理的Python爬虫开发从入门到实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奥升德推出Acteev Protect™
- 下一篇: 10019---JavaScript--