爬蟲也可以稱為Python爬蟲
不知從何時起氏豌,Python這門語言和爬蟲就像一對戀人喉酌,二者如膠似漆 ,形影不離泵喘,你中有我泪电、我中有你,一提起爬蟲纪铺,就會想到Python相速,一說起Python,就會想到人工智能……和爬蟲
所以鲜锚,一般說爬蟲的時候突诬,大部分程序員潛意識里都會聯(lián)想為Python爬蟲,為什么會這樣芜繁,我覺得有兩個原因:
- Python生態(tài)極其豐富旺隙,諸如Request、Beautiful Soup骏令、Scrapy催束、PySpider等第三方庫實在強大
- Python語法簡潔易上手,分分鐘就能寫出一個爬蟲(有人吐槽Python慢伏社,但是爬蟲的瓶頸和語言關系不大)
任何一個學習Python的程序員抠刺,應該都或多或少地見過甚至研究過爬蟲,我當時寫Python的目的就非常純粹——為了寫爬蟲摘昌。所以本文的目的很簡單速妖,就是說說我個人對Python爬蟲的理解與實踐,作為一名程序員聪黎,我覺得了解一下爬蟲的相關知識對你只有好處罕容,所以讀完這篇文章后,如果能對你有幫助稿饰,那便再好不過
什么是爬蟲
爬蟲是一個程序锦秒,這個程序的目的就是為了抓取萬維網(wǎng)信息資源,比如你日常使用的谷歌等搜索引擎喉镰,搜索結果就全都依賴爬蟲來定時獲取
看上述搜索結果旅择,除了wiki相關介紹外,爬蟲有關的搜索結果全都帶上了Python侣姆,前人說Python爬蟲生真,現(xiàn)在看來果然誠不欺我~
爬蟲的目標對象也很豐富沉噩,不論是文字、圖片柱蟀、視頻川蒙,任何結構化非結構化的數(shù)據(jù)爬蟲都可以爬取,爬蟲經(jīng)過發(fā)展长已,也衍生出了各種爬蟲類型:
- 通用網(wǎng)絡爬蟲:爬取對象從一些種子 URL 擴充到整個 Web畜眨,搜索引擎干的就是這些事
- 垂直網(wǎng)絡爬蟲:針對特定領域主題進行爬取,比如專門爬取小說目錄以及章節(jié)的垂直爬蟲
- 增量網(wǎng)絡爬蟲:對已經(jīng)抓取的網(wǎng)頁進行實時更新
- 深層網(wǎng)絡爬蟲:爬取一些需要用戶提交關鍵詞才能獲得的 Web 頁面
不想說這些大方向的概念术瓮,讓我們以一個獲取網(wǎng)頁內(nèi)容為例胶果,從爬蟲技術本身出發(fā),來說說網(wǎng)頁爬蟲斤斧,步驟如下:
- 模擬請求網(wǎng)頁資源
- 從HTML提取目標元素
- 數(shù)據(jù)持久化
什么是爬蟲,這就是爬蟲:
"""讓我們根據(jù)上面說的步驟來完成一個簡單的爬蟲程序"""
import requests
from bs4 import BeautifulSoup
target_url = 'http://www.baidu.com/s?wd=爬蟲'
# 第一步 發(fā)起一個GET請求
res = requests.get(target_url)
# 第二步 提取HTML并解析想獲取的數(shù)據(jù) 比如獲取 title
soup = BeautifulSoup(res.text, "lxml")
# 輸出 soup.title.text
title = soup.title.text
# 第三步 持久化 比如保存到本地
with open('title.txt', 'w') as fp:
fp.write(title)
加上注釋不到20行代碼霎烙,你就完成了一個爬蟲撬讽,簡單吧
怎么寫爬蟲
網(wǎng)頁世界多姿多彩、億萬網(wǎng)頁資源供你選擇悬垃,面對不同的頁面游昼,怎么使自己編寫的爬蟲程序夠穩(wěn)健、持久尝蠕,這是一個值得討論的問題
俗話說烘豌,磨刀不誤砍柴工,在開始編寫爬蟲之前看彼,很有必要掌握一些基本知識:
- 網(wǎng)頁的結構是HTML廊佩,爬蟲的目標就是解析HTML,獲取目標字段并保存
- 客戶端展現(xiàn)的網(wǎng)頁由瀏覽器渲染靖榕,客戶端和服務端的信息交互依靠HTTP協(xié)議
這兩句描述體現(xiàn)了一名爬蟲開發(fā)人員需要掌握的基本知識标锄,不過一名基本的后端或者前端工程師都會這些哈哈,這也說明了爬蟲的入門難度極低茁计,從這兩句話料皇,你能思考出哪些爬蟲必備的知識點呢?
- 基本的HTML知識星压,了解HTML才方便目標信息提取
- 基本的JS知識 践剂,JS可以異步加載HTML
- 了解CSS Selector、XPath以及正則娜膘,目的是為了提取數(shù)據(jù)
- 了解HTTP協(xié)議逊脯,為后面的反爬蟲斗爭打下基礎
- 了解基本的數(shù)據(jù)庫操作,為了數(shù)據(jù)持久化
有了這些知識儲備竣贪,接下來就可以選擇一門語言男窟,開始編寫自己的爬蟲程序了盆赤,還是按照上一節(jié)說的三個步驟,然后以Python為例歉眷,說一說要在編程語言方面做那些準備:
- 網(wǎng)頁請求:內(nèi)置有urllib庫牺六,第三方庫的話,同步請求可以使用requests汗捡,異步請求使用aiohttp
- 分析HTML結構并提取目標元素:CSS Selector和XPath是目前主流的提取方式淑际,第三方庫可以使用Beautiful Soup或者PyQuery
- 數(shù)據(jù)持久化:目標數(shù)據(jù)提取之后,可以將數(shù)據(jù)保存到數(shù)據(jù)庫中進行持久化扇住,MySQL春缕、MongoDB等,這些都有對應的庫支持艘蹋,當然你也可以保存在硬盤锄贼,誰硬盤沒點東西對吧(滑稽臉)
掌握了上面這些,你大可放開手腳大干一場女阀,萬維網(wǎng)就是你的名利場宅荤,去吧~
我覺得對于一個目標網(wǎng)站的網(wǎng)頁,可以分下面四個類型:
- 單頁面單目標
- 單頁面多目標
- 多頁面單目標
- 多頁面多目標
具體是什么意思呢浸策,可能看起來有點繞冯键,但明白這些,你之后寫爬蟲庸汗,只要在腦子里面過一遍著網(wǎng)頁對應什么類型惫确,然后套上對應類型的程序(寫多了都應該有一套自己的常用代碼庫),那寫爬蟲的速度蚯舱,自然不會慢
單頁面單目標
通俗來說改化,就是在這個網(wǎng)頁里面,我們的目標就只有一個枉昏,假設我們的需求是抓取這部 電影-肖申克的救贖 的名稱所袁,首先打開網(wǎng)頁右鍵審查元素,找到電影名稱對應的元素位置凶掰,如下圖所示:
在某個單一頁面內(nèi)燥爷,看目標是不是只有一個,一眼就能看出標題的CSS Selector規(guī)則為:#content > h1 > span:nth-child(1)
懦窘,然后用我自己寫的常用庫前翎,我用不到十行代碼就能寫完抓取這個頁面電影名稱的爬蟲:
import asyncio
from ruia import Item, TextField
class DoubanItem(Item):
title = TextField(css_select='#content > h1 > span:nth-child(1)')
async_func = DoubanItem.get_item(url="https://movie.douban.com/subject/1292052/")
item = asyncio.get_event_loop().run_until_complete(async_func)
print(item.title)
多頁面多目標就是此情況下多個url的衍生情況
單頁面多目標
假設現(xiàn)在的需求是抓取 豆瓣電影250 第一頁中的所有電影名稱,你需要提取25個電影名稱畅涂,因為這個目標頁的目標數(shù)據(jù)是多個item的港华,因此目標需要循環(huán)獲取,這就是所謂的單頁面多目標了:
import asyncio
from ruia import Item, TextField
class DoubanItem(Item):
target_item = TextField(css_select='div.item')
title = TextField(css_select='span.title')
async def clean_title(self, title):
if isinstance(title, str):
return title
else:
return ''.join([i.text.strip().replace('\xa0', '') for i in title])
async_func = DoubanItem.get_items(url="https://movie.douban.com/top250")
items = asyncio.get_event_loop().run_until_complete(async_func)
for item in items:
print(item)
多頁面多目標
多頁面多目標是上述單頁面多目標情況的衍生午衰,在這個問題上來看立宜,此時就是獲取所有分頁的電影名稱
from ruia import TextField, Item, Request, Spider
class DoubanItem(Item):
"""
定義爬蟲的目標字段
"""
target_item = TextField(css_select='div.item')
title = TextField(css_select='span.title')
async def clean_title(self, title):
if isinstance(title, str):
return title
else:
return ''.join([i.text.strip().replace('\xa0', '') for i in title])
class DoubanSpider(Spider):
start_urls = ['https://movie.douban.com/top250']
concurrency = 10
async def parse(self, res):
etree = res.html_etree
pages = ['?start=0&filter='] + [i.get('href') for i in etree.cssselect('.paginator>a')]
for index, page in enumerate(pages):
url = self.start_urls[0] + page
yield Request(
url,
callback=self.parse_item,
metadata={'index': index},
request_config=self.request_config
)
async def parse_item(self, res):
items_data = await DoubanItem.get_items(html=res.html)
res_list = []
for item in items_data:
res_list.append(item.title)
return res_list
if __name__ == '__main__':
DoubanSpider.start()
如果網(wǎng)絡沒問題的話冒萄,會得到如下輸出:
注意爬蟲運行時間,1s不到橙数,這就是異步的魅力
用Python寫爬蟲尊流,就是這么簡單優(yōu)雅,諸位灯帮,看著網(wǎng)頁就思考下:
- 是什么類型的目標類型
- 用什么庫模擬請求
- 怎么解析目標字段
- 怎么存儲
一個爬蟲程序就成型了崖技,順便一提,爬蟲這東西钟哥,可以說是防君子不防小人迎献,robots.txt
大部分網(wǎng)站都有(它的目的是告訴爬蟲什么可以爬取什么不可以爬取,比如:https://www.baidu.com/robots.txt
)腻贰,各位想怎么爬取吁恍,自己衡量
如何進階
不要以為寫好一個爬蟲程序就可以出師了,此時還有更多的問題在前面等著你播演,你要含情脈脈地看著你的爬蟲程序冀瓦,問自己三個問題:
- 爬蟲抓取數(shù)據(jù)后是正當用途么?
- 爬蟲會把目標網(wǎng)站干掉么宾巍?
- 爬蟲會被反爬蟲干掉么?
前兩個關于人性的問題在此不做過多敘述渔伯,因此跳過顶霞,但你們?nèi)绻鳛榕老x工程師的話,切不可跳過
會被反爬蟲干掉么锣吼?
最后關于反爬蟲的問題才是你爬蟲程序強壯與否的關鍵因素选浑,什么是反爬蟲?
當越來越多的爬蟲在互聯(lián)網(wǎng)上橫沖直撞后玄叠,網(wǎng)頁資源維護者為了防止自身數(shù)據(jù)被抓取古徒,開始進行一系列的措施來使得自身數(shù)據(jù)不易被別的程序爬取,這些措施就是反爬蟲
比如檢測IP訪問頻率读恃、資源訪問速度隧膘、鏈接是否帶有關鍵參數(shù)、驗證碼檢測機器人寺惫、ajax混淆疹吃、js加密等等
對于目前市場上的反爬蟲,爬蟲工程師常有的反反爬蟲方案是下面這樣的:
- 不斷試探目標底線西雀,試出單IP下最優(yōu)的訪問頻率
- 構建自己的IP代理池
- 維護一份自己常用的UA庫
- 針對目標網(wǎng)頁的Cookie池
- 需要JS渲染的網(wǎng)頁使用無頭瀏覽器進行代碼渲染再抓取
- 一套破解驗證碼程序
- 扎實的JS知識來破解混淆函數(shù)
爬蟲工程師的進階之路其實就是不斷反反爬蟲萨驶,可謂艱辛,但換個角度想也是樂趣所在
關于框架
爬蟲有自己的編寫流程和標準艇肴,有了標準腔呜,自然就有了框架叁温,像Python這種生態(tài)強大的語言,框架自然是多不勝數(shù)核畴,目前世面上用的比較多的有:
- Scrapy
- PySpider
- Portia
這里不過多介紹膝但,框架只是工具,是一種提升效率的方式膛檀,看你選擇
說明
任何事物都有兩面性锰镀,爬蟲自然也不例外,因此我送諸位一張圖咖刃,關鍵時刻好好想想