本文由博主最初發(fā)布于華為開發(fā)者社區(qū)把敢,原地址:https://portal.huaweicloud.com/blogs/46c0ffaad0ee11e7b8317ca23e93a891降宅,再次感謝華為對(duì)文章的認(rèn)可與鼓勵(lì) ??
爬蟲是時(shí)下十分熱門的一種程序狈孔,谷歌赃磨、百度等搜索引擎以及今日頭條、即刻等熱門應(yīng)用均建立在爬蟲程序的基礎(chǔ)上动壤,構(gòu)成互聯(lián)網(wǎng)巨大流量的入口牢撼。那么現(xiàn)代的爬蟲是如何工作,我們自己又如何借助華為云服務(wù)搭建自己的爬蟲呢爽锥?下面我們以爬取華為開發(fā)者社區(qū)所有的博客為例涌韩,利用時(shí)下熱門的 PySpider 框架快速搭建一個(gè)基本的爬蟲服務(wù)。
爬蟲的基本原理
如果把互聯(lián)網(wǎng)看作是各個(gè)站點(diǎn)相互引用氯夷、串聯(lián)形成的一張網(wǎng)臣樱,那爬蟲顧名思義就是在這張網(wǎng)上穿梭的蜘蛛(Spider)。一個(gè)頁面作為互聯(lián)網(wǎng)中的一個(gè)節(jié)點(diǎn),很可能會(huì)包含有指向其他頁面的鏈接雇毫,可以理解為節(jié)點(diǎn)之間的連線玄捕。爬蟲通過遍歷節(jié)點(diǎn)之間的連線,確定頁面之間的網(wǎng)絡(luò)結(jié)構(gòu)棚放,抓取所需要的信息枚粘。
在開始之前,我們應(yīng)該清楚一些基本的概念:
WWW (World Wide Web, 萬維網(wǎng)) 是一個(gè)由互相連接的超文本頁面組成的系統(tǒng)
每個(gè)頁面有對(duì)應(yīng)的 URL(Uniform Resource Locator, 網(wǎng)址) 所標(biāo)記
頁面通過 HTTP (Hypertext Transfer Protocol, 超文本傳輸協(xié)議) 傳輸
網(wǎng)頁使用 HTML(HyperText Markup Language, 超文本標(biāo)記語言) 表示其頁面結(jié)構(gòu)
那么具體而言飘蚯,爬蟲程序的運(yùn)行過程是:
尋找包含所需信息的頁面 URL
通過 HTTP 獲取頁面
從 HTML 中解析出信息
從中發(fā)現(xiàn)更多包含所需信息的 URL馍迄,跳轉(zhuǎn)到步驟2
在實(shí)現(xiàn)層面上,爬蟲程序運(yùn)行之初局骤,需要提供給它一個(gè) URL 列表攀圈,引導(dǎo)爬蟲從這些頁面開始訪問。當(dāng)爬蟲訪問這些 URL 時(shí)峦甩,會(huì)識(shí)別出頁面中的所需的 URL赘来,并將這些 URL 加入到待訪問的 URL 列表中。URL 列表中的地址會(huì)以一系列策略遞歸地被爬蟲所訪問凯傲,爬蟲會(huì)記錄這些地址所對(duì)應(yīng)的頁面以及一些頁面信息撕捍。
PySpider 簡(jiǎn)介
PySpider 是一個(gè)基于 Python 開發(fā)的強(qiáng)大爬蟲系統(tǒng),它具有以下特點(diǎn):
用戶使用 Python 編寫腳本以控制爬蟲的工作流程
包含基于 WebUI 的可視化腳本編輯器泣洞、任務(wù)監(jiān)視器、項(xiàng)目管理器及結(jié)果預(yù)覽工具
可以使用 MySQL默色、MongoDB球凰、Redis、SQLite腿宰、ElasticSearch呕诉、PostgreSQL 等工具結(jié)合 SQLAlchemy 作為存儲(chǔ)后端
可以使用 RabbitMQ、Beanstalk吃度、Redis 和 Kombu 作為消息隊(duì)列
支持任務(wù)優(yōu)先級(jí)甩挫、任務(wù)重試、周期任務(wù)椿每、根據(jù)頁面壽命自動(dòng)重爬等高級(jí)功能
分布式架構(gòu)伊者,支持 js 解析,支持 Python2 與 Python 3
總結(jié)一下间护,利用 PySpider亦渗,可以方便地搭建出專業(yè)的爬蟲框架。
實(shí)戰(zhàn)操作
1. 配置華為云服務(wù)
作為一個(gè)專業(yè)穩(wěn)定的爬蟲服務(wù)汁尺,而不是簡(jiǎn)單批量下載頁面的工具法精,我們往往需要搭建一個(gè)服務(wù)器,來讓它自動(dòng)地、定期地去工作搂蜓,這時(shí)一個(gè)穩(wěn)定可信賴的云服務(wù)就至關(guān)重要了狼荞,我們這里就以業(yè)界領(lǐng)先的華為云服務(wù)為例,搭建爬蟲的基礎(chǔ)運(yùn)行環(huán)境帮碰。
我們首先需要在 https://console.huaweicloud.com/ecm/?locale=zh-cn#/ecs/createVm 申請(qǐng)一個(gè)臺(tái)云服務(wù)器相味。
我的服務(wù)器配置為
這里我選擇了個(gè)人更偏好的 CentOS 7.3 系統(tǒng),下面就以 CentOS 7.3 系統(tǒng)為例收毫,繼續(xù)接下來的搭建攻走。
2. 環(huán)境準(zhǔn)備
在安裝 PySpider 前,我們可以做一些環(huán)境準(zhǔn)備此再,來提升服務(wù)的穩(wěn)定性昔搂,這也是在搭建服務(wù)時(shí)的好習(xí)慣。
首先更新 yum输拇,這步會(huì)比較漫長(zhǎng)
yum update -y
安裝 EPEL(Extra Packages for Enterprise Linux)摘符,用以安裝下面需要的包
yum install epel-release
安裝依賴庫
yum install python-pip python-devel libxml2-devel python-lxml libxslt-devel openssl-devel -y
升級(jí) pip
pip install --upgrade pip
至此,運(yùn)行環(huán)境及必需的依賴已升級(jí)至最新版本策吠。下面我們根據(jù)喜好進(jìn)行一些可選的配置逛裤,首先安裝 MariaDB 作為爬蟲的后端數(shù)據(jù)庫。
yum install mariadb-server mariadb -y
啟動(dòng) MariaDB 服務(wù)
systemctl start mariadb
如果有需要猴抹,可以設(shè)置數(shù)據(jù)庫 root 用戶的密碼(your_password 應(yīng)替換為你的密碼)带族,當(dāng)然也可以不設(shè)置:
mysqladmin -u root password "your_password"
這時(shí)可以使用如下命令來檢查 MariaDB 是否配置成功(如果未設(shè)置密碼,直接使用 mysql
即可):
mysql -u root -p
然后輸入剛才設(shè)置的密碼蟀给,如果沒有問題蝙砌,應(yīng)該可以看到以 MariaDB [(none)]>
的提示。
此時(shí)輸入 SHOW DATABASES;
語句跋理,查看所有數(shù)據(jù)庫择克,如果有類似下面的輸出,則配置正常前普。
MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.00 sec)
輸入 exit
退出肚邢,進(jìn)入下一步。
接下來拭卿,安裝 Redis骡湖,作為消息隊(duì)列使用。
yum install redis -y
在啟動(dòng) Redis 之前记劈,需要做一些設(shè)置勺鸦,讓 Redis 作為一個(gè)服務(wù)運(yùn)行。首先創(chuàng)建一份配置文件:
mkdir -p /etc/redis
cp /etc/redis.conf /etc/redis/redis.conf
修改 /etc/redis/redis.conf 目木,將其中的 daemonize
項(xiàng)修改為 yes
:
daemonize yes
配置文件路徑作為參數(shù)换途,啟動(dòng) Redis 服務(wù):
redis-server /etc/redis/redis.conf
3. 部署PySpider
在安裝 PySpider 之前懊渡,需要先安裝 mysql-connector 及 redis 兩個(gè)依賴,而最新的 mysql-connector 2.2 有 protobuf 的依賴军拟,protobuf 筆者目前還沒找到合適的安裝方法剃执,所以這里退而求其次懈息,安裝不依賴 protobuf 的 mysql-connector 2.1.6
pip install mysql-connector==2.1.6 redis
安裝 PySpider
pip install pyspider
創(chuàng)建 PySpider 配置目錄
mkdir /etc/pyspider
在 /etc/pyspider
目錄下創(chuàng)建 pyspider.conf.json 文件肾档,以下可作為參考:
{
"taskdb": "mysql+taskdb://root:your_password@localhost:3306/taskdb",
"projectdb": "mysql+projectdb://root:your_password@localhost:3306/projectdb",
"resultdb": "mysql+resultdb://root:your_password@localhost:3306/resultdb",
"message_queue": "redis://localhost:6379/db",
"webui": {
"username": "sunny",
"password": "your_user_password",
"need-auth": true
}
}
其中 taskdb
, projectdb
和 resultdb
分別為任務(wù)、項(xiàng)目及結(jié)果的數(shù)據(jù)庫連接地址辫继,這里我們用一個(gè) MariaDB 服務(wù)存儲(chǔ)這三類數(shù)據(jù)怒见,上面的 root
為我們的 MariaDB 用戶名,后面 your_password
應(yīng)替換為之前設(shè)置的數(shù)據(jù)庫密碼姑宽。 message_queue
后應(yīng)填寫消息隊(duì)列服務(wù)的地址遣耍,這里我們是將 Redis 做為消息隊(duì)列使用。 webui
項(xiàng)中的 username
及 password
是用來配置訪問 WebUI 時(shí)的用戶名和密碼炮车,也可以不設(shè)置用戶名和密碼舵变,只需將 need-auth
改成 false
即可。
接下來創(chuàng)建一個(gè)工作文件夾瘦穆,并啟動(dòng)爬蟲
mkdir ~/pyspider
cd ~/pyspider
pyspider -c /etc/pyspider/pyspider.conf.json
此時(shí)訪問 http://你的服務(wù)器IP地址:5000
纪隙,應(yīng)該可以看到 PySpider 的 Dashboard。
如果訪問失敗扛或,很可能是防火墻組織了發(fā)往5000端口的數(shù)據(jù)包绵咱,這時(shí)我們需要打開5000端口
iptables -A INPUT -p tcp --dport 5000 -j ACCEPT
iptables --flush
此時(shí)重新啟動(dòng)爬蟲,就可以訪問 Dashboard 了熙兔,我們也可以結(jié)合 nohup
讓爬蟲服務(wù)運(yùn)行在后臺(tái):
nohup pyspider -c /etc/pyspider/pyspider.conf.json &
至此 PySpider 服務(wù)的部署就完成了麸拄。
4. 爬取華為開發(fā)者社區(qū)
在搭建好服務(wù)之后,我們的目標(biāo)是建立一個(gè)華為開發(fā)者社區(qū)所有文章的數(shù)據(jù)庫黔姜,也就是要保存社區(qū)中所有的博客文章以及相關(guān)的數(shù)據(jù)。既然要對(duì)這些文章下手蒂萎,那第一件事就是需要找到一個(gè)合適的文章列表秆吵。一個(gè)理想的列表應(yīng)該具有這些特征:
盡可能多地包含指向文章頁面的鏈接
可以通過跳轉(zhuǎn)到下一頁獲取到所有文章的 URL
列表根據(jù)時(shí)間由新到舊排序,方便獲取到最新的文章
很幸運(yùn)五慈,我們發(fā)現(xiàn)社區(qū)博客頁面的首頁就有這樣一個(gè)我們所期待的列表
https://portal.huaweicloud.com/blogs
接下來我們正式開始上手 PySpider纳寂。首先輸入之前設(shè)置的用戶名和密碼進(jìn)入 Dashboard,然后點(diǎn)擊 Create 新建一個(gè) Project泻拦,命名為 huawei_developer
毙芜,初始 URL 填寫我們剛剛找到的列表頁面https://portal.huaweicloud.com/blogs,類型選擇 Script
争拐。
右側(cè)是我們寫爬蟲代碼的編輯區(qū)腋粥,在 PySpider 中,on_start
是整個(gè)爬蟲的入口
@every(minutes=24 * 60)
def on_start(self):
self.crawl('https://portal.huaweicloud.com/blogs', callback=self.index_page)
self.crawl
會(huì)獲取頁面,并調(diào)用 callback
來分析響應(yīng)隘冲,@every
裝飾器表示 on_start
會(huì)每天運(yùn)行一次確保不會(huì)漏掉新的文章闹瞧。
點(diǎn)擊綠色的 Run 按鈕,切換到 follow 頁面展辞,下面會(huì)出現(xiàn)一個(gè)鏈接奥邮,就是我們這里首先要爬取的第一個(gè)頁面,我們這里點(diǎn)擊綠色三角按鈕
在首頁上罗珍,我們需要提取兩種鏈接:一是文章的鏈接洽腺,比如https://portal.huaweicloud.com/blogs/b16fc680d01811e7b8317ca23e93a891
;二是列表翻到下一頁的鏈接覆旱。
我們可以看到蘸朋,在爬取首頁后,默認(rèn)設(shè)置的爬蟲在頁面上找出了127條鏈接通殃。而其中我們想要的頁面鏈接實(shí)際上只存在于文章列表的區(qū)域度液,下面我們將使用類似于 CSS 選擇器的工具來過濾出我們想要的元素。
CSS 選擇器是 CSS 用來選擇 HTML 中元素的工具画舌,CSS 通過這種簡(jiǎn)單的語法指定特定的元素并應(yīng)用一些樣式堕担。由于包含信息的元素往往具有不同的樣式,所以這里使用 CSS 選擇器來過濾元素非常合適曲聂。關(guān)于 CSS 選擇器的更多信息可以參考:
https://www.w3schools.com/cssref/css_selectors.asp
這里我們可以通過 PyQuery 內(nèi)建的 response.doc
對(duì)象使用 CSS 選擇器霹购。
PySpider 提供了一個(gè)叫做 css selector helper 的工具,利用它我們可以更方便地通過點(diǎn)擊元素得到它對(duì)應(yīng)的 css 選擇器朋腋。切換到 web 頁面并點(diǎn)擊 enable css selector helper 就可以使用它齐疙。
當(dāng)光標(biāo)移到元素上時(shí),對(duì)應(yīng)元素會(huì)變成橙色高亮狀態(tài)旭咽。點(diǎn)擊元素贞奋,一條CSS 選擇器信息會(huì)出現(xiàn)在頁面頂部,你可以進(jìn)一步編輯來定位需要的元素穷绵,然后把 CSS 選擇器加在你的代碼里轿塔。
我們點(diǎn)擊一條帶鏈接的文章標(biāo)題,然后將選擇器加在代碼里:
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('a.common-blog-title').items():
self.crawl(each.attr.href, callback=self.detail_page)
在提取到文章鏈接的同時(shí)仲墨,我們也要考慮翻頁操作勾缭。這里可以用同樣的思路得到指向下一頁的鏈接。值得注意的是目养,最后一頁的頁面上沒有對(duì)應(yīng)的翻頁控件俩由,因此需要做一個(gè)額外的判斷,在有翻頁按鈕的情況下才向后翻頁癌蚁。此外幻梯,我們解析文章列表頁的函數(shù)是 index_page
本身兜畸,所以翻頁的回調(diào)函數(shù)應(yīng)設(shè)為 self.index_page
,修改后的代碼為:
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('a.common-blog-title').items():
self.crawl(each.attr.href, callback=self.detail_page)
next = response.doc('.ucd-pager-next > a')
if next:
self.crawl(next.attr.href, callback=self.index_page)
再次點(diǎn)擊 run礼旅,然后進(jìn)入 follow 頁膳叨,選擇一個(gè)文章鏈接,點(diǎn)擊三角進(jìn)入詳情痘系。
我們可以加一些鍵值來提取和保存更多的信息:
@config(priority=2)
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('.cloud-blog-detail-title').text(),
"author": response.doc('.sub-content-username').text(),
"release_time": response.doc('.article-write-time').text()[4:],
"view_time": response.doc('.common-blog-eye').next().text(),
"comment_time": response.doc('.common-blog-bubbling').next().text(),
"tags": [i.text() for i in response.doc('.blog-menu-footer > a').items()],
"summary": response.doc('.cloud-blog-detail-summary-tag').text(),
"content": response.doc('#blogContent').html()
}
在原先只保存 url
和 title
的基礎(chǔ)上菲嘴,我們把文章相關(guān)的作者、發(fā)布時(shí)間汰翠、瀏覽次數(shù)龄坪、評(píng)論數(shù)、標(biāo)簽复唤、摘要和正文都從頁面上提取出來健田,具體的獲取細(xì)節(jié)和上面類似,這里就不展開了佛纫。點(diǎn)擊 run 可以看到提取的效果妓局。
從預(yù)覽來看,結(jié)果還是很符合我們的預(yù)期的呈宇。完成代碼編輯和測(cè)試后好爬,別忘了點(diǎn)右上角的 save,然后回到 Dashboard甥啄,將項(xiàng)目對(duì)的 status 改成 DEBUG
或者 RUNNING
存炮,然后點(diǎn)擊 Run,爬蟲就開始每天按計(jì)劃進(jìn)行爬取并更新數(shù)據(jù)啦蜈漓。
任務(wù)完成后點(diǎn)擊 Results 可以看到爬蟲保存的數(shù)據(jù)穆桂,可以導(dǎo)出并做后續(xù)的加工處理。由于我們使用了 MariaDB 保存結(jié)果融虽,實(shí)際上后續(xù)的數(shù)據(jù)處理過程也可以直接連接數(shù)據(jù)庫獲取數(shù)據(jù)享完。
總結(jié)
至此,我們的 PySpider 爬蟲實(shí)戰(zhàn)入門項(xiàng)目就完成了有额。華為開發(fā)者社區(qū)是一個(gè)開放的平臺(tái)驼侠,因此數(shù)據(jù)獲取也并沒有受到太多阻礙,然而在實(shí)際的運(yùn)用中谆吴,會(huì)遇到很多具有反爬蟲策略的網(wǎng)站。道高一尺苛预,魔高一丈句狼,我們用上 ip 代理、模擬登陸热某、驗(yàn)證碼識(shí)別腻菇、無頭瀏覽器等等技術(shù)手段和網(wǎng)站維護(hù)人員斗智斗勇胳螟,還是有辦法突破他們?cè)O(shè)置的封鎖的。
這篇博文只是一個(gè)系列的開篇筹吐,后續(xù)我會(huì)為大家總結(jié)應(yīng)對(duì)反爬蟲策略的一些方法糖耸,以及數(shù)據(jù)分析和處理的技巧,歡迎大家一起交流丘薛。