zoukankan      html  css  js  c++  java
  • python网络爬虫(9)构建基础爬虫思路

    目的意义

    基础爬虫分5个模块,使用多个文件相互配合,实现一个相对完善的数据爬取方案,便于以后更完善的爬虫做准备。

    这里目的是爬取200条百度百科信息,并生成一个html文件,存储爬取的站点,词条,解释。

    本文思路来源书籍。其代码部分来源书籍。https://book.douban.com/subject/27061630/

    功能模块

    主文件:爬虫调度器,通过调用其他文件中的方法,完成最终功能实现。

    其他文件:URL管理器HTML下载器HTML解析器数据存储器

    设计思路

    定义SpiderMan类作为爬虫调度器。输入根URL开始爬取数据然后爬取结束。

    在爬取过程中,需要获取网页,和解析网页。

    解析网页需要HTML解析器,获取网页需要HTML下载器

    解析网页需要解析的数据有:URL,TITLE,CONTEXT等。则需要URL管理器数据存储器

    主文件设计

    主文件添加根URL,然后提取该URL,下载该URL内容。

    根据内容,调用解析器:

          解析出该URL中的新URL,存入URL管理器;

          解析出该URL中的标题,文本等信息,存入数据存储器。

    完成后开始下一次。这时URL管理器多出了新的URL,提取出新的URL,下载,解析,不断重复即可。

    重复结束以提取出的URL数量超过200则结束。

    代码如下:

    from BaseSpider.DataOutput import DataOutput
    from BaseSpider.HtmlDownloader import HtmlDownloader
    from BaseSpider.HtmlParser import HtmlParser
    from BaseSpider.UrlManager import UrlManager
    class SpiderMan():
        def __init__(self):
            self.manager=UrlManager()
            self.downloader=HtmlDownloader()
            self.parser=HtmlParser()
            self.output=DataOutput()
            
        def crawl(self,root_url):
            self.manager.add_new_url(root_url)
            while(self.manager.has_new_url() and self.manager.old_url_size()<200):
                new_url=self.manager.get_new_url()
                text=self.downloader.download(new_url)
                if text is None:
                    print('None text')
                    break
                new_urls,data=self.parser.parser(new_url,text)
                self.manager.add_new_urls(new_urls)
                self.output.store_data(data)
                print(self.manager.old_url_size())       
            self.output.output_html()
        
    if __name__ == "__main__":
        spider_man=SpiderMan()
        spider_man.crawl("https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711?fr=aladdin")
        print('finish')
    

    作为最初的设计,应该允许异常抛出,便于查看程序终止的原因,然后排查错误。

    HTML下载器设计

    下载网页,返回文本。即可。

    import requests
    import chardet
    class HtmlDownloader(object):
        def download(self,url):
            if url is None:
                return None
            user_agent='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'
            headers={'User-Agent':user_agent}
            r=requests.get(url,headers=headers)
            if r.status_code is 200:
                r.encoding=chardet.detect(r.content)['encoding']
                return r.text
            return None
        
    

    HTML解析器设计

    HTML解析器将下载的文本进行解析,需要解析出的数据有:页面的新URL页面的新数据文本

    建立相应的解析器,需要打开源码对比,然后进行使用源码分析,使用BeautifulSoup获取所需信息。

    为了便于主函数调用或者其他原因,将所有数据通过parser实现返回,其parser分别调用获取URL和获取数据文本的信息。

    为了处理一些不同网页可能抓取的意外情况导致程序终止,添加了一些判断。

    import re
    from urllib import parse
    from bs4 import BeautifulSoup
    class HtmlParser(object):
        def parser(self,page_url,html_cont):
            if page_url is None or html_cont is None:
                return
            soup=BeautifulSoup(html_cont,'lxml')
            new_urls=self.getNewUrls(page_url,soup)
            new_data=self.getNewData(page_url,soup)
            return new_urls,new_data
        
        def getNewUrls(self,page_url,soup):
            new_urls=set()
            links=soup.find_all('a',href=re.compile(r'/item/.*'))
            for link in links:
                new_url=link['href']
                new_full_url=parse.urljoin(page_url,new_url)
                new_urls.add(new_full_url)
            return new_urls
        
        def getNewData(self,page_url,soup):
            data={}
            data['url']=page_url
            title=soup.find('dd',class_="basicInfo-item value")
            if title is not None:
                data['title']=title.string
                summary=soup.find('meta',attrs={"name":"description"})
                data['summary']=summary['content']
                return data
            else:
                title=soup.find('meta',attrs={"name":"keywords"})
                if title is not None:
                    data['title']=title['content']
                    summary=soup.find('meta',attrs={"name":"description"})
                    data['summary']=summary['content']
                    return data
                else:
                    data['title']="ERROR!"
                    data['summary']="Please check the url for more information"
                    data['url']=page_url
                    return data
                

    URL管理器设计

    为了避免重复的URL,使用python的set,建立集合初始化。参阅:https://www.runoob.com/python3/python3-set.html

    使用old_urls存储已经访问过的网址,使用new_urls存入将要提取的网址。

    然后写好has_new_url等方法,辅助主程序调用。当得到新的URL们时,主程序调用函数将他们存入。

    而主程序需要的其他URL管理方案,如提取,数量判定等,也在这里实现。

    class UrlManager():
        def __init__(self):
            self.old_urls=set()
            self.new_urls=set()
            pass
        
        def has_new_url(self):
            return self.new_url_size()!=0
        
        def new_url_size(self):
            return len(self.new_urls)
        
        def old_url_size(self):
            return len(self.old_urls)
        
        def get_new_url(self):
            new_url=self.new_urls.pop()
            self.old_urls.add(new_url)
            return new_url
        
        def add_new_url(self,url):
            if url is None:
                return
            if url not in self.new_urls and url not in self.old_urls:
                self.new_urls.add(url)
            pass
        
        def add_new_urls(self,urls):
            if urls is None or len(urls) == 0:
                return
            
            for url in urls:
                self.add_new_url(url)
            pass
    

    数据存储器设计

    通过HTML解析器获取的数据,通过数据存储器进行存储。

    而最终将数据从内存写入到本地磁盘,也在该文件实现。

    为了调试美观,建议是先爬取一两个数据做好测试,写好table的宽度设定,加入style='word-break:break-all;word-wrap:break-word;'参数。参阅:https://zhidao.baidu.com/question/1385859725784504260.html

    import codecs
    class DataOutput(object):
        def __init__(self):
            self.datas=[]
        
        def store_data(self,data):
            if data is None:
                return
            self.datas.append(data)
        
        def output_html(self):
            fout=codecs.open('baike.html', 'w', encoding='utf-8')
            fout.write("<html>")
            fout.write("<head><meta charset='urf-8'></head>")
            fout.write("<body>")
            fout.write("<table border='1' width=1800  style='word-break:break-all;word-wrap:break-word;'>")
            fout.write("<tr>")
            fout.write("<td width='300'>URL</td>")
            fout.write("<td width='100'>标题</td>")
            fout.write("<td width='1200'>释义</td>")
            fout.write("</tr>")
            for data in self.datas:
                fout.write("<tr>")
                fout.write("<td><a href=%s>%s</a></td>"%(data['url'],data['url']))
                fout.write("<td>%s</td>"%data['title'])
                fout.write("<td>%s</td>"%data['summary'])
                fout.write("</tr>")
            fout.write("</table>")  
            fout.write("</body>")      
            fout.write("</html>")
            fout.close()
                
    

    最终效果:

    当然还有一些数据没有处理好。

  • 相关阅读:
    Django(app的概念、ORM介绍及编码错误问题)
    Django(完整的登录示例、render字符串替换和redirect跳转)
    Construct Binary Tree from Preorder and Inorder Traversal
    Single Number II
    Single Number
    Binary Tree Level Order Traversal II
    Binary Tree Level Order Traversal
    Binary Tree Zigzag Level Order Traversal
    Recover Binary Search Tree
    Add Binary
  • 原文地址:https://www.cnblogs.com/bai2018/p/10994735.html
Copyright © 2011-2022 走看看