爬蟲也可以稱為Python爬蟲
不知從何時起,Python這門語言和爬蟲就像一對戀人,二者如膠似漆 ,形影不離,你中有我、我中有你,一提起爬蟲,就會想到Python,一說起Python,就會想到人工智能……和爬蟲
所以,一般說爬蟲的時候,大部分程序員潛意識里都會聯想為Python爬蟲,為什么會這樣,我覺得有兩個原因:
- Python生態極其豐富,諸如Request、Beautiful Soup、Scrapy、PySpider等第三方庫實在強大
- Python語法簡潔易上手,分分鐘就能寫出一個爬蟲(有人吐槽Python慢,但是爬蟲的瓶頸和語言關系不大)
任何一個學習Python的程序員,應該都或多或少地見過甚至研究過爬蟲,我當時寫Python的目的就非常純粹——為了寫爬蟲。所以本文的目的很簡單,就是說說我個人對Python爬蟲的理解與實踐,作為一名程序員,我覺得了解一下爬蟲的相關知識對你只有好處,所以讀完這篇文章后,如果能對你有幫助,那便再好不過
什么是爬蟲
爬蟲是一個程序,這個程序的目的就是為了抓取萬維網信息資源,比如你日常使用的谷歌等搜索引擎,搜索結果就全都依賴爬蟲來定時獲取
看上述搜索結果,除了wiki相關介紹外,爬蟲有關的搜索結果全都帶上了Python,前人說Python爬蟲,現在看來果然誠不欺我~
爬蟲的目標對象也很豐富,不論是文字、圖片、視頻,任何結構化非結構化的數據爬蟲都可以爬取,爬蟲經過發展,也衍生出了各種爬蟲類型:
- 通用網絡爬蟲:爬取對象從一些種子 URL 擴充到整個 Web,搜索引擎干的就是這些事
- 垂直網絡爬蟲:針對特定領域主題進行爬取,比如專門爬取小說目錄以及章節的垂直爬蟲
- 增量網絡爬蟲:對已經抓取的網頁進行實時更新
- 深層網絡爬蟲:爬取一些需要用戶提交關鍵詞才能獲得的 Web 頁面
不想說這些大方向的概念,讓我們以一個獲取網頁內容為例,從爬蟲技術本身出發,來說說網頁爬蟲,步驟如下:
- 模擬請求網頁資源
- 從HTML提取目標元素
- 數據持久化
什么是爬蟲,這就是爬蟲:
"""讓我們根據上面說的步驟來完成一個簡單的爬蟲程序"""
import requests
from bs4 import BeautifulSoup
target_url = 'http://www.baidu.com/s?wd=爬蟲'
# 第一步 發起一個GET請求
res = requests.get(target_url)
# 第二步 提取HTML并解析想獲取的數據 比如獲取 title
soup = BeautifulSoup(res.text, "lxml")
# 輸出 soup.title.text
title = soup.title.text
# 第三步 持久化 比如保存到本地
with open('title.txt', 'w') as fp:
fp.write(title)
加上注釋不到20行代碼,你就完成了一個爬蟲,簡單吧
怎么寫爬蟲
網頁世界多姿多彩、億萬網頁資源供你選擇,面對不同的頁面,怎么使自己編寫的爬蟲程序夠穩健、持久,這是一個值得討論的問題
俗話說,磨刀不誤砍柴工,在開始編寫爬蟲之前,很有必要掌握一些基本知識:
- 網頁的結構是HTML,爬蟲的目標就是解析HTML,獲取目標字段并保存
- 客戶端展現的網頁由瀏覽器渲染,客戶端和服務端的信息交互依靠HTTP協議
這兩句描述體現了一名爬蟲開發人員需要掌握的基本知識,不過一名基本的后端或者前端工程師都會這些哈哈,這也說明了爬蟲的入門難度極低,從這兩句話,你能思考出哪些爬蟲必備的知識點呢?
- 基本的HTML知識,了解HTML才方便目標信息提取
- 基本的JS知識 ,JS可以異步加載HTML
- 了解CSS Selector、XPath以及正則,目的是為了提取數據
- 了解HTTP協議,為后面的反爬蟲斗爭打下基礎
- 了解基本的數據庫操作,為了數據持久化
有了這些知識儲備,接下來就可以選擇一門語言,開始編寫自己的爬蟲程序了,還是按照上一節說的三個步驟,然后以Python為例,說一說要在編程語言方面做那些準備:
- 網頁請求:內置有urllib庫,第三方庫的話,同步請求可以使用requests,異步請求使用aiohttp
- 分析HTML結構并提取目標元素:CSS Selector和XPath是目前主流的提取方式,第三方庫可以使用Beautiful Soup或者PyQuery
- 數據持久化:目標數據提取之后,可以將數據保存到數據庫中進行持久化,MySQL、MongoDB等,這些都有對應的庫支持,當然你也可以保存在硬盤,誰硬盤沒點東西對吧(滑稽臉)
掌握了上面這些,你大可放開手腳大干一場,萬維網就是你的名利場,去吧~
我覺得對于一個目標網站的網頁,可以分下面四個類型:
- 單頁面單目標
- 單頁面多目標
- 多頁面單目標
- 多頁面多目標
具體是什么意思呢,可能看起來有點繞,但明白這些,你之后寫爬蟲,只要在腦子里面過一遍著網頁對應什么類型,然后套上對應類型的程序(寫多了都應該有一套自己的常用代碼庫),那寫爬蟲的速度,自然不會慢
單頁面單目標
通俗來說,就是在這個網頁里面,我們的目標就只有一個,假設我們的需求是抓取這部 電影-肖申克的救贖 的名稱,首先打開網頁右鍵審查元素,找到電影名稱對應的元素位置,如下圖所示:
在某個單一頁面內,看目標是不是只有一個,一眼就能看出標題的CSS Selector規則為:#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的衍生情況
單頁面多目標
假設現在的需求是抓取 豆瓣電影250 第一頁中的所有電影名稱,你需要提取25個電影名稱,因為這個目標頁的目標數據是多個item的,因此目標需要循環獲取,這就是所謂的單頁面多目標了:
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()
如果網絡沒問題的話,會得到如下輸出:
注意爬蟲運行時間,1s不到,這就是異步的魅力
用Python寫爬蟲,就是這么簡單優雅,諸位,看著網頁就思考下:
- 是什么類型的目標類型
- 用什么庫模擬請求
- 怎么解析目標字段
- 怎么存儲
一個爬蟲程序就成型了,順便一提,爬蟲這東西,可以說是防君子不防小人,robots.txt
大部分網站都有(它的目的是告訴爬蟲什么可以爬取什么不可以爬取,比如:https://www.baidu.com/robots.txt
),各位想怎么爬取,自己衡量
如何進階
不要以為寫好一個爬蟲程序就可以出師了,此時還有更多的問題在前面等著你,你要含情脈脈地看著你的爬蟲程序,問自己三個問題:
- 爬蟲抓取數據后是正當用途么?
- 爬蟲會把目標網站干掉么?
- 爬蟲會被反爬蟲干掉么?
前兩個關于人性的問題在此不做過多敘述,因此跳過,但你們如果作為爬蟲工程師的話,切不可跳過
會被反爬蟲干掉么?
最后關于反爬蟲的問題才是你爬蟲程序強壯與否的關鍵因素,什么是反爬蟲?
當越來越多的爬蟲在互聯網上橫沖直撞后,網頁資源維護者為了防止自身數據被抓取,開始進行一系列的措施來使得自身數據不易被別的程序爬取,這些措施就是反爬蟲
比如檢測IP訪問頻率、資源訪問速度、鏈接是否帶有關鍵參數、驗證碼檢測機器人、ajax混淆、js加密等等
對于目前市場上的反爬蟲,爬蟲工程師常有的反反爬蟲方案是下面這樣的:
- 不斷試探目標底線,試出單IP下最優的訪問頻率
- 構建自己的IP代理池
- 維護一份自己常用的UA庫
- 針對目標網頁的Cookie池
- 需要JS渲染的網頁使用無頭瀏覽器進行代碼渲染再抓取
- 一套破解驗證碼程序
- 扎實的JS知識來破解混淆函數
爬蟲工程師的進階之路其實就是不斷反反爬蟲,可謂艱辛,但換個角度想也是樂趣所在
關于框架
爬蟲有自己的編寫流程和標準,有了標準,自然就有了框架,像Python這種生態強大的語言,框架自然是多不勝數,目前世面上用的比較多的有:
- Scrapy
- PySpider
- Portia
這里不過多介紹,框架只是工具,是一種提升效率的方式,看你選擇
說明
任何事物都有兩面性,爬蟲自然也不例外,因此我送諸位一張圖,關鍵時刻好好想想