美文网首页
第4章 并发下载

第4章 并发下载

作者: AmaAnchor | 来源:发表于2019-03-06 10:30 被阅读0次

    串行处理几百个网页还勉强,在爬取拥有100万网页的大型网站时,耗时超过11天(假设每秒1个网页的速度)

    4.1

    目标:
    http://s3.amazonaws.com/alexa-static/top-1m.csv.zip中获取全球热门网站列表
    并取前500个网站用链接爬虫爬取

    from io import BytesIO,TextIOWrapper
    import requests
    from zipfile import ZipFile
    
    url='http://s3.amazonaws.com/alexa-static/top-1m.csv.zip'
    urls=[]
    resp=requests.get(url,stream=True)
    with ZipFile(BytesIO(resp.content)) as zf:
        csv_name=zf.namelist()[0]
        with zf.open(csv_name) as csv_file:
            for item in csv_file:
                website=item.decode('utf-8').split(',')[1]
                urls.append(website)
    

    抽象成Alexa类:

    import csv
    from zipfile import ZipFile
    from io import TextIOWrapper, BytesIO
    import requests
    
    
    class AlexaCallback:
        def __init__(self, max_urls=500):
            self.max_urls = max_urls
            self.seed_url = 'http://s3.amazonaws.com/alexa-static/top-1m.csv.zip'
            self.urls = []
    
        def __call__(self):
            resp = requests.get(self.seed_url, stream=True)
            with ZipFile(BytesIO(resp.content)) as zf:
                csv_filename = zf.namelist()[0]
                with zf.open(csv_filename) as csv_file:
                    for _, website in csv.reader(TextIOWrapper(csv_file)):
                        self.urls.append('http://' + website)
                        if len(self.urls) == self.max_urls:
                            break
    
    

    爬取目标改变
    由于墙太厚,爬取速度太慢
    我将爬取目标改为了爬取http://top.chinaz.com/上前500个购物网站

    只获取内链的代码

    if not re.match('^(/|'+domain+')',href):#筛选出内部网页
                        continue
    

    4.2串行爬虫

    相比之前的链接爬虫添加了:
    1.爬取的域名变了,需要更新对每个站点中的robots.txt文件的处理方式。
    2. url路径合法性问题
     串行爬虫所用时间
    

    以下代码从http://top.chinaz.com/获取排名前500的购物网站并且写入到本地

    import requests
    from bs4 import BeautifulSoup
    import re
    import json
    import sys
    import csv
    sys.path.append('..')
    from download import download
    
    '''
    从Chinaz.com爬取最受欢迎的前几百个网页存入列表
    
    '''
    class GetWebList(object):
        def __init__(self,web_num=100,max_page=20):#web_num:需要的网页数  max_page:要爬取多少页
            self.web_num=web_num
            self.web_li=[]
            self.max_page=max_page
        
        def __call__(self):
            start_url='http://search.top.chinaz.com/top.aspx?p='
            page=1
            while page<self.max_page:
                url=start_url+str(page)
                html=download(url)
                soup=BeautifulSoup(html,'html.parser')
                div_li=soup.find_all('div',attrs={'class':'ContTit ulli clearfix'})
                for div in div_li:
                    href=div.find('div',attrs={'class':'w320 PCop'}).a.get('href')
                    href='http://'+re.findall(r'site_(.*?)\.html',href)[0]
                    self.web_li.append(href)
                page+=1
    
    
    
    path='D:\study\python\python_crawler\data\weblist.csv'
    
    if __name__=='__main__':
        gwl=GetWebList()
        gwl()
        with open(path,'w',newline='') as fp:
            cw=csv.writer(fp)
            for web in gwl.web_li:
                cw.writerow([web])#cw.writerow函数会将传入的参数解包然后写入一行的各列
        
    

    核心代码部分:

    while crawl_li:
            #观察urls发现  待爬取列表中都缺少http协议
            url=crawl_li.pop()
            print('url:',url)
            domain='http://'+urlparse(url).netloc
            no_robots=False
            rp=robots.get('domain')
            if not rp:#不在robots缓存
                robot_url='{}/robots.txt'.format(domain)
                rp=get_robot_parser(robot_url)
                if not rp:
                    no_robots=True
                else:
                    robots[domain]=rp
            if no_robots or rp.can_fetch(user_agent,url):
                depth=seen.get(url,0)
                if depth==max_depth:#判断深度
                    print('this may be a trap:',url)
                    continue
                html=d(url)
                if not html:
                    continue
                href_li=re.findall("""<a\shref=['"](.*?)['"]""",html)#一个html资源的所有链接
                for href in href_li:
                    if not re.match('^(/|'+domain+')',href):#筛选出内部网页
                        continue
                    #以下为针对url的合法性处理
                    if 'http' not in href:
                        if href.startswith('//'):
                            href='{}{}'.format('http:',href)
                        elif href.startswith('://'):
                            href='{}{}'.format('http',href)
                        else:#href为相对链接的情况
                            href='{}{}'.format(domain,href)
                        if href in seen:#判断该href是否已经爬过
                            print('the url has been visited before!'
                            continue
                        crawl_li.append(href)
                        seen[url]=depth+1
    

    爬取用时:

    #由于只是为了学习爬取,没有对每个待爬网站做定制化处理
    #有部分网站爬取失败
    386.28738594055176
    

    4.3多线程爬虫

    多线程爬虫,提高cpu利用率
    不需要上锁,不会导致死锁情形

    顺便复习下python多线程相关知识
    常规思路是在主线程中:
    定义一个线程方法,里面是线程要执行的操作
    定义一个线程队列,实时记录线程的情况
    因此,要将单线程链接爬虫该为多线程,只要将涉及待爬取队列的操作由多个线程操作即可
    相关代码:

    def process_queen():#线程的操作函数
            while crawl_li:
                #观察urls发现  待爬取列表中都缺少http协议
                url=crawl_li.pop()
                print('url:',url)
                domain='http://'+urlparse(url).netloc
                no_robots=False
                rp=robots.get('domain')
                if not rp:#不在robots缓存
                    robot_url='{}/robots.txt'.format(domain)
                    rp=get_robot_parser(robot_url)
                    if not rp:
                        no_robots=True
                    else:
                        robots[domain]=rp
                if no_robots or rp.can_fetch(user_agent,url):
                    depth=seen.get(url,0)
                    if depth==max_depth:#判断深度
                        print('this may be a trap:',url)
                        continue
                    html=d(url)
                    if not html:
                        continue
                    href_li=re.findall("""<a\shref=['"](.*?)['"]""",html)#一个html资源的所有链接
                    for href in href_li:
                        if 'com' not in href or 'cn' not in href:
                            continue
                        if 'http' not in href:
                            #以下为针对url的合法性处理
                            if href.startswith('//'):
                                href='{}{}'.format('http:',href)
                            elif href.startswith('://'):
                                href='{}{}'.format('http',href)
                            else:#href为相对链接的情况
                                href='{}{}'.format(domain,href)
                            if href in seen:#判断该href是否已经爬过
                                print('the url has been visited before!')
                                continue
                            crawl_li.append(href)
                            seen[url]=depth+1
    
        threads=[]
        while threads or crawl_li:
            for thread in threads:
                if not thread.isAlive():
                    threads.remove()
            while len(threads)<max_thread and crawl_li:
                thread=threading.Thread(target=process_queen)
                thread.setDaemon(True)
                thread.start()
                threads.append(thread)
            for thread in threads:
                thread.join()
    
    

    多进程爬虫
    暂时不作了解

    相关文章

      网友评论

          本文标题:第4章 并发下载

          本文链接:https://www.haomeiwen.com/subject/xidouqtx.html