爬虫学习笔记(四)——遍历下载网站群link_crawler

遍历下载网站群————link_crawler
能够想到的方法有:1.通过网站地图 2.通过网站的url特点3.像普通用户一样追踪链接。
由于1、2两种方法比较简单,并且有很大的局限性,所以着重讲第三种方法,也是应用面更广的
方法。
实例:link_crawler('http://example.webscraping.com','/(index|view)')

1.版本1.0

import re
def link_crawler(seed_url):
    crawl_queue=[seed_url]
    while crawl_queue:
        url=crawl_queue.pop()
        html=download(url)#笔记三中的函数
        crawl_queue.append(get_link(html))
        
def get_link(html):
    webpage_patt=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
    return webpage_patt.findall(html.decode())#返回一个包含所以页面link的列表,由于匹配模式是str类型,而html是bytes类型。所以要把html进行decode()解码。--默认为utf-8来解码

2.按一定规则搜集同类型的页面,版本2.0

import re
def link_crawler(seed_url,link_res):
    crawl_queue=[seed_url]
    while crawl_queue:
        url=crawl_queue.pop()
        html=download(url)
        #加入一个过滤器
        for link in get_link(html):
            if re.match(link_res,link):
                crawl_queue.append(link)
        
def get_link(html):
    webpage_patt=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
    return webpage_patt.findall(html.decode())#返回一个包含所以页面link的列表

3.发现get_link的网站是相对链接,并不是绝对链接所以要拼接,用urllib.parse

版本3.0

import re
from urllib import parse
def link_crawler(seed_url,link_res):
    crawl_queue=[seed_url]
    while crawl_queue:
        url=crawl_queue.pop()
        html=download(url)
        #加入一个过滤器
        for link in get_link(html):
            if re.match(link_res,link):
                link=parse.urljoin(seed_url,link)
                crawl_queue.append(link)
        
def get_link(html):
    webpage_patt=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
    return webpage_patt.findall(html.decode())#返回一个包含所以页面link的列表

4.发现有的网站之间相互存在链接,为避免重复抓取。

版本4.0:

import re
from urllib import parse
def link_crawler(seed_url,link_res):
    crawl_queue=[seed_url]
    seen=set(crawl_queue)
    '''
    这里的前两行是初始赋值的作用,后面的循环中
    就不好赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    while crawl_queue:
        url=crawl_queue.pop()
        html=download(url)
        #加入一个过滤器#在过滤器中看是否重复
        for link in get_link(html):
            if re.match(link_res,link):
                link=parse.urljoin(seed_url,link)
                if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                    crawl_queue.append(link)
                    seen.add(link)
                    
def get_link(html):
    webpage_patt=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
    return webpage_patt.findall(html.decode())#返回一个包含所以页面link的列表

5.为了防止爬虫被禁,运用urllib.robotparser模块分析robots.txt

版本5.0

import re
from urllib import parse
from urllib import robotparser


def link_crawler(seed_url,link_res,User_agent='wswp'):
    crawl_queue=[seed_url]
    seen=set(crawl_queue)
    
    #读取robots.txt
    rp=robotparser.RobotFileParser()
    rp.set_url('http://example.webscraping.com/robots.txt')
    rp.read()
    '''
    这里的前几行是初始赋值的作用,后面的循环中
    就不再需要赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    while crawl_queue:
        url=crawl_queue.pop()
        #检查该url是否能被禁止爬取
        if rp.can_fetch(User_agent,url):
            html=download(url)
            #加入一个过滤器#在过滤器中看是否重复
            for link in get_link(html):
                if re.match(link_res,link):
                    link=parse.urljoin(seed_url,link)
                    if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                        crawl_queue.append(link)
                        seen.add(link)
        else:
            print('Blocked by robots.txt',url)
                        
def get_link(html):
    webpage_patt=re.compile('<a[^>]+href=["\'](.*?)["\']',re.IGNORECASE)
    return webpage_patt.findall(html.decode())#返回一个包含所以页面link的列表

6.下载限速,为了防止爬虫速度过快,遭到禁封和造成服务器过载的风险。需要在两次同一主站网站下载之间设置延迟:

这时直接加入程序到link_crawler函数中:
应该实现:1.要能记录两次下载时间的功能,计算是否需要延迟,以及延时多久。

版本6.0

