當前位置:
首頁 > 知識 > Python3 開發輕量級爬蟲教程

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 獲取到的內容進行分析



  • 輸出器:將分析完成後的數據進行輸出、存儲、利用等



爬蟲邏輯可分解為如下幾個部分:




  1. 查詢管理器中是否有待爬取的 URL



  2. 調度器從管理器中獲取一個待爬取 URL



  3. 將獲取到的 URL 交給下載器處理



  4. 將下載器獲取到的網頁數據交給解析器處理



  5. 將解析器處理後的 URL 集合交給管理器



  6. 將解析器處理後的數據交給輸出器



  7. 重複上述步驟


三、開發輕量級爬蟲


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)

運行結果:


【點擊成為源碼大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

女生哪裡最緊?
孫悟空為什麼那麼瘦?

TAG:Python開發 |