【爬虫之路】A站文章围观量&B站视频播放量的简单爬虫想法

众所周知,acfun与bilibili一直是国内受到广泛关注的宅社区以及二次元社区,一直以来我有一个想法,便是如何获知两个网站里最有意思的作品,仅仅依靠官方的推荐并不足以满足个人化的需求。
于是这只小爬虫便诞生了。

0.一开始的想法是抓取b站av8到av2000000+的近两百万个视频的播放量,然后降序排序。
但面临着两个问题,一是播放页并非静态网页,播放量来自于js自动生成,于是我们通过抓取一个视频的uid与aid来打开含有播放量的静态页面,从而达到目的。
二是某些投稿已被和谐,会出现404 not found错误,于是我们通过try&except的方式跳过死链。
解决以上两个问题后,发现又有了两个新的问题,一个是每一个视频页面的cid与uid出现方式不同(不同视频源所导致),导致正则表达式引擎无法正确抓取出aid&cid号。另一个问题是速度过慢,10M宽带环境下提取100个视频播放量就需要耗时40s左右。前者需要分析每一种视频源的html格式,后者需要多线程解决,极大的增加了难度。

1.在第一种爬虫想法搁浅后,通过相关查阅,我发现一个或许可以优化的方法,便是通过分类来抓取,我们知道b站有每个区导航的页面,例如http://www.bilibili.com/video/part-twoelement-1.html 表示完结番剧的第一页,查看源代码我们发现这是个静态页面,播放量已经生成,例如这是其中一段html代码:

<a href="/video/av2198990/" target="_blank" class="title">【意大利语配音】秒速五厘米【诸神】a><div class="info">硬盘 意大利语版的秒五,也没有什么好介绍的,只是想吐槽欧洲配音的开头都没有体现出萝莉正太音啊=。=div><div class="w-info"><i class="gk" title="观看" number="1063">1063i><i class="sc" title="收藏" number="32">32i><i class="dm" title="弹幕" number="18">18i><a href="http://space.bilibili.com/98665" target="_blank" title="死蝶浮月-真月"><i class="up r10000">死蝶浮月-真月i>a><i class="date" title="日期">2015-04-08 20:51i>div>div>li><li class="l1"><div class="l-item"><a href="/video/av2198400/" target="_blank" class="preview"><img src="http://i2.hdslb.com/320_180/video/20/20f652c7d08f08cb727f348dee87db2a.jpg">a>

只需稍作正则表达式的处理我们就可以获得想要的信息。
按照这个想法写好python代码后,进行解释运行,发现需要进行一个网站传回的gzip包的处理,百度了一下,三行代码即解决。
测试版代码(这里是针对amv&mad区的视频爬取)如下

#这只是个人的一点兴趣,尚需要大量优化,谨慎使用

#coding:utf-8


import urllib2
import re
import gzip
import StringIO

doc='''
Attention:
It is not a perfect spider,be careful.

Author : Flint_x and you
   Ver : 0.1415926
contact: 940852578@qq.com

'''
print (doc)

# 页数
page_num = 3212

# 最小观看量
look_max_num = 100000

i = 0