import re
from urllib import parse
from urllib import robotparser
import time
def link_crawler(seed_url,link_res,User_agent='wswp'):
    crawl_queue=[seed_url]
    seen=set(crawl_queue)
    
    #读取robots.txt
    rp=robotparser.RobotFileParser()
    rp.set_url('http://example.webscraping.com/robots.txt')
    rp.read()
    '''
    这里的前几行是初始赋值的作用,后面的循环中
    就不再需要赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    b=0
    delay=3
    while crawl_queue:
        url=crawl_queue.pop()
        #检查该url是否能被禁止爬取
        if rp.can_fetch(User_agent,url):
            #时间管理器
            a=time.time()
            if delay:
                sleeptime=delay-(a-b)
                if sleeptime>0:
                    time.sleep(sleeptime)
            html=download(url)
            b=time.time()
            #加入一个过滤器#在过滤器中看是否重复
            for link in get_link(html):
                if re.match(link_res,link):
                    link=parse.urljoin(seed_url,link)
                    if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                        crawl_queue.append(link)
                        seen.add(link)
        else:
            print('Blocked by robots.txt',url)

但是,在访问有外链的网站时,比如hao123的网址有百度的链接。就不需要暂停。
所以还要实现以下功能:
1.要能记录两次下载时间的功能,计算是否需要延迟,以及延时多久。
2.要能记录不同主站的下载时间。————需要创建一个字典来建立对应关系。
eg:hao123,下的网址download完了,可以立刻下载baidu下的网址,但是要隔3秒才能下载hao123下的网址。
版本6.1

import time
import re
from urllib import parse
from urllib import robotparser
def link_crawler(seed_url,link_res,User_agent='wswp'):
    crawl_queue=[seed_url]
    seen=set(crawl_queue)
    #读取robots.txt
    rp=robotparser.RobotFileParser()
    rp.set_url('http://example.webscraping.com/robots.txt')
    rp.read()
    #时间管理器初始化
    #b=0
    last_time={}
    delay=3
    '''
    这里的前几行是初始赋值的作用,后面的循环中
    就不再需要赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    while crawl_queue:
        url=crawl_queue.pop()
        #检查该url是否能被禁止爬取
        if rp.can_fetch(User_agent,url):
            #时间管理器
            #记录下载的是哪个网站
            net_loc=urlparse(url).netloc
            #a=time.time()
            if delay:
                #sleeptime=delay-(time.time()-last_time[net_loc])#初始化的原因,一开始是空字典,这么做会出错。需要用到字典取值的另外的方法:.get(key),key存在返回value。不存在返回None
                if last_time.get(net_loc) is not None:
                    sleeptime=delay-(time.time()-last_time[net_loc])
                    if sleeptime>0:
                        time.sleep(sleeptime)
            html=download(url)
            last_time[net_loc]=time.time()
            #b=time.time()
            #加入一个过滤器#在过滤器中看是否重复
            for link in get_link(html):
                if re.match(link_res,link):
                    link=parse.urljoin(seed_url,link)
                    if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                        crawl_queue.append(link)
                        seen.add(link)
        else:
            print('Blocked by robots.txt',url)

虽然,至此已经解决了下载限速的问题。但是把这种有特点功能的程序都写在一起并不好,首先代码的可读性降低,其次等要修改时间管理器的时候还要找在什么地方要修改。
为了让程序有更好的扩展性,可以把它模块化。在程序设计越来越复杂的时候需要把一定功能的程序抽象为模块(面向对象的设计),让程序更加可控。
所以建立一个类,其作用就是时间管理器。
版本6.2

import re
from urllib import parse
from urllib import robotparser

class Timedelay:
    #初始化
    def __init__(self,delay):
        #设置延迟时间
        self.delay=delay
        #创建记录主站的字典
        self.domains={}
    #创建等待函数,同时还要实现记录走后一次访问时间
    def wait(self,url):
        netloc=urlparse(url).netloc
        last_time=self.domains.get(netloc)
        if self.delay and last_time:
            sleeptime=self.delay-(time.time()-last_time)
            if sleeptime>0:
                time.sleep(sleeptime)
        #每次暂停后,或者没暂停都重置最后一次访问时间
        self.domains[netloc]=time.time()
        
def link_crawler(seed_url,link_res,User_agent='wswp',delay=None,proxy=None):
    crawl_queue=[seed_url]
    seen=set(crawl_queue)
    
    #读取robots.txt
    rp=robotparser.RobotFileParser()
    rp.set_url('http://example.webscraping.com/robots.txt')
    rp.read()
    timedelay=Timedelay(delay)#同样是初始化
    '''
    这里的前几行是初始赋值的作用,后面的循环中
    就不再需要赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    while crawl_queue:
        url=crawl_queue.pop()
        #检查该url是否能被禁止爬取
        if rp.can_fetch(User_agent,url):
            timedelay.wait(url)#暂停,并记下本次主站下载开始时间
            html=download(url,proxy=proxy)
            #加入一个过滤器#在过滤器中看是否重复
            for link in get_link(html):
                if re.match(link_res,link):
                    link=parse.urljoin(seed_url,link)
                    if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                        crawl_queue.append(link)
                        seen.add(link)
        else:
            print('Blocked by robots.txt',url)  

7.爬虫陷阱。目前爬虫会跟踪以前从没有访问过的页面。但是有的网站动态生成页面内容,这样会出现无限多的网页。这样爬虫会无限制链接下去。

为了避免爬虫陷阱,简单的方法是到达当前页面经过了多少链接,也就是深度。
功能:当达到这个深度的时候就不往队列里添加链接了,修改seen,不仅可以记录,而且还有对应的深度记录。改为字典(判别重复的概率低了)

def link_crawler(seed_url,link_res,User_agent='wswp',delay=None,proxy=None,maxdepth=2):
    crawl_queue=[seed_url]
    #seen=set(crawl_queue)
    seen={}
    seen[seed_url]=0
    #读取robots.txt
    rp=robotparser.RobotFileParser()
    rp.set_url('http://example.webscraping.com/robots.txt')
    rp.read()
    timedelay=Timedelay(delay)#同样是初始化
    '''
    这里的前几行是初始赋值的作用,后面的循环中
    就不再需要赋值了,特别是在循环中很难操作set()
    使其增加
    '''
    while crawl_queue:
        url=crawl_queue.pop()
        #检查该url是否能被禁止爬取
        if rp.can_fetch(User_agent,url):
            timedelay.wait(url)#暂停,并记下本次主站下载开始时间
            html=download(url,proxy=proxy)
            #加入一个过滤器#在过滤器中看是否重复
            dept=seen[url]
            if dept!=maxdepth:
                for link in get_link(html):
                    if re.match(link_res,link):
                        link=parse.urljoin(seed_url,link)
                        if link not in seen:#先筛选符合条件的link,再进行筛选是否看过,这个顺序能减少工作量。
                            crawl_queue.append(link)
                            seen[link]=dept+1
            print(seen)
        else:
            print('Blocked by robots.txt',url)  
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容