代碼結構分析
這是一個同步新聞爬蟲程序,主要包含以下幾個部分:
們把爬蟲設計為一個類,類在初始化時,連接數據庫,初始化logger,創建網址池,加載hubs并設置到網址池。
爬蟲開始運行的入口就是run(),它是一個while循環,設計為永不停息的爬。先從網址池獲取一定數量的url,然后對每個url進行處理,
處理url也就是實施抓取任務的是process(),它先通過downloader下載網頁,然后在網址池中設置該url的狀態。接著,從下載得到的html提取網址,并對得到的網址進行過濾(filter_good()),過濾的原則是,它們的host必須是hubs的host。最后把下載得到的html存儲到數據。
運行這個新聞爬蟲很簡單,生成一個NewsCrawlerSync的對象,然后調用run()即可。當然,在運行之前,要先在config.py里面配置MySQL的用戶名和密碼,也要在crawler_hub表里面添加幾個hub網址才行。
##思考題: 如何收集大量hub列表
比如,我想要抓新浪新聞?news.sina.com.cn?, 其首頁是一個hub頁面,但是,如何通過它獲得新浪新聞更多的hub頁面呢?小猿們不妨思考一下這個問題,并用代碼來實現一下。
這個時候已經抓取到很多網頁了,但是怎么抽取網頁里的文字呢?
1. 異步的downloader
還記得我們之前使用requests實現的那個downloader嗎?同步情況下,它很好用,但不適合異步,所以我們要先改造它。幸運的是,已經有aiohttp模塊來支持異步http請求了,那么我們就用aiohttp來實現異步downloader。
async def fetch(session, url, headers=None, timeout=9):_headers = {'User-Agent': ('Mozilla/5.0 (compatible; MSIE 9.0; ''Windows NT 6.1; Win64; x64; Trident/5.0)'),}if headers:_headers = headerstry:async with session.get(url, headers=_headers, timeout=timeout) as response:status = response.statushtml = await response.read()encoding = response.get_encoding()if encoding == 'gb2312':encoding = 'gbk'html = html.decode(encoding, errors='ignore')redirected_url = str(response.url)except Exception as e:msg = 'Failed download: {} | exception: {}, {}'.format(url, str(type(e)), str(e))print(msg)html = ''status = 0redirected_url = urlreturn status, html, redirected_url
這個異步的downloader,我們稱之為fetch(),它有兩個必須參數:
- seesion: 這是一個aiohttp.ClientSession的對象,這個對象的初始化在crawler里面完成,每次調用fetch()時,作為參數傳遞。
- url:這是需要下載的網址。
實現中使用了異步上下文管理器(async with),編碼的判斷我們還是用cchardet來實現。
有了異步下載器,我們的異步爬蟲就可以寫起來啦~
- 導入部分:引入必要的庫和模塊
- 主類定義:
NewsCrawlerSync
?類,包含爬蟲的主要功能 - 初始化方法:
__init__
?方法,初始化數據庫連接、日志和 URL 池 - 輔助方法:如加載樞紐 URL、保存數據到數據庫、過濾 URL 等
- 核心處理方法:
process
?方法處理 URL 下載和內容提取 - 運行方法:
run
?方法實現爬蟲的主循環
涉及的知識點
- 網絡爬蟲基礎:URL 處理、HTML 下載和解析
- 數據庫操作:使用 MySQL 存儲爬取的 URL 和 HTML 內容
- 數據壓縮:使用 LZMA 算法壓縮 HTML 內容
- 哈希算法:使用 farmhash 生成 URL 的哈希值
- 日志記錄:使用自定義日志函數記錄操作
- URL 管理:使用自定義的 UrlPool 類管理 URL 隊列
- 網頁解析:使用正則表達式提取鏈接
執行流程
- 初始化爬蟲對象,建立數據庫連接,初始化日志和 URL 池
- 從數據庫加載樞紐 URL,并將它們添加到 URL 池
- 進入主循環,每次從 URL 池取出 5 個 URL
- 對每個 URL,下載其內容并處理
- 如果是樞紐頁面,提取其中的鏈接并過濾,將有效鏈接添加到 URL 池
- 如果是普通新聞頁面,將其內容保存到數據庫
- 重復步驟 3-6,直到程序停止
現在我將按照這個思路來詳細分析代碼的結構、知識點和流程。
新聞爬蟲代碼分析
一、整體結構
這是一個同步新聞爬蟲程序,采用面向對象設計,核心類為?NewsCrawlerSync
,主要包含以下模塊:
- 數據存儲模塊:通過?
ezpymysql.Connection
?連接 MySQL 數據庫,存儲 URL 和 HTML 內容 - URL 管理模塊:使用?
UrlPool
?類管理待爬取 URL 隊列 - 下載處理模塊:通過?
fn.downloader
?下載網頁內容 - 鏈接提取模塊:通過?
fn.extract_links_re
?從 HTML 中提取鏈接 - 日志記錄模塊:使用?
fn.init_file_logger
?記錄操作日志
二、核心知識點
1. 數據庫操作
- 數據去重:通過
farmhash.hash64(url)
生成 URL 哈希值,避免重復存儲相同內容 - SQL 執行:使用參數化查詢防止 SQL 注入(如
db.execute(sql, urlhash, url, html_lzma)
) - 異常處理:捕獲 1062 錯誤(唯一鍵沖突),處理數據重復情況
2. 網頁處理
- URL 解析:使用
urllib.parse.urlparse
解析 URL 的 host 部分 - 內容壓縮:通過
lzma.compress
壓縮 HTML 內容,減少存儲體積 - 鏈接提取:通過正則表達式從 HTML 中提取鏈接(
fn.extract_links_re
)
3. 爬蟲架構
- 樞紐頁面機制:從
crawler_hub
表加載樞紐 URL,作為爬蟲入口 - URL 過濾:只爬取樞紐頁面關聯的域名(
self.hub_hosts
集合) - 狀態管理:通過
UrlPool.set_status
記錄 URL 爬取狀態
三、執行流程詳解
1. 初始化階段
圖片
代碼
創建NewsCrawlerSync實例
初始化數據庫連接
初始化日志記錄器
初始化UrlPool
調用load_hubs加載樞紐URL
創建NewsCrawlerSync實例
初始化數據庫連接
初始化日志記錄器
初始化UrlPool
調用load_hubs加載樞紐URL
豆包
你的 AI 助手,助力每日工作學習
- 從
crawler_hub
表讀取樞紐 URL,提取域名存入hub_hosts
集合 - 將樞紐 URL 添加到 UrlPool,設置 300 秒的重復爬取間隔
2. 主爬取循環
圖片
代碼
是
否
開始run循環
從UrlPool取出5個URL
是否有URL?
遍歷每個URL
調用process處理URL
是
否
開始run循環
從UrlPool取出5個URL
是否有URL?
遍歷每個URL
調用process處理URL
豆包
你的 AI 助手,助力每日工作學習
3. 單 URL 處理流程
圖片
代碼
是
否
process方法
下載URL內容
記錄URL狀態
是否為樞紐頁面?
提取頁面所有鏈接
過濾出樞紐域名的鏈接
將有效鏈接添加到UrlPool
保存HTML到數據庫
是
否
process方法
下載URL內容
記錄URL狀態
是否為樞紐頁面?
提取頁面所有鏈接
過濾出樞紐域名的鏈接
將有效鏈接添加到UrlPool
保存HTML到數據庫
豆包
你的 AI 助手,助力每日工作學習
四、關鍵方法解析
1. save_to_db - 數據存儲核心
python
運行
def save_to_db(self, url, html):# 生成URL哈希值urlhash = farmhash.hash64(url)# 檢查是否已存在相同哈希的記錄d = self.db.get('select url from crawler_html where urlhash=%s', urlhash)if d:# 處理哈希沖突if d['url'] != url:self.logger.error('farmhash collision')return True# 壓縮HTML內容if isinstance(html, str):html = html.encode('utf8')html_lzma = lzma.compress(html)# 插入數據庫,處理唯一鍵沖突try:self.db.execute('insert into crawler_html(urlhash, url, html_lzma) values(%s, %s, %s)',urlhash, url, html_lzma)return Trueexcept Exception as e:if e.args[0] == 1062: # 重復記錄return Trueelse:traceback.print_exc()raise e
2. process - 爬蟲核心邏輯
python
運行
def process(self, url, ishub):# 下載網頁內容status, html, redirected_url = fn.downloader(url)# 更新URL狀態self.urlpool.set_status(url, status)if redirected_url != url:self.urlpool.set_status(redirected_url, status)# 處理非200狀態碼if status != 200:return# 處理樞紐頁面if ishub:newlinks = fn.extract_links_re(redirected_url, html) # 提取所有鏈接goodlinks = self.filter_good(newlinks) # 過濾樞紐域名鏈接self.urlpool.addmany(goodlinks) # 添加到URL池# 處理新聞頁面else:self.save_to_db(redirected_url, html) # 保存到數據庫
五、技術特點與注意事項
-
同步爬蟲特性:
- 單線程執行,通過循環依次處理 URL
- 適合小規模爬取,大規模爬取需改造為異步模式
-
去重機制:
- 基于 farmhash 哈希值去重,可能存在哈希沖突(代碼中已處理)
- 數據庫中通過
urlhash
建立唯一索引強化去重
-
可優化點:
- 改為異步爬蟲(使用
asyncio
)提升并發效率 - 添加 User-Agent 輪換和請求延遲,避免被封 IP
- 完善代理 IP 池機制,應對反爬措施
- 改為異步爬蟲(使用
通過這個爬蟲框架,可以實現對指定新聞網站的持續爬取,并將內容結構化存儲到數據庫中,適合作為入門級爬蟲系統的參考