Python3 開發輕量級爬蟲教程
程序員大咖
點擊右側關注,免費進階高級!
來源: python教程
https://mp.weixin.qq.com/s/vCMNT5TG0Yvs69yiFPUDug
爬蟲又被稱為網頁蜘蛛,網路機器人。是一種按照一定的規則,自動地抓取互聯網上信息的程序或者腳本。
一、爬蟲演算法
在寫爬蟲時候有兩種常用的演算法可使用,即深度優先演算法、廣度優先演算法。
深度優先演算法
對每一個可能的分支路徑深入到不能再深入為止,而且每個結點只能訪問一次。直到訪問完成後再返回到最上層,然後重複上述步驟。
廣度優先演算法
從上往下對每一層依次訪問,在每一層中,從左往右(也可以從右往左)訪問結點,訪問完一層就進入下一層,直到沒有結點可以訪問為止。
負載均衡
當爬取量很大的話,需要負載到多台伺服器同時運行(搜索引擎都是這麼做的)。但這樣會出現一個問題,當 A 伺服器已經爬取完成的 URL,但 B 伺服器並不知道 A 是否爬取完成,這樣會造成資源的浪費,那怎麼辦呢?如何突破爬蟲的瓶頸?
其中最簡單的便是 URL 分類。舉個栗子:現在有 A、B、C、D、X 五台伺服器同時運行爬蟲,X 為負載均衡伺服器。所有的 URL 都要經過 X 伺服器進行分配, X 伺服器遇到域名是 .com
結尾的就分配給 A,遇到 .cn
結尾就分配給 B,遇到 .net
結尾就分配給 C,其他域名都分配給 D。這樣就解決了爬蟲瓶頸的問題,這個問題可是谷歌的面試題。
二、爬蟲邏輯
爬蟲可大致分為五個部分:
調度器:引擎,是爬蟲邏輯實現的模塊
管理器:URL 管理器,負責新增、刪除、獲取、存儲、計數等功能,避免爬取重複的 URL。
下載器:HTML 下載器,將 URL 地址中的 HTML 內容獲取到
解析器:HTML 解析器,將 HTML 獲取到的內容進行分析
輸出器:將分析完成後的數據進行輸出、存儲、利用等
爬蟲邏輯可分解為如下幾個部分:
查詢管理器中是否有待爬取的 URL
調度器從管理器中獲取一個待爬取 URL
將獲取到的 URL 交給下載器處理
將下載器獲取到的網頁數據交給解析器處理
將解析器處理後的 URL 集合交給管理器
將解析器處理後的數據交給輸出器
重複上述步驟
三、開發輕量級爬蟲
1. 調度器
spider.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【調度器】
#
# 調度器又稱為引擎,是爬蟲邏輯實現的模塊。
#
# 爬蟲邏輯可分解為如下幾個部分:
#
# 1. 查詢管理器中是否有待爬取的 URL
# 2. 調度器從管理器中獲取一個待爬取 URL
# 3. 將獲取到的 URL 交給下載器處理
# 4. 將下載器獲取到的網頁數據交給解析器處理
# 5. 將解析器處理後的 URL 集合交給管理器
# 6. 將解析器處理後的數據交給輸出器
# 7. 重複上述步驟
#
import manager, download, parser, output
class Spider(object):
def __init__(self):
# 實例化 管理器、下載器、解析器、輸出器
self.manager = manager.Manager()
self.download = download.Download()
self.parser = parser.Parser()
self.output = output.Output()
def spider(self, url):
# 爬蟲計數器
count = 0
# 將第一個 URL 放入管理器中
self.manager.add_url(//img.ifuun.com/getimg.php?url=url)
# 判斷管理器中 URL 的數量是否為 0
while self.manager.num_url(//img.ifuun.com/getimg.php?url=):
try:
# 從管理器中獲取一個 URL
new_url = self.manager.get_url(//img.ifuun.com/getimg.php?url=)
# 將獲取到的 URL 交給下載器處理
html_cont = self.download.download(new_url)
# 將 URL 和 下載器處理後的網頁數據交給解析器處理
new_manager, new_data = self.parser.parser(new_url, html_cont)
# 將解析器處理後的數據和新的 URL 分別交給管理器和輸出器
self.manager.add_urls(new_manager)
self.output.add_data(new_data)
# 最多爬取 100 次
if count == 100:
break
count += 1
print("爬蟲運行狀態 ==> %d : %s" % (count, new_url))
except:
print("爬蟲爬取失敗")
# 當爬蟲爬取完所有數據後,輸出器將數據輸出到文件
self.output.output()
if __name__ == "__main__":
# URL 入口(爬蟲爬取的第一個 URL)
url = "http://www.cnblogs.com/peida/archive/2012/12/05/2803591.html"
# 實例化調度器並啟動爬蟲
run = Spider()
run.spider(url)
2. 管理器
manager.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【管理器】
#
# 對外提供四個方法:
#
# 1. add_url(//img.ifuun.com/getimg.php?url=url)
# 添加一個 URL,接收一個參數 URL
#
# 2. add_urls(url)
# 批量添加 URL,接收一個參數 URL
#
# 3. num_url(//img.ifuun.com/getimg.php?url=)
# 管理器中URL 的數量
#
# 4. get_url(//img.ifuun.com/getimg.php?url=)
# 從管理器中獲取一個 URL
#
class Manager(object):
def __init__(self):
# 待爬取的 URL 集合
self.new_urls = set()
# 已爬取的 URL 集合
self.old_urls = set()
# 添加一個 URL
def add_url(//img.ifuun.com/getimg.php?url=self, url):
# 判斷 url 是否為空,為空則返回 None
if url is None:
return
# 判斷 url 是否存在於【待爬取的 URL 集合】和【已爬取的 URL 集合】,如若都不在將新 url 添加到【待爬取的 URL 集合】中
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
# 批量添加 URL
def add_urls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
# 將 URL 集合交給 add_url(//img.ifuun.com/getimg.php?url=) 函數處理
self.add_url(//img.ifuun.com/getimg.php?url=url)
# 返回管理器中 URL 的數量
def num_url(//img.ifuun.com/getimg.php?url=self):
return len(self.new_urls) != 0
# 從管理器中獲取一個 URL
def get_url(//img.ifuun.com/getimg.php?url=self):
url = self.new_urls.pop()
self.old_urls.add(url)
return url
3. 下載器
download.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【下載器】
#
# requests 模塊需要安裝
# sudo pip3 install requests
#
# 對外提供一個方法:
#
# download(url)
# 下載網頁中的內容,接收一個參數 URL
#
import requests
class Download(object):
def download(self, url):
# 判斷 URL 是否為空,為空直接返回 None
if url is None:
return None
# 請求 URL
response = requests.get(url = url)
# 判斷 URL 地址是否訪問成功,若失敗直接返回 None
if response.status_code != 200:
return None
return response.text
4. 解析器
parser.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【解析器】
#
# bs4 模塊需要安裝
# sudo pip3 install bs4
#
# 對外提供一個方法:
#
# 1. parser(url)
# BeautifulSoup DOM 樹處理,接收兩個參數 URL 和 html 內容
#
# 對內提供兩個方法:
#
# 1. _get_urls(url, soup)
# 獲取網頁中的 URL,接收兩個參數 URL 和 DOM 樹
#
# 2. _get_data(url, soup)
# 獲取網頁中的數據,接收兩個參數 URL 和 DOM 樹
#
import re
from bs4 import BeautifulSoup
class Parser(object):
def _get_urls(self, url, soup):
urls = set()
links = soup.find_all("a", href=re.compile(r"/peida/archive/*"))
for link in links:
new_url = link["href"]
urls.add(new_url)
return urls
def _get_data(self, url, soup):
res_data = {}
res_data["url"] = url
title_node = soup.find("div", class_="post").find("a")
res_data["title"] = title_node.get_text()
return res_data
def parser(self, url, html):
# 判斷 url 是否為空,或網頁數據是否為空,為空則返回 None
if url is None or html is None:
return
# 格式化 html 為 BeautifulSoup DOM 樹
soup = BeautifulSoup(html, "html.parser")
# 獲取網頁中的 URL
urls = self._get_urls(url, soup)
# 獲取網頁中的數據
data = self._get_data(url, soup)
return urls, data
5. 輸出器
output.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【輸出器】
#
# 對外提供兩個方法:
#
# 1. add_data(data)
# 存儲網頁數據,接收一個參數 data
#
# 2. output()
# 將數據輸出到 html 文件中
#
class Output(object):
def __init__(self):
self.datas = []
def add_data(self, data):
if data is None:
return
self.datas.append(data)
def output(self):
head = """
<html>
<meta charset="UTF-8">
<body>
<table>
"""
foot = """
</table>
</body>
</html>
"""
with open("CSDN.html", "w") as f:
f.write(head)
for data in self.datas:
f.write("<tr><td><a href =%s>%s</a></td>" % (data["url"], data["url"]))
f.write("<td>%s</td></tr>" % data["title"])
f.write(foot)
運行結果:
【點擊成為源碼大神】


TAG:Python開發 |