• python学习笔记(一)


      因为工作需要,经常需要到新浪某博客去找资料,在博文目录里一页页地肉眼搜索,看到合适的标题再点击开链接查看内容,知道合适地再复制下来。很烦人。于是一直有个想法,学会爬虫。

      拿着单位发的购书卡去买了本入门的书《python编程从入门到实践》,凭着一点编程的底子,三个小时看完了基础部分,然后安装python就开始搜集各种网络文字教程,开始实践。

      (一)正则标题式提取博文文字

      新浪博客博文一句话可以放在几个标签里面,感觉很奇葩(也许就是我无知)。因为乱七八糟的缘故,sir学习了一下re正则表达式模块,然后开始动手

    #导入request方法
    from urllib import request
    #导入正则表达式模块
    import re
    
    #打开网页,获取响应文本
    response=request.urlopen('http://blog.sina.com.cn/s/blog_xxxxxxx.html')
    
    #读取网页源码 不知道怎么表达这个东西
    page=response.read()
    
    #转码
    js= page.decode('utf-8')
    
    #编译匹配模式
    pat=re.compile(r'((?<=>)[^>]+?(?=</FONT>)|(?<=>)[^>]+?(?=</SPAN>))')
    
    #匹配网页内容
    match=re.findall(pat,js)
    
    #若匹配则输出
    if match:
        n=0
        for each_match in match:
            n+=1
            print(n," : "+each_match +'
    ')
    

      正则匹配模式写得不够完美把极少数的无关的内容匹配出来,加上各种网络教程各种的看不懂,使得sir失去了努力的方向。

      (二)根据Excel中的Url下载图片

      时隔一个月,突然有了采集图片的机会,又让sir有了一点动力。因为URL已经通过其他爬虫采集到excel工作簿当中,于是sir又学习了一下第三方包openpyxl,知道如何操作Excel之后又开始动手了。

    from urllib import request                    #导入request函数
    from openpyxl import load_workbook #导入load_workbook函数
    
    #自定义一个根据url和图片文件名称为参数,自动下载图片的函数
    def downloadImage(imageUrl,imagePath):
        response=request.urlopen(imageUrl)#访问图片地址,获得响应
        imageContents=response.read()#获取图片内容
        f=open(imagePath,'wb')#打开文件
        f.write(imageContents)#写入内容
        f.close#关闭文件
    
    
    #打开Excel workbook
    wb=load_workbook('image.xlsx')
    #指定工作表
    ws=wb.active
    #获取单元格内容 从第二行开始 到空白单元格结束 i=2 while ws.cell(row=i,column=1).value!=None: #生成文件名 A列为文件名称 Path= str(i-1)+'_'+ws.cell(row=i,column=1).value +'.jpg' #获取URL地址 B列为URL地址 Url=ws.cell(row=i,column=2).value #调用函数下载图片 downloadImage(imageUrl=Url,imagePath=Path) #循环下载图片 print('已下载第'+str(i-1)+'张高清图片>>>') i+=1

      因为函数downloadimage内部没有添加异常处理,于是碰到request失败的时候,脚本就停止不动了。添加了异常处理之后,sir有发觉下载的速度真的好慢。于是第二天求助了一位相识。

      (三)多线程下载图片

      这位朋友发了一段代码,让我自己学习。研究之后sir发现了,把具体要做的事情,放到多线程下载那个类的内部,就可以使用多线程下载。速度立马快的飞起。30秒给我下了2000多张图片,高达1G,还好我及时终止了脚本。

      另外,Sir学到异常处理怎么写,也知道利用requests.session()创建会话以便更快地下载。唯一费解的就是在json中找到图片地址,花了一段时间才明白怎么回事,使用了josn.loads()之后把内容粘贴到在线解析网站去,然后肉眼分析数据的储存结构。

    import requests
    import json
    import threading
    
    Default_Header = {
        #具体请求头自己去弄
    }
    _session=requests.session()
    _session.headers.update(Default_Header)
    
    
    #多线程下载
    class myThread(threading.Thread):
        def __init__(self,imgUrl,fname):
            threading.Thread.__init__(self)
            self.imgUrl=imgUrl
            self.fname=fname
        def run(self):
            print("downloading",self.imgUrl)
            download(self.imgUrl,self.fname)
    
    def download(fileid,type):
        img_url="http://img.hb.aicdn.com/"+fileid
        imgresp=requests.get(img_url)
        byte_img = imgresp.content
        try:
            out = open(type, 'wb')
            out.write(byte_img)
            out.flush()
            out.close()
        except Exception as e:
            print(e)
    if __name__ == "__main__":
        r =_session.get('http://huaban.com/pins/873774526/?xxxxxx')
        url=json.loads(r.text)
        urlList=url['pin']['board']['pins']
        for i in urlList:
            key=i['file']['key']
            print(key)
            #download(key,key+'.jpg')
            myThread(key,key+'.jpg').start()
    

      

      (四)多线程采集改造

      吸收了一些精华之后,sir开始改造自己爬虫。首先遇到的问题就是变量的作用域问题,函数内部的私有变量 需用通过 global 方法全局化。第二个是线程无序执行的问题,按我的理解,主线程就是一条生产线,有AB先后两个生产环节,假设A环节安排5个人开工,就是多线程。遗憾的是,生产工具只有一套,同一时间只能一个人进行A环节生产,5个人都想着先完成工作于是抢占生产工具,当1个人抢到生产工具时,剩下的人就要等着。到最后,出现了两个问题:一是5个人都完成了A环节的工作,但是我们没有办法得知谁先谁后;二是而且5个人在进行A环节的同时,B环节也在进行,就会出现:B环节结束了,A环节还有人没有完成任务。为了解解决两个问题,就需要按顺序把子线程放进列表,然后逐一启动,然后再用线程的join()方法,让A环节之后的B环节等着,等着5个人都完成了,才能继续。

     

    from urllib import request     
    from bs4 import BeautifulSoup  
    from openpyxl import Workbook   
    import threading
    import re
    import time
    
    #功能函数
    def get_title(url):
        global n          #变量全局化
        global pageindex  #变量全局化
        #global ws        #为什么声明与不声明都一样?
        try:              #打开网页,保存试卷标题和链接
            r=request.urlopen(url,timeout=10) #打开网页,设置超时 response
            s=r.read()    #读取网页源码
            js=s.decode('utf-8') #网页转码
            soup=BeautifulSoup(js,'lxml') #网页解析
            #正则查找子节点
            for a in soup.find_all('a',href=re.compile(r'http://blog.sina.com.cn/s/blog')): 
                n+=1      #计数
                ws.cell(row=n+1,column=1).value=n             #写入序号
                ws.cell(row=n+1,column=2).value=a.string      #写入标题
                ws.cell(row=n+1,column=3).value=a.get('href') #写入链接
            print("download succeed >>>>"+url+'
    ')           #正常输出
        except  Exception as e:
            print("download failed  >>>>"+url+'
    ')           #异常输出
            
    if __name__ =='__main__':#主线程
        start=time.time()    #开始时间
        filename='多线程标题与链接.xlsx' #Excel文件名
        threads=[]           #新建线程列表
        wb=Workbook()        #新建工作簿
        ws=wb.active         #活动工作表
        n=0                  #初始化计数
        #输入表头
        ws.cell(row=n+1,column=1).value='序号'
        ws.cell(row=n+1,column=2).value='标题'
        ws.cell(row=n+1,column=3).value='链接'
    
        #设置起止页码
        pageindex=1
        pagecount=10
        #filename=str(pagecount)+filename
        
        while pageindex<=pagecount:
            url=u'http://blog.sina.com.cn/s/articlelist_xxxxxxxxx_0_'+str(pageindex)+'.html'
            t=threading.Thread(target=get_title,args=(url,))#添加子线程
            threads.append(t) #加入线程列表
            pageindex+=1      #下一页
    
        
        for t in threads:
            t.setDaemon(True) #守护线程 必须在start()之前
            t.start()         #开始线程
                              #若t.join()放此处 则变成单线程
        for t in threads:
            t.join()          #线程等待 主线程必须在子线程执行结束之后 才能继续  
        
        wb.save(filename=filename) #保存工作簿
        end=time.time()       #结束时间
        print('Usedtime %f seconds!' % (end-start) +'
    ') #输出耗时!
    

      

       (五)多进程采集

      在了解多线程的时候,sir也知道了多进程这个概念,也知道有人评价说多线程在python中是鸡肋一般的存在。要实现快速采集,需要使用多进程。多进程就好像是有5条流水线,可以同时开工,每条生产线之间相互独立。然后sir又遇到了一个问题,上面的例子中,不同子线程可以操作同一个worksheet工作表,因为线程在一个时间段内始终有先后。但是进程确实同时发生的,必然就出现多个进程同时写入worksheet的问题,造成紊乱。于是就有了共享变量的问题。一般的变量可以用共享变量的办法,比如一个整型变量或者一个数组(事先确定大小),但是python里的高级对象,如列表,字典等等就需要使用multiprocessing.Manager()进行托管。这样才能在不同进程之间共享。sir在此处遇到了一些问题,比如列表如何托管,第三方包的对象worksheet如何托管?经过尝试之后,字典对象托管成功了。大致思路就是多进程,托管字典,爬完了数据再将字典的数据写入worksheet。因为入门没几天,还在用pythonIDLE导致多进程无法测试。需要关闭py文件之后双击里运行。

    from urllib import request     
    from bs4 import BeautifulSoup  
    import re
    import time
    import multiprocessing
    import os
    from openpyxl import Workbook
    ################################################################
    def get_title(url,n,d):
        try:
            r=request.urlopen(url,timeout=10)
            s=r.read()   
            js=s.decode('utf-8') 
            soup=BeautifulSoup(js,'lxml') 
            for a in soup.find_all('a',href=re.compile(r'http://blog.sina.com.cn/s/blog')):
                n.value+=1
                d[n.value]=(n.value,str(a.string),str(a.get('href')))
            print('request succeed : '+ url)
        except Exception as e:
            print('request failed : '+ url)
    ################################################################        
    def execute_func(q,n,lock,d):
        while True:
            url=q.get()
            #lock.acquire()
            get_title(url,n,d)
            #lock.release()
            q.task_done()
    ################################################################        
    def in_queue(urls,q):
        for url in urls:
            print("添加到队列"+url)
            q.put(url)#把url放入队列
    
    ################################################################        
    if __name__ =='__main__':
        os.system("pause")
        start=time.time()
    
        filename='博文目录.xlsx'
        wb=Workbook()
        ws=wb.active
        
        n=multiprocessing.Value('i',0)
        d = multiprocessing.Manager().dict()
        lock=multiprocessing.Lock()
        q=multiprocessing.JoinableQueue()#创建队列
        
        pageindex=1
        pagecount=398
    
        urls=[]#URL列表
        while pageindex<=pagecount:
            url=u'http://blog.sina.com.cn/s/articlelist_xxxxxxxxxx_0_'+str(pageindex)+'.html'
            urls.append(url)
            pageindex+=1
            
        for i in range(10):      #添加多个子进程,执行同一个函数
            p=multiprocessing.Process(target=execute_func,args=(q,n,lock,d))
            p.daemon=True #守护进程
            p.start()               #启动进程
        
        in_queue(urls,q)
        print(">>>>>>>>>>添加队列完成>>>>>>>>>>")
    
        q.join()              #等待队列里所有项被处理
        ws.append(['Index','Title','Url'])
        print(">>>>>>>>>>爬取完成,正在写入excel>>>>>>>>>>")
        for v in d.values():
            #print (v)
            ws.append(v)
        #print (d.values())
        print(">>>>>>>>>>写入excel完成,正在保存>>>>>>>>>>")    
        wb.save(filename=filename)
        end=time.time()       #结束时间
    
        print('Usedtime %f seconds!' % (end-start) +'
    ') #输出耗时!
        
        os.system("pause")
    

      结果,花了80多秒,把一个博客400页的目录给爬下来。

      (六)多进程多线程

      为了验证自己的想法,sir把下载图片的函数放进了线程,嵌套在上面的例子里,也不知道算什么东西了。结果就是下载好快。然后把shell关闭了,我以为了终止了脚本,但线程还在不断地执行,按照那样的爬取速度,sir的C盘一会就得满。不知所措的我只好选择了关机……

      

      不是科班出身并非理由,好些概念、原理都要现学现用,学习起来困难还是不小,继续努力~~~~~

      

      

      

      

  • 相关阅读:
    bzoj 1195: [HNOI2006]最短母串 爆搜
    bzoj 4066: 简单题 kd-tree
    NOI冲刺计划2
    bzoj 3572: [Hnoi2014]世界树 虚树 && AC500
    bzoj 3153: Sone1 Toptree
    CTSC && APIO 总结
    bzoj 4031: [HEOI2015]小Z的房间 轮廓线dp
    bzoj 1902: Zju2116 Christopher lucas定理 && 数位DP
    BZOJ 1754: [Usaco2005 qua]Bull Math
    BZOJ 1648: [Usaco2006 Dec]Cow Picnic 奶牛野餐
  • 原文地址:https://www.cnblogs.com/nextseven/p/7117212.html
Copyright © 2020-2023  润新知