video_get_list = []
#这个while可以做成一个函数
while i < page_num:
    i += 1
    print r'the', i, ' page, total',  page_num, ' page'
    this_url = "http://www.bilibili.com/video/douga-mad-" + str(i) + ".html"
    try:
        data = urllib2.urlopen(this_url).read()

        data = StringIO.StringIO(data)#gzip包处理
        gz_page = gzip.GzipFile(fileobj=data)
        html = gz_page.read()   

        re_str = '''
  • (.*?)/i>'''#正则表达式提取播放数 re_pat = re.compile(re_str) dh_list = re_pat.findall(html) # print dh_list print '-------------------------' for item in dh_list: # print '-------->', item look_str = '''title="观看">(.*?)<''' look_pat = re.compile(look_str) look_list = look_pat.findall(item) if str(look_list[0]).find('-') < 0: #会员的世界不计入排行 look_num = int(look_list[0]) if look_num > look_max_num: dn_str = '''class="title">(.*?) dn_pat = re.compile(dn_str) dn_list = dn_pat.findall(item) av_str = ''' av_pat = re.compile(av_str) av_list = av_pat.findall(item) print '-------->video name:', dn_list[0] print '-------->look number:', look_list[0] video_get_list.append({'av' : av_list[0], 'name': dn_list[0], 'look': look_list[0]}) except urllib2.HTTPError, e: pass except urllib2.URLError, e: pass # 排序 video_get_list.sort(key=lambda obj: int(obj.get('look')), reverse=True) # for dh in video_get_list: # print '-------->', dh['name'], '观看数量:', dh['look'] # print '------->获得动画总数:', len(video_get_list) print r'------->loading~' # 新建文件写入结果 new_path_filename = "D:\mad&amv_list.txt" f = open(new_path_filename, 'w+') i = 0 for dh in video_get_list: i += 1 insert_str = 'No.' + str(i) + '\n' + str(dh['av']) + '\n' + str(dh['name']) + '\n' +' 观看数量:' + str(dh['look']) + '\n\n' f.write(insert_str) f.close()
  • 2.陆续爬了几个区之后,觉得代码bug挺多的,而且可读性挺糟糕,于是用函数优化了一下,并且开始转战A岛,重新分析了一下A岛html源码与b站的区别,改写了正则表达式。
    代码如下:

    #coding:utf-8
    
    import urllib2
    import gzip
    import re
    import StringIO
    
    doc = '''
    Attention:
    It is not a perfect spider,be careful.
    
    Author : Flint_x and you
       Ver : 0.1415926
    contact: 940852578@qq.com
    
    '''
    
    print (doc)
    
    arc_get_list = []
    
    # def deal_gzip(data):
    #   data = StringIO.StringIO(data)
    #   gz_page = gzip.GzipFile(data)
    #   html = gz_page.read()
    #   return html
    
    
    def get_acfun(type_name,page_num,min_click_num):
        i = 0
        while i < page_num:
    
            i += 1
            print r'the ' + str(i) + ' page , Total ' + str(page_num) + ' page'
            this_url = 'http://www.acfun.tv/v/' + type_name +'/index_' + str(i) + '.htm'
            # print this_url
            try:
                data = urllib2.urlopen(this_url).read()
                # print data
            # gzip包处理
                # data = StringIO.StringIO(data)
                # gz_page = gzip.GzipFile(fileobj = data)
                # data = gz_page.read()
            # 正则表达式提取关键字
                re_str = ''''''
                re_pat = re.compile(re_str)
                arc_list = re_pat.findall(data)
                # print arc_list[0]
                print '-------------cut-off rule-----------------'
    
                for item in arc_list:
                    # 统计点击量
                    click_str = '''条评论,(.*?)人围观'''
                    click_pat = re.compile(click_str)
                    click_list = click_pat.findall(item)
                    # print click_list[0]
    
                    if str(click_list[0]).find('-') < 0:
                        click_num = int(click_list[0])
                        if click_num > min_click_num:
                            # 标题提取
                            name_str = '''class="title">(.*?)'''
                            name_pat = re.compile(name_str)
                            name_list = name_pat.findall(item)
                            # print name_list
                            # ac号提取
                            ac_str = '''# 评论数提取
                            com_str = '''共有(.*?)条评论'''
                            com_pat = re.compile(com_str)
                            com_list = com_pat.findall(item)
                            # print com_list[0]
                            com_num = int(com_list[0])
                            #得分计算公式
                            point = click_num + 50 * com_num
    
                            print '---->',ac_list[0]
                            print '---->',name_list[0]
                            print '---->click number :',click_list[0]
                            print '---->comment number :',com_list[0]
                            print '---->points :',point
                            print '---------------------------------------------------------'
                            # 添加文章信息
                            arc_get_list.append({'ac' : ac_list[0] , 'name' : name_list[0] , 'click' : click_list[0] , 'comment' : com_list[0] , 'point' : point})
            except urllib2.HTTPError, e:
                print 'urllib2.HTTPError'
            except urllib2.URLError, e:
                print 'urllib2.URLError'
    
    def sort_and_write(new_path,list):
        arc_get_list.sort(key = lambda obj: int(obj.get('point')) , reverse = True)
        print 'Total Arcticle : ', len(arc_get_list)
        print '---------->Writing~'
    
        f = open(new_path,'w+')
        x = 0 
        for arc in list :
            x += 1
            insert_str1 = 'No.' + str(x) + '\n' + str(arc['ac']) + '\n' + str(arc['name']) + '\n'
            insert_str2 = '共有 ' + str(arc['click']) + ' 人围观,' + str(arc['comment']) + '条评论' + '\n'
            f.write(insert_str1)
            f.write(insert_str2)
        f.close()
    
    get_acfun('list110',7520,100000)
    sort_and_write("D:\Arc_list.txt",arc_get_list)
    

    3.不足之处:
    3.0 速度还是很慢,解决方式无非有提升网速或多线程处理,还需进一步研究。分布式爬虫也不错(笑
    3.1 扩展功能还有很多,下次可以尝试征服评论区
    3.2 会员的世界还没想好合适的统计方式,可以参考b站周刊的算法
    3.3 知识还是不够,还要多多学习!!!

    你可能感兴趣的:(爬虫之路)