python写简单的爬虫——延迟(下载限速)

import urllib.request as ur
from urllib.error import URLError,ContentTooShortError,HTTPError
import re
from urllib.parse import urljoin
from urllib import robotparser
import time
from urllib.parse import urlparse

class Throttle(object):
    def __init__(self,delay):
        self.delay=delay
        #保留上一次完成的时间的时间戳
        self.domains={}

    def wait(self,url):
        #result.scheme : 网络协议
        # result.netloc: 服务器位置(也有可能是用户信息)
        # result.path: 网页文件在服务器中的位置
        # result.params: 可选参数
        # result.query: &连接键值对
        # result.fragment:
        domain=urlparse(url).netloc
        last_successed=self.domains.get(domain)
        if self.delay>0 and last_successed is not None:
            sleep_secs=self.delay-(time.time()-last_successed)
            #判断时间是否符合延迟设定
            if sleep_secs>0:
                time.sleep(sleep_secs)
        self.domains[domain]=time.time()

def download(url, num_retries=2, user_agent='wswp',charset='utf-8',proxy=None):
    print('Downloading:',url)
    request=ur.Request(url)
    #添加请求头(一般添加'Cookies','User-Agent')
    #默认请求头wswp (Web Scrapying With Python)
    request.add_header('User-Agent',user_agent)
    #运用异常,try...except...处理遇到一些无法控制的错误的情况:
    try:
        if proxy:
            getProxy()
        resp=ur.urlopen(request)
        #headers.get_content_charset()得到请求Http响应返回的字符集
        cs=resp.headers.get_content_charset()
        #如果不存在,则采用默认的字符集utf-8
        if not cs:
            cs=charset
        #decode()表示根据某种编码表示
        html=resp.read().decode(cs)
    except (URLError,ContentTooShortError,HTTPError) as e:
        #e.reason 输出错误的原因
        print('Download error:',e.reason)
        html=None
        if num_retries>0:
            #hasattr(object,name)判断对象(objedt)是否包含对应属性(name)
            if hasattr(e,'code') and (500<=e.code<600):
                #一般地说,4XX错误都是发生在请求中的,5XX错误都是发生在服务器端的
                #重下载,排除由5XX引起的错误,设定重下载次数为num_retries
                return download(url,num_retries-1)
    return html


#获取代理IP
def getProxy():
    proxy_address = ur.urlopen("{代理IP网址}").read().decode('utf-8')
    proxy_handler = ur.ProxyHandler({'http',proxy_address})
    proxy_openner = ur.build_opener(proxy_handler)
    return proxy_openner
#注意ur.urlopen和ur.install_opener的区别
#install_opener是全局定义 以后的每一次ur.urlopen会默认调度install_opener

#解析robots.txt文件,以避免下载禁止爬取的URL
def get_robots_parser(robot_url):
    rp=robotparser.RobotFileParser()
    #set_url加载robots.txt文件
    rp.set_url(robot_url)
    rp.read()
    return rp


def link_crawlinks(start_url,link_regex,robots_url=None,user_agent='wswp'):
    crawl_queue=[start_url]
    #从根目录开始爬取,只要在其url末尾加入robots.txt.
    if not robots_url:
        robots_url='{}/robots.txt'.format(start_url)
    rp=get_robots_parser(robots_url)
    #集合化crawl_queue,用于判断是否有重复元素
    seen=set(crawl_queue)
    while crawl_queue:
        #设定每次的指定延迟时间
        throttle=Throttle(delay=0.5)
        #删除末尾的信息,并存储到url内
        url=crawl_queue.pop()
        #增加解析器,判断是否符合robts.txt
        if rp.can_fetch(user_agent,url):
            #爬虫的延迟
            throttle.wait(url)
            html=download(url,user_agent=user_agent)
            if not html:
                continue
            for link in get_links(html):
            #出现符合linke_regrex形式的url,将他保存至crawl_queue
                if re.match(link_regex,link):
                    #urljoin(url1,url2)拼接两个地址
                    abs_link=urljoin(start_url,link)
                    if abs_link not in seen:
                        #避免重复访问同一个网站
                        seen.add(abs_link)
                        crawl_queue.append(abs_link)
        else:
            print('Blocked by robots.txt:',url)

def get_links(html):
    #re.compile指定匹配的规; re.IGNORECASE忽略大小写; re.findall找到所有满足条件的信息
    webpage_regex=re.compile("""<a[^>]+href=["'](.*?)['"]""",re.IGNORECASE)
    return webpage_regex.findall(html)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值