课程设计 Python 网络爬虫(广度优先方法)

很多语言都可以实现爬虫,由于python的易用性,这里使用python.

  • 程序功能:爬取一个域名下的所有网页,并将网页之间的指向关系存储在字典中。可以通过domain 设置域名、可以通过depth设置深度。
  • 程序原理:1. 使用urllib.request.urlopen 打开网页,使用BeautifulSoup解析打开的网页;2. 使用BeautifulSoup.find功能找到网页中的链接,然后将链接存入List中;3.转到步骤1,继续打开网页,重复depth次。
  • 程序要点:1. (非常重要)打开网页要设置时间间隔,不能打开一个后马上下一个,否则一方面可能会被网站拉入黑名单,从而拒绝你的网页请求,另一方面会造成网站服务器的压力,造成不必要的麻烦;2. 记录网页爬行的路径,避免重复爬行,这个可以通过字典、数据库等好多方式实现;3要分清爬行到的记录是否为网站内部网页,经常出现的一种情况是爬着爬着就不知道爬到哪里去了,大型网站平均每个网页都会有好几千个链接,如果不设定爬行边界,那爬行的网页数量将呈指数级增长,这是我们不希望看到的;4. 虽然有很多链接看起来不一样,但实际上二者是同一个网页,比如说网站的内容页:http://www.81.cn/jmywyl/2018-07/06/content_8081243.htm、http://www.81.cn/jmywyl/2018-07/06/content_8081840.htm、http://www.81.cn/jmywyl/2018-07/06/content_8081240.htm ,这些链接看起来是不同的,但实际上他们采用了同一个模板自动生成,如果想将他们记为一类,则需要下点功夫进行归类了。

下面的程序只实现了程序要点中的1、2、3。实现方法都是最简单、基础的方法。至于如何实现4,还在进一步思考中,将在下一篇中更新。

欢迎留言探讨更深一步的优化

代码实现主要包括以下几个部分:

  • [x] 定义一个类,包装整个爬取的程序 UrlParser
  • [x] 定义一个根据当前页面搜索包含链接列表的方法 Parser
  • [x] 定义一个可以存储链接之间相互联系的字典,并设置添加节点的方法 addNode
  • [x] 定义一个可以判断链接是否为内部链接的方法 isInnerLink
  • [x] 定义一个存储爬取结果的方法 saveGraph

代码如下:


# -*- coding: utf-8 -*-
"""
# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
import re
import logging

class UrlParser:
    def __init__(self,domain,depth,domainName):
        self.domain = domain
        print(self.domain)
        self.depth = depth
        self.domainName = domainName
        self.LinkGraph= {}
        self.LinkGraph[self.domain] = self.domain
        logging.basicConfig(filename='D://example.txt',level=logging.DEBUG)
        logging.info('=====================starting==========')
    #使用字典存储网页之间的连接关系  
    def addNode(self, parent,children):
        if(self.LinkGraph.get(children)!=None):
            return False
        else:
            self.LinkGraph[children] = parent
            return True
        
    #判断一个链接是否为网站内部的链接
    def isInnerLink(self,url):
        # url 以http开头 则判断是否在同一个domain下
        # url 不以http开头 则将url 补全
        if(url==None):
            return ''
        if(url.startswith('http')):
            if(re.match(r'^https*://\w*\.'+self.domainName+'\.',url)!=None):
                return url
            else:
                return ''
        else:
            if(url.startswith('//')):
                url = url[2:]
                if(re.match(r'\w*\.'+self.domainName+'\.',url)!=None):
                    return 'http://'+url
                else:
                    return ''
            elif(url.startswith('/')):
                return self.domain+url                
            else:
                if(re.match(r'\w*\.'+self.domainName+'\.',url)!=None):
                    return 'http://'+url
                else:
                    return ''    
    #依次访问LinkListpre中的链接指向的网页,并分析网页,得到网页中的链接
    def Parser(self,LinkListpre,LinkListnext):
        for url in LinkListpre:
            print(url)
            try:
                html = urlopen(url)
            except BaseException as e:
                logging.error('Parser::::warmning: url cannot open:::'+url)
                continue;
            try:
                bsObj = BeautifulSoup(html.read(),'lxml')
                for link in bsObj.find_all('a'):
                    #print(link)
                    try:
                        link = link['href']
                    except KeyError as e:
                        continue
                    
                    #是内部链接
                    link = self.isInnerLink(link)
                    #长度超过2 是内部链接 且没有访问过 
                    if ((len(link.rstrip())>2)):
                        if(self.addNode(url,link)):
                            LinkListnext.append(link)
            except BaseException as e:
                logging.error('Parser::::warmning: page cannot be parsed:::'+url)
            #模拟人类点击习惯,设置随机间隔
            time.sleep(1+random.random())
        return True
    def saveGraph(self):
        Gra = pd.DataFrame({'next':self.LinkGraph})
        Gra.to_csv('project1/test.csv')
    def Go(self):
        LinkListpre = [self.domain]
        LinkListnext = []
        
        for i in range(self.depth):
            print('====================')            
            self.Parser(LinkListpre,LinkListnext)
            print(len(LinkListnext))
            LinkListpre = LinkListnext
            LinkListnext = []
            if(len(LinkListpre)<1):
                print('*********')
                break
        self.saveGraph()

MyParser = UrlParser('https://XXX.mycooper.com',2,'mycooper')   
MyParser.Go()    
 

你可能感兴趣的:(课程设计 Python 网络爬虫(广度优先方法))