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则结束。

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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下载器设计

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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和获取数据文本的信息。

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

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管理方案,如提取,数量判定等,也在这里实现。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

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()

            

最终效果:

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

猜你喜欢

转载自blog.csdn.net/java276582434/article/details/91384956
今日推荐