簡單分布式爬蟲——第三彈:nodeSpider的實現(xiàn)

前兩講分別講了分布式爬蟲的結(jié)構(gòu)和masterSpider的實現(xiàn):
簡單分布式爬蟲——第一彈:了解分布式爬蟲結(jié)構(gòu)
簡單分布式爬蟲——第二彈:masterSpider的實現(xiàn)
接下來完成分布式爬蟲的最后一部分:節(jié)點爬蟲的實現(xiàn)。
節(jié)點爬蟲顧名思義就是負責爬取任務(wù),具體來講主要實現(xiàn):
1、從網(wǎng)絡(luò)上獲取masterSpider放在task_queue隊列中的任務(wù)(url)
2、爬取獲取的url的內(nèi)容,并解析出新的url和所要提取的數(shù)據(jù)內(nèi)容
3、將解析好的數(shù)據(jù)以一定格式(字典)返還給masterSpider(放入result_queue隊列)
接下來來完成各部分的功能實現(xiàn):
根據(jù)模塊化思想,我們將nodeSpider分為三部分:
MainSpider: 負責nodeSpider調(diào)度
HtmlDownloader: 下載網(wǎng)頁
HtmlParser: 解析網(wǎng)頁

## MainSpider.py
# -*- coding: utf-8 -*-
from multiprocessing.managers import BaseManager
from HtmlDownLoader import HtmlDownloader
from HtmlParser import HtmlParser

class SpiderWork(object):
    '''
    爬蟲節(jié)點調(diào)度器:
        初始化:
            從主節(jié)點注冊任務(wù)
            初始化下載器和解析器
        開始爬取:
    '''
    def __init__(self):
        #初始化分布式進程中的工作節(jié)點的連接工作
        # 實現(xiàn)第一步:使用BaseManager注冊獲取Queue的方法名稱
        BaseManager.register('get_task_queue')
        BaseManager.register('get_result_queue')
        # 實現(xiàn)第二步:連接到服務(wù)器:
        server_addr = '127.0.0.1' 
        print('Connect to server %s...' % server_addr)
        # 端口和驗證口令注意保持與服務(wù)進程設(shè)置的完全一致:
        self.m = BaseManager(address=(server_addr, 10001), authkey='python'.encode('utf-8'))
        try:
            self.m.connect()
            # 實現(xiàn)第三步:獲取Queue的對象:
            self.task = self.m.get_task_queue()
            self.result = self.m.get_result_queue()
            #初始化網(wǎng)頁下載器和解析器
            self.downloader = HtmlDownloader()
            self.parser = HtmlParser()
            print('*********Init finished!*********')
        except ConnectionRefusedError as e:
            print('?。≈鞴?jié)點未響應(yīng)')
            exit(-1)

    def crawl(self, counter):
        while True:
            try:
                if not self.task.empty():
                    url = self.task.get()

                    if url == 'end':
                        print('控制節(jié)點通知爬蟲節(jié)點停止工作...')
                        #接著通知其它節(jié)點停止工作
                        self.result.put({'new_urls': 'end', 'data': 'end'})
                        return
                    print('%s節(jié)點正在解析:%s'%(counter,url.encode('utf-8')))
                    counter += 1
                    content = self.downloader.download(url)
                    new_urls, data = self.parser.parser(url, content)
                    self.result.put({"new_urls": new_urls, "data": data})
            except EOFError as e:
                print("連接工作節(jié)點失敗")
                return
            except Exception as e:
                print(e)
                print('!!Crawled fail')

if __name__ == "__main__":
    spider = SpiderWork()
    spider.crawl()

## HtmlDownloader.py
# -*- coding: utf-8 -*-
import requests

class HtmlDownloader(object):
    '''
    HTML下載器:
        一個接口:
            網(wǎng)頁下載接口: downloader(url)
    '''

    def download(self,url):
        if url is None:
            return None
        user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        headers={'User-Agent': user_agent}
        r = requests.get(url,headers=headers)
        if r.status_code == 200:
            r.encoding = 'utf-8'
            return r.text
        return None
## HtmlParser.py
# -*- utf-8 -*-
import re
from urllib.parse import urlparse, urljoin
from bs4 import BeautifulSoup


class HtmlParser(object):
    '''
    HTML內(nèi)容解析器:
        一個對外接口:
            內(nèi)容解析器: parser(url,content)
    '''

    def parser(self, url, content):
        '''
        用于解析網(wǎng)頁內(nèi)容抽取URL和數(shù)據(jù)
        :param url: 下載頁面的URL
        :param content: 下載的網(wǎng)頁內(nèi)容
        :return:返回URL和數(shù)據(jù)
        '''
        if url is None or content is None:
            return
        soup = BeautifulSoup(content, 'html.parser', from_encoding='utf-8')
        new_urls = self._get_new_urls(url, soup)
        new_data = self._get_new_data(url, soup)
        return new_urls, new_data


    def _get_new_urls(self,page_url,soup):
        '''
        抽取新的URL集合
        :param page_url: 下載頁面的URL
        :param soup:soup
        :return: 返回新的URL集合
        '''
        new_urls = set()
        #抽取符合要求的a標簽: 示例
        links = soup.find_all('a',href=re.compile(r'/item/.*'))
        for link in links:
            #提取href屬性
            new_url = link['href']
            #拼接成完整網(wǎng)址
            new_full_url = urljoin(page_url,new_url)
            new_urls.add(new_full_url)
        return new_urls
    def _get_new_data(self,page_url,soup):
        '''
        抽取有效數(shù)據(jù): 示例
        :param page_url:下載頁面的URL
        :param soup:
        :return:返回有效數(shù)據(jù)
        '''
        data={}
        data['url']=page_url
        title = soup.find('dd',class_='lemmaWgt-lemmaTitle-title').find('h1')
        data['title']=title.get_text()
        summary = soup.find('div',class_='lemma-summary')
        #獲取到tag中包含的所有文版內(nèi)容包括子孫tag中的內(nèi)容,并將結(jié)果作為Unicode字符串返回
        data['summary']=summary.get_text()
        return data

nodeSpider的結(jié)構(gòu)很清晰,根據(jù)自己需要修改Html解析類即可。

至此,我們完成了整個打造簡單分布式爬蟲的任務(wù),這其中重在理清爬蟲框架結(jié)構(gòu),更重要的是要自己在理解了框架的基礎(chǔ)上動手實踐。

下一講:關(guān)于簡單分布式爬蟲的一點想法

參考資料:《Python爬蟲開發(fā)與項目實戰(zhàn)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容