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