V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
3023369823
V2EX  ›  Python

python 爬虫:百度网盘爬虫(资源校验问题求大神交流)

  •  
  •   3023369823 · 2016-08-23 12:04:26 +08:00 · 4383 次点击
    这是一个创建于 3018 天前的主题,其中的信息可能已经有所发展或是发生改变。

    因为要做去转盘网分类模式点我),所以一定要爬取网盘资源,本来想自己写一个爬虫挺不容易的,不想分享出来,但最后还是决定了拿给大家一起看吧,毕竟有交流才有进步,有兴趣的朋友也可以看看我写的其他日志或者关注我,会发现去转盘网的大部分技术现在可以说是公开状态,如有对你有帮助还是认真读读吧,下面是爬虫代码,我立马公开:

    ps :不会 python 的孩子先去学学 python ,代码是 python 写的

    我附上点资料:点我下载 1 点我下载 2

    磁力站的源码我也公开了,放到了下面,喜欢的看看:ok 搜搜

    TEL :继本篇之后,以下是其他所有重要的博客,喜欢的可以看看:

    中文分词算法 邀请好友注册 js 分页部分代码 数据库备份 ok 搜搜爬虫源代码

    #coding: utf8
     
    """
     
    author:haoning
     
    create time: 2015-8-15
     
    """
     
      
     
    import re #正则表达式模块
     
    import urllib2 #获取 URLs 的组件
     
    import time
     
    from Queue import Queue
     
    import threading, errno, datetime
     
    import json
     
    import requests #Requests is an Apache2 Licensed HTTP library
     
    import MySQLdb as mdb
     
      
     
    DB_HOST = '127.0.0.1'
     
    DB_USER = 'root'
     
    DB_PASS = ''
     
      
     
      
     
    #以下是正则匹配规则
     
    re_start = re.compile(r'start=(\d+)') #\d 表示 0-9 任意一个数字 后面有+号 说明这个 0-9 单个数位出现一到多次 比如 21312314
     
    re_uid = re.compile(r'query_uk=(\d+)') #查询编号
     
    re_urlid = re.compile(r'&urlid=(\d+)') #url 编号
     
      
     
    ONEPAGE = 20 #一页数据量
     
    ONESHAREPAGE = 20 #一页分享连接量
     
      
     
    #缺少专辑列表
     
    URL_SHARE = 'http://yun.baidu.com/pcloud/feed/getsharelist?auth_type=1&start={start}&limit=20&query_uk={uk}&urlid={id}' #获得分享列表
     
    """
     
    {"feed_type":"share","category":6,"public":"1","shareid":"1541924625","data_id":"2418757107690953697","title":"\u5723\u8bde\u58c1\u7eb8\u5927\u6d3e\u9001","third":0,"clienttype":0,"filecount":1,"uk":1798788396,"username":"SONYcity03","feed_time":1418986714000,"desc":"","avatar_url":"http:\/\/himg.bdimg.com\/sys\/portrait\/item\/1b6bf333.jpg","dir_cnt":1,"filelist":[{"server_filename":"\u5723\u8bde\u58c1\u7eb8\u5927\u6d3e\u9001","category":6,"isdir":1,"size":1024,"fs_id":870907642649299,"path":"%2F%E5%9C%A3%E8%AF%9E%E5%A3%81%E7%BA%B8%E5%A4%A7%E6%B4%BE%E9%80%81","md5":"0","sign":"1221d7d56438970225926ad552423ff6a5d3dd33","time_stamp":1439542024}],"source_uid":"871590683","source_id":"1541924625","shorturl":"1dDndV6T","vCnt":34296,"dCnt":7527,"tCnt":5056,"like_status":0,"like_count":60,"comment_count":19},
     
    public:公开分享
     
    title:文件名称
     
    uk:用户编号
     
    """
     
    URL_FOLLOW = 'http://yun.baidu.com/pcloud/friend/getfollowlist?query_uk={uk}&limit=20&start={start}&urlid={id}' #获得订阅列表
     
    """
     
    {"type":-1,"follow_uname":"\u597d\u55e8\u597d\u55e8\u554a","avatar_url":"http:\/\/himg.bdimg.com\/sys\/portrait\/item\/979b832f.jpg","intro":"\u9700\u8981\u597d\u8d44\u6599\u52a0994798392","user_type":0,"is_vip":0,"follow_count":2,"fans_count":2276,"follow_time":1415614418,"pubshare_count":36,"follow_uk":2603342172,"album_count":0},
     
    follow_uname:订阅名称
     
    fans_count :粉丝数
     
    """
     
    URL_FANS = 'http://yun.baidu.com/pcloud/friend/getfanslist?query_uk={uk}&limit=20&start={start}&urlid={id}' # 获取关注列表
     
    """
     
    {"type":-1,"fans_uname":"\u62e8\u52a8\u795e\u7684\u5fc3\u7eea","avatar_url":"http:\/\/himg.bdimg.com\/sys\/portrait\/item\/d5119a2b.jpg","intro":"","user_type":0,"is_vip":0,"follow_count":8,"fans_count":39,"follow_time":1439541512,"pubshare_count":15,"fans_uk":288332613,"album_count":0}
     
    avatar_url :头像
     
    fans_uname :用户名
     
    """
     
      
     
    QNUM = 1000
     
    hc_q = Queue(20) #请求队列
     
    hc_r = Queue(QNUM) #接收队列
     
    success = 0
     
    failed = 0
     
      
     
    def req_worker(inx): #请求
     
        s = requests.Session() #请求对象
     
        while True:
     
            req_item = hc_q.get() #获得请求项
     
             
     
            req_type = req_item[0] #请求类型,分享?订阅?粉丝?
     
            url = req_item[1] #url
     
            r = s.get(url) #通过 url 获得数据
     
            hc_r.put((r.text, url)) #将获得数据文本和 url 放入接收队列
     
            print "req_worker#", inx, url #inx 线程编号; url 分析了的 url
     
             
     
    def response_worker(): #处理工作
     
        dbconn = mdb.connect(DB_HOST, DB_USER, DB_PASS, 'baiduyun', charset='utf8')
     
        dbcurr = dbconn.cursor()
     
        dbcurr.execute('SET NAMES utf8')
     
        dbcurr.execute('set global wait_timeout=60000') #以上皆是数据库操作
     
        while True:
     
            """
     
            #正则备注
     
            match() 决定 RE 是否在字符串刚开始的位置匹配
     
            search() 扫描字符串,找到这个 RE 匹配的位置
     
            findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回
     
            finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回
     
                      百度页面链接: http://pan.baidu.com/share/link?shareid=3685432306&uk=1798788396&from=hotrec
     
            uk 其实用户 id 值
     
            """
     
            metadata, effective_url = hc_r.get() #获得 metadata (也就是前面的 r.text )和有效的 url
     
            #print "response_worker:", effective_url
     
            try:
     
                tnow = int(time.time()) #获得当前时间
     
                id = re_urlid.findall(effective_url)[0] #获得 re_urlid 用户编号
     
                start = re_start.findall(effective_url)[0] #获得 start 用户编号
     
                if True:
     
                    if 'getfollowlist' in effective_url: #type = 1 ,也就是订阅类
     
                        follows = json.loads(metadata) #以将文本数据转化成 json 数据格式返回
     
                        uid = re_uid.findall(effective_url)[0] #获得 re_uid ,查询编号
     
                        if "total_count" in follows.keys() and follows["total_count"]>0 and str(start) == "0": #获得订阅数量
     
                            for i in range((follows["total_count"]-1)/ONEPAGE): #开始一页一页获取有用信息
     
                                try:
     
                                    dbcurr.execute('INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 1, 0)' % (uid, str(ONEPAGE*(i+1)), str(ONEPAGE)))
     
                                    #存储 url 编号,订阅中有用户编号, start 表示从多少条数据开始获取,初始 status=0 为未分析状态
     
                                except Exception as ex:
     
                                    print "E1", str(ex)
     
                                    pass
     
                         
     
                        if "follow_list" in follows.keys(): #如果订阅者也订阅了,即拥有 follow_list
     
                            for item in follows["follow_list"]:
     
                                try:
     
                                    dbcurr.execute('INSERT INTO user(userid, username, files, status, downloaded, lastaccess) VALUES(%s, "%s", 0, 0, 0, %s)' % (item['follow_uk'], item['follow_uname'], str(tnow)))
     
                                    #存储订阅这的用户编号,用户名,入库时间
     
                                except Exception as ex:
     
                                    print "E13", str(ex)
     
                                    pass
     
                        else:
     
                            print "delete 1", uid, start
     
                            dbcurr.execute('delete from urlids where uk=%s and type=1 and start>%s' % (uid, start))
     
                    elif 'getfanslist' in effective_url: #type = 2,也就是粉丝列表
     
                        fans = json.loads(metadata)
     
                        uid = re_uid.findall(effective_url)[0]
     
                        if "total_count" in fans.keys() and fans["total_count"]>0 and str(start) == "0":
     
                            for i in range((fans["total_count"]-1)/ONEPAGE):
     
                                try:
     
                                    dbcurr.execute('INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 2, 0)' % (uid, str(ONEPAGE*(i+1)), str(ONEPAGE)))
     
                                except Exception as ex:
     
                                    print "E2", str(ex)
     
                                    pass
     
                         
     
                        if "fans_list" in fans.keys():
     
                            for item in fans["fans_list"]:
     
                                try:
     
                                    dbcurr.execute('INSERT INTO user(userid, username, files, status, downloaded, lastaccess) VALUES(%s, "%s", 0, 0, 0, %s)' % (item['fans_uk'], item['fans_uname'], str(tnow)))
     
                                except Exception as ex:
     
                                    print "E23", str(ex)
     
                                    pass
     
                        else:
     
                            print "delete 2", uid, start
     
                            dbcurr.execute('delete from urlids where uk=%s and type=2 and start>%s' % (uid, start))
     
                    else: #type=0 ,也即是分享列表
     
                        shares = json.loads(metadata)
     
                        uid = re_uid.findall(effective_url)[0]
     
                        if "total_count" in shares.keys() and shares["total_count"]>0 and str(start) == "0":
     
                            for i in range((shares["total_count"]-1)/ONESHAREPAGE):
     
                                try:
     
                                    dbcurr.execute('INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 0, 0)' % (uid, str(ONESHAREPAGE*(i+1)), str(ONESHAREPAGE)))
     
                                except Exception as ex:
     
                                    print "E3", str(ex)
     
                                    pass
     
                        if "records" in shares.keys():
     
                            for item in shares["records"]:
     
                                try:
     
                                    dbcurr.execute('INSERT INTO share(userid, filename, shareid, status) VALUES(%s, "%s", %s, 0)' % (uid, item['title'], item['shareid'])) #item['title']恰好是文件名称
     
                                    #返回的 json 信息:
     
                                except Exception as ex:
     
                                    #print "E33", str(ex), item
     
                                    pass
     
                        else:
     
                            print "delete 0", uid, start
     
                            dbcurr.execute('delete from urlids where uk=%s and type=0 and start>%s' % (uid, str(start)))
     
                    dbcurr.execute('delete from urlids where id=%s' % (id, ))
     
                    dbconn.commit()
     
            except Exception as ex:
     
                print "E5", str(ex), id
     
        dbcurr.close()
     
        dbconn.close() #关闭数据库
     
         
     
    def worker():
     
        global success, failed
     
        dbconn = mdb.connect(DB_HOST, DB_USER, DB_PASS, 'baiduyun', charset='utf8')
     
        dbcurr = dbconn.cursor()
     
        dbcurr.execute('SET NAMES utf8')
     
        dbcurr.execute('set global wait_timeout=60000')
     
        #以上是数据库相关设置
     
        while True:
     
      
     
            #dbcurr.execute('select * from urlids where status=0 order by type limit 1')
     
            dbcurr.execute('select * from urlids where status=0 and type>0 limit 1') #type>0,为非分享列表
     
            d = dbcurr.fetchall()
     
            #每次取出一条数据出来
     
            #print d
     
            if d: #如果数据存在
     
                id = d[0][0] #请求 url 编号
     
                uk = d[0][1] #用户编号
     
                start = d[0][2]
     
                limit = d[0][3]
     
                type = d[0][4] #哪种类型
     
                dbcurr.execute('update urlids set status=1 where id=%s' % (str(id),)) #状态更新为 1 ,已经访问过了
     
                url = ""
     
                if type == 0: #分享
     
                    url = URL_SHARE.format(uk=uk, start=start, id=id).encode('utf-8') #分享列表格式化
     
                    #query_uk uk 查询编号
     
                    #start
     
                    #urlid id url 编号
     
                elif  type == 1: #订阅
     
                    url = URL_FOLLOW.format(uk=uk, start=start, id=id).encode('utf-8') #订阅列表格式化
     
                elif type == 2: #粉丝
     
                    url = URL_FANS.format(uk=uk, start=start, id=id).encode('utf-8') #关注列表格式化
     
                if url:
     
                    hc_q.put((type, url)) #如果 url 存在,则放入请求队列, type 表示从哪里获得数据
     
                    #通过以上的 url 就可以获得相应情况下的数据的 json 数据格式,如分享信息的,订阅信息的,粉丝信息的
     
                     
     
                #print "processed", url
     
            else: #否则从订阅者或者粉丝的引出人中获得信息来存储,这个过程是爬虫树的下一层扩展
     
                dbcurr.execute('select * from user where status=0 limit 1000')
     
                d = dbcurr.fetchall()
     
                if d:
     
                    for item in d:
     
                        try:
     
                            dbcurr.execute('insert into urlids(uk, start, limited, type, status) values("%s", 0, %s, 0, 0)' % (item[1], str(ONESHAREPAGE)))
     
                            #uk 查询号,其实是用户编号
     
                            #start 从第 1 条数据出发获取信息
     
                            #
     
                            dbcurr.execute('insert into urlids(uk, start, limited, type, status) values("%s", 0, %s, 1, 0)' % (item[1], str(ONEPAGE)))
     
                            dbcurr.execute('insert into urlids(uk, start, limited, type, status) values("%s", 0, %s, 2, 0)' % (item[1], str(ONEPAGE)))
     
                            dbcurr.execute('update user set status=1 where userid=%s' % (item[1],)) #做个标志,该条数据已经访问过了
     
                            #跟新了分享,订阅,粉丝三部分数据
     
                        except Exception as ex:
     
                            print "E6", str(ex)
     
                else:
     
                    time.sleep(1)
     
                     
     
            dbconn.commit()
     
        dbcurr.close()
     
        dbconn.close()
     
             
     
    def main():
     
        print 'starting at:',now()
     
        for item in range(16):   
     
            t = threading.Thread(target = req_worker, args = (item,))
     
            t.setDaemon(True)
     
            t.start() #请求线程开启,共开启 16 个线程
     
        s = threading.Thread(target = worker, args = ())
     
        s.setDaemon(True)
     
        s.start() #worker 线程开启
     
        response_worker()  #response_worker 开始工作
     
        print 'all Done at:', now()   
    

    特别求问:

    本人现在发现个问题,百度云的链接失效问题很难解决,比如:

    https://pan.baidu.com/share/link?uk=186824885&shareid=1531849562

    当向百度发送 http 请求的时候,开始是好的,但随着 http 请求的频率过快,百度开始限制 ip ,也就是资源校验开始失效,这个问题很让然头疼,希望 V2EX 上的大神能够给出宝贵建议或者可行见解,十分感谢。

    本人建个 qq 群,欢迎大家一起交流技术, 群号: 512245829 喜欢微博的朋友关注:转盘娱乐即可

    11 条回复    2016-08-24 18:11:34 +08:00
    3023369823
        1
    3023369823  
    OP
       2016-08-23 12:06:49 +08:00
    沙发归 lz ,急求最后 lz 提出的问题的解决方案:
    **特别求问:**

    本人现在发现个问题,百度云的链接失效问题很难解决,比如:

    https://pan.baidu.com/share/link?uk=186824885&shareid=1531849562

    当向百度发送 http 请求的时候,开始是好的,但随着 http 请求的频率过快,百度开始限制 ip ,也就是资源校验开始失效,这个问题很让然头疼,希望 V2EX 上的大神能够给出宝贵建议或者可行见解,十分感谢。
    firefox12
        2
    firefox12  
       2016-08-23 13:24:54 +08:00
    因为你解决的问题 是百度要干掉的东西。 一开始不禁止 是怕误伤普通用户,随着你请求资源越来越多,会有越来越多的限制,最后直接跳验证码都是有可能的,任何一家公司都会这么做。
    3023369823
        3
    3023369823  
    OP
       2016-08-23 15:05:14 +08:00
    @firefox12 有思路?
    Thoxvi
        4
    Thoxvi  
       2016-08-24 10:12:48 +08:00
    既然限制 IP ,那能不能使用分布式?
    bianchensz
        5
    bianchensz  
       2016-08-24 10:12:56 +08:00
    前端狗表示无能为力,帮顶
    3023369823
        6
    3023369823  
    OP
       2016-08-24 10:26:53 +08:00
    @bianchensz js 发送 http 请求要跨域,这个显然是不符合互联网规范的,实现不了
    3023369823
        7
    3023369823  
    OP
       2016-08-24 10:27:38 +08:00
    @Thoxvi 分布式的话理论是可行的,但是费用又是个问题
    liubo
        8
    liubo  
       2016-08-24 15:33:37 +08:00
    限制 ip ,可以考虑用代理。
    1 、自己扫常见代理端口,收集可用代理。这个不太推荐,我自己尝试过效率比较低下,还不如花点钱买服务....
    2 、收集网络上的免费代理,然后验证;
    3 、用付费的代理服务。跟免费的相对,一般会提供 api 支持,直接调调接口就能获取代理列表。这个可用性稍微好一点,不过也不算高,自己定时验证一下可用性就行了。
    4 、还有一些专门给爬虫做代理服务的,这个自己找吧.
    3023369823
        10
    3023369823  
    OP
       2016-08-24 18:11:17 +08:00
    @liubo 谢谢哈,我研究下
    3023369823
        11
    3023369823  
    OP
       2016-08-24 18:11:34 +08:00
    @holajamc 好的,谢谢你,我研究下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1823 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 16:31 · PVG 00:31 · LAX 08:31 · JFK 11:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.