• python实战--csdn博客专栏下载器


       打算利用业余时间好好研究Python的web框架--web.py,深入剖析其实现原理,体会web.py精巧之美。但在研究源码的基础上至少得会用web.py。思前想后,没有好的Idea,于是打算开发一个csdn博客专栏下载器,界面不是很好看,还过得去吧。

    效果图如下:



    为了简单,下载以html格式保存。

    下载我自己的博客专栏,目录列表


    主界面html文件非常简单,如下:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>CSDN 博客专栏下载器</title>
    <link rel="stylesheet" type="text/css" href="/static/main.css"/>
    <script type="text/javascript" src="static/main.js"></script>
    </head>
    
    <body>
    <input type="text" class="name" name="csdnname" id="csdnid"/><br/>
    <button type="button" class="btn" onclick="category()">获取专栏</button>
    <div id="categorylist">
    </div>
    <div id="download">
    </div>
    <div id="status">
    </div>
    <div id="footer">
    </div>
    </body>
    </html>
    


    获取后端数据使用Ajax,没有用封装好的库,所以看起来很简洁。

    function ajax(requesturl,handler){
    	var xmlhttp;
    	if (window.XMLHttpRequest){
      		xmlhttp=new XMLHttpRequest();
      	}
    	else{
      		xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
      	}
      	xmlhttp.onreadystatechange=function(){
      		if (xmlhttp.readyState==4 && xmlhttp.status==200){
        		handler(xmlhttp.responseText);
        	}
      	}
    	xmlhttp.open("GET",requesturl,true);
    	xmlhttp.send();
    }
    
    function category(){
    	var name=document.getElementById('csdnid').value;
    	document.getElementById('categorylist').innerHTML=""
    	if (name==""){
    		alert("用户名不可以为空");
    	}
    	else{
    		ajax('category'+'?name='+name,function(content){
    					document.getElementById('categorylist').innerHTML=content;
    				   });
    	}
    }
    
    function down(){
    	var box=document.getElementsByName('check');
    	var atag=new Array();
    	for (var i=0; i<box.length; i++) {
    		if(box[i].checked){
    			atag.push(box[i].value);
    		}
    	}
    	var astring=atag.join('*');
    	ajax('down'+'?urls='+astring,function(content){
    					if(content!=""){
    						document.getElementById('download').innerHTML=content;
    					}
    					else{
    						document.getElementById('download').innerHTML="正在下载";
    					}
    				   });
    }
    
    


     ajax函数,一个参数是请求url,另一个是请求成功回调函数。在函数里创建一个XMLHttpRequest对象,发送请求给url,并调用回调函数。

     category函数,主要是获取csdn用户ID,并发送给ajax请求获取专栏信息,成功后显示。

     down函数,获取选中的复选框,将选中专栏url拼接后发送ajax请求。


    主文件,如下

    #coding=utf-8
    import web
    import os
    import urllib2
    import cookielib
    import re
    import threading
    import thread
    import sys
    
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    urls = (
        '/','index',
        '/category(.*)','category',
        '/down','down'
    
           )
    render = web.template.render('templates/')
    
    #所有用到的正则表达式
    contentMatch={
        'category':re.compile(r"<div id="panel_Category"(.*?)博(.*?)>(.*?)</div>", re.I|re.DOTALL), #专栏显示
        'zlalink':re.compile(r"a(s*)href(s*)=("|')(.*?)(3)(.*?)>(.*?)</a>",re.I|re.DOTALL),   #各个专栏url
        'blogalink': re.compile(r"<a(s*)name(.*?)href(s*)=("|')(.*?)(4)",re.I|re.DOTALL),  #博客链接url
        'lastpagenum': re.compile(r"<a(s*)href=(.*?)?page=(d)">尾页",re.I|re.DOTALL),    #尾页链接
        'title':re.compile(r"<title>(.*?)</title>",re.I|re.DOTALL)
        }
    
    class Http:
        """
           由于CSDN做了特殊处理,如果使用简单的httplib2.Http().request()会抓取不到数据,所以我们需要模拟真实用户行为,
        """
        def __init__(self):
            cookie_support= urllib2.HTTPCookieProcessor(cookielib.CookieJar())
            self.opener = urllib2.build_opener(cookie_support,urllib2.HTTPHandler)
            #urllib2.install_opener(opener)
            self.opener.addheaders = [("User-agent","Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"),("Accept","*/*"),("Referer","http://www.google.com")]
    
        def open(self,url):
            return self.opener.open(url)
    
    class index:
        """
        首页
        """
        def GET(self):
            return render.index()
    
    class category:
        """
        获取专栏信息,并显示相应的复选框
        """
        def GET(self,name):
            name=web.input(name=None).name
            url = "http://blog.csdn.net/"+name
            try:
                opener=Http()
                res=opener.open(url)
                content = res.read()
                category_match=contentMatch['category'].search(content)
                if category_match:
                    left=category_match.group()
                    right=""
                    list_match=contentMatch['zlalink'].findall(left)
                    for i in range(len(list_match)):
                        if i%2 == 1:
                            right+="""
                            <input type="checkbox" name="check" value="%s" /> %s<br />
                            """ % (list_match[i][3],list_match[i][6])
                    submit="""<button type="button" class="btn" onclick="down()">下载专栏</button>"""
                    return left+right+submit
                else:
                    return "该用户没有开通专栏"
            except Exception:
                return "请检查网络和用户名"
    
    class createfile(threading.Thread):
        """
        下载专栏中文章的线程类
        """
        def __init__(self,zlurl):
            threading.Thread.__init__(self)
            self.blogurl=[]
            self.opener=Http()
            self.zlname=""
            try:
                res=self.opener.open(zlurl)
                content = res.read()
                zlname_match=contentMatch['title'].search(content)
                if zlname_match:
                    self.zlname="".join(zlname_match.group(1).split('-')[:-2]).decode('utf8')
                    if not os.path.exists(self.zlname):
                        os.mkdir(self.zlname)
                else:
                    thread.exit_thread()
                if content.find("尾页") < 0:
                    self.addblog(content)
                else:
                    page_match=contentMatch['lastpagenum'].search(content)
                    page=int(page_match.group(3))
                    for x in range(1,page+1):
                        url="%s?page=%d" %(zlurl,x)  #分页处理
                        content=self.opener.open(url).read()
                        self.addblog(content)
    
            except Exception,e:
                print 'init:'+str(e)
                thread.exit_thread()
    
        def addblog(self,content): #获取文章url
            try:
                blogs_match=contentMatch['blogalink'].findall(content)
                if blogs_match:
                    for m in blogs_match:
                        if m[4] not in self.blogurl:
                            self.blogurl.append(m[4])
            except Exception,e:
                print "addblog:"+str(e)
    
        def write(self,content): #写入文件
            try:
                if content !="":
                    blogtitle="".join(contentMatch['title'].search(content).group(1).split('-')[:-3])
                    #path="%s%s%s.html" %(self.zlname.encode('utf8'),os.sep,blogtitle)
                    path="%s.html" % blogtitle
                    f = open(path.decode('utf8'),"w")
                    f.write(content.decode('utf8'))
                    f.close()
            except Exception,e:
                print "write:"+str(e)
    
        def run(self):
            try:
                print len(self.blogurl)
                for blog in self.blogurl:
                    self.write(self.opener.open(blog).read())
            except Exception,e:
                print "run:"+str(e)
    
    
    
    class down:
        def GET(self):
            urls=web.input().urls
            urllist=urls.split('*')
            for url in urllist:
                file=createfile(url)
                file.start()
                file.join()
            #等待线程结束,结束后返回下载完成
            return "下载完成"
    
    if __name__=='__main__':
        app = web.application(urls,globals())
        app.run()


       使用了多线程,博主有几个专栏,将启动几个线程,主程序等待线程结束。

        在这里也遇到了一个问题,本身是每个专栏一个文件夹,但处理时有点问题,拼接path后open时总是报没有这个文件或文件夹,应该是编码的问题。

    留个遗憾,等待各位亲的指教。



  • 相关阅读:
    math相关实现
    集合——PHP实现
    PHP Fatal error——内存用尽
    偶串——状态记忆
    享元模式
    php对象的传递——“通过引用传递”or“传递的是object identifier”?
    JSONObject的parseArray方法作用。
    java android 中的Toast
    delphi android 中 Toast 的实现(老外写的UNIT)
    Delphi XE5 for android 调用Java类库必看的文件
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3212333.html
Copyright © 2020-2023  润新知