使用python爬虫框架scrapy抓取虎牙主播数据

前言

本文利用python的scrapy框架对虎牙web端的主播、主播订阅数、主播当前观看人数等基本数据进行抓取,并将抓取到的数据以csv格数输出,以及存储到mongodb中

思路

观察虎牙网站后确认所有频道url都在www.huya.com/g中的,而主播房间数据则是ajax异步数据,获取数据的链接为

http://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId={频道id}&tagAll=0&page={页码}

该链接通过控制gameId和page来返回某频道下某页的数据,根据以上观察爬行设计思路如下
第一步:访问www.huya.com/g页面,在li(class类型为game-list-item)中获取当前所有频道的链接、标题、频道id
第二步:根据第一步获取到的频道的链接进入频道页面,在频道页面获取当前频道页数,再根据该频道id,页数构造异步数据请求链接
第三步:从第二步中获取频道返回的异步数据内容,将返回的json数据类型转化为字典,再获取要抓取的目标内容。
第四步:向第三步中获取到的主播房间url发出请求,进入房间页面后抓取主播订阅数
第五步:将数据输出为csv格式以及存在mongodb数据库中。

频道分类页面
ajax异步请求对应的链接

代码

<b>items</b>
在items中定义要抓取的字段内容,items代码如下

class HuyaspiderItem(scrapy.Item):
    channel = scrapy.Field() #主播所在频道
    anchor_category   = scrapy.Field() #主播类型
    anchor_name = scrapy.Field()       #主播名称  
    anchor_url  = scrapy.Field()       #直播房间链接  
    anchor_tag  = scrapy.Field()       #主播标签
    anchor_roomname = scrapy.Field()    #主播房间名称
    position    = scrapy.Field()       #当前频道的主播排名 
    watch_num   = scrapy.Field()       #观看人数
    fan_num     = scrapy.Field()      #订阅数量
    crawl_time  = scrapy.Field()      #爬取时间

<b>pipelines</b>
在pipelines中设置输出为csv表以及将数据保存到mongodb中,pipelines代码设置如下

# -*- coding: utf-8 -*-

import json,codecs
import pymongo

class HuyaspiderPipeline(object):
        
    def __init__(self):  
              
        self.file = codecs.open('huyaanchor.csv','wb+',encoding='utf-8')   #创建以utf-8编码的csv文件
               
        client    = pymongo.MongoClient('localhost',27017)  #创建mongodb连接

        db        = client['huya']      #创建mongodb数据库huya

        self.collection =db['huyaanchor']    #创建数据库huya中collection

    def process_item(self, item, spider):

        item = dict(item)                   #将抓到的item转为dict格式

        line = json.dumps(item)+'\n'         #定义line字段将抓到的item转为jump格式,加上空格换行

        self.file.write(line.decode('unicode_escape')) #将line写进csv中输出

        self.collection.insert(item)     #将item写进mongodb中

<b>middlewares</b>
在middlewares中以继承UserAgentMiddleware父类方式创建创建HuyaUserAgentMiddlewares类,该类用于scrapy每次执行请求时使用指定的useragent,middlewares代码如下

from scrapy import signals
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware 
import random

class  HuyaUserAgentMiddleware(UserAgentMiddleware):

    def __init__ (self,user_agent=""):

        '''定义随机user_agent列表'''        
        self.user_agent =user_agent
        
        self.ua_list = ["Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
        "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
        "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
        "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
        "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",]

        self.count=0

    def process_request(self,request,spider):

        ua ='Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0'

        request.headers.setdefault('Use-Agent',ua)   #设定reuqest使用的Use-Agent为ua

        request.headers.setdefault('Host','www.huya.com')  #设定reuqest使用的Host为www.huya.com

        request.headers.setdefault('Referer','http://www.huya.com/')   #设定reuqest使用的Referer为http://www.huya.com/

<b>settings</b>
settings配置如下,在“DOWNLOADER_MIDDLEWARES”以及“ITEM_PIPELINES”设置上述items和middlewares中的配置。

DOWNLOADER_MIDDLEWARES = {
    'huyaspider.middlewares.HuyaUserAgentMiddleware': 400,                      #启动middlewares中设定好的usragent
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware':None,   #禁用默认的usragent
}

ITEM_PIPELINES = {
   'huyaspider.pipelines.HuyaspiderPipeline': 300,  #设置pipelines
}

<b>spider</b>
在spider中定义了parse、channel_get、channel_parse、room_parse四个函数,其作用说明如下

parse :获取虎牙下所有频道的url 、频道id  、频道名称
channel_get : def parse的回调函数,根据频道id构造主播数据连接并执行请求
channel_parse :channel_get 的回调函数,根据返回的json数据抓取相应内容,并抓出主播的房间链接,对房间链接执行请求
room_parse :channel_parse的回调函数,抓取主播的订阅数量

代码如下

# -*- coding: utf-8 -*-
import scrapy,re,json,time
from scrapy.http import Request
from huyaspider.items import HuyaspiderItem

class HuyaSpider(scrapy.Spider):
    name = "huya"   

    allowed_domains = ["www.huya.com"]      #设置爬虫允许抓取的
    
    start_urls = ['http://www.huya.com/g']  #设置第一个爬取的url

    allow_pagenum = 5  #设置爬取频道的数量

    total_pagenum = 0  #计算档前已爬取频道的数量

    url_dict={}       #设置存放url的dict

    def parse(self,response):
                
        parse_content= response.xpath('/html/body/div[3]/div/div/div[2]/ul/li')  #抓取当前频道

        for i in parse_content:

            channel_title   = i.xpath('a/p/text()').extract()  #抓取频道名称
 
            channel_url     = i.xpath('a/@href').extract_first()  #抓取当前频道url

            channel_id      = i.xpath('a/@report').re(r'game_id\D*(.*)\D\}')  #抓取当前频道对应的id,用正则去掉不需要部分

            channel_data    = {"url":channel_url,"channel_id":channel_id[0]}   #将频道url和频道id组成一一对应的dict

            self.url_dict[channel_title[0]]=channel_data  #将频道名称和channel_data添加在url_dict中
         
             if self.total_pagenum <= self.allow_pagenum: #用于控制爬出抓取数量,当total_pagenum小于allow_pagenum 继续爬

                self.total_pagenum += 1

                yield Request(url=channel_url,meta={'channel_data':channel_data,'channel':channel_title},callback=self.channel_get)   #使用request,meta携带数据为频道url,频道id,回调函数为channel_get
            

    def channel_get(self, response):
        
        page_num   = int( response.xpath('/html/body/div[3]/div/div/div["js-list-page"]/div[1]/@data-pages').extract_first( ) )  #抓取当前频道一共有多少页,并转为int格式
        
        channel_id = response.meta['channel_data']['channel_id'] #将传入meta的dict(channel_data)中的channel_id值赋给channel_id,该id用于构造url从而实现翻页
        
        channel    = response.meta['channel']  #将传入的meta的dict中的channel_id值赋给channel_id
        

        for i in range(1,page_num+1):  #根据page_num数量构造"下一页"并继续抓取    
            
            url ='http://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId={gameid}&tagAll=0&page={page}'.format(gameid=channel_id,page=i)  #获取下一页的json数据
            

            yield Request(url=url,meta={'page':i,'channel':channel},callback=self.channel_parse)  #meta携带数据为频道当前页码,频道名称,回调函数为channel_parse
      

    def channel_parse(self, response):

         print 'channel_parse start'
         
         count =0 #用于当前房间的位置计算位置
         
         response_json = json.loads(response.text)  #利用json.loads将json数据转为字典
         
         channel =response.meta['channel'] 

         
         for i in response_json['data']['datas']:
             
               count +=1  
            
               items=HuyaspiderItem()   #实例化item.HuyaspiderItem
            
                items['channel']         = channel    #获取频道名称
                
                items['anchor_category'] = i['gameFullName'].replace('/n','')  #获取主播类型,并删内容中的换行符
                  
                items['watch_num']       = i['totalCount']   #获取观看数量
                
                items['anchor_roomname'] = i['roomName']   #获取房间名称
                
                items['anchor_url']      = 'http://www.huya.com/'+i['privateHost'] #获房间url
                
                items['anchor_name']     = i['nick']  #获主播名称

                items['anchor_tag']      = i['recommendTagName']    #获主播推荐标签

                items['position']        = str(response.meta['page'])+"-"+str(count)  #获取所在频道的位置

                yield Request(url=items['anchor_url'],meta={'items':items},callback=self.room_parse)  #进入主播房间url获取主播订阅数量,meta携带数据为刚抓取的items,回调函数为room_parse

    def room_parse(self,response):
        
        print "room_parse start"
        
        items =response.meta['items']

        try:
            items['fan_num'] =response.xpath('/html/body/div[2]/div/div/div[1]/div[1]/div[2]/div/div[1]/div[2]/text()').extract()  #获取主播订阅数量

       except Exception as e:

            items['fan_num'] ='none'  #如果主播订阅数量为空值则数据则为none


        items['crawl_time']  = time.strftime('%Y-%m-%d %X',time.localtime()) #记录爬取时间

        yield items  #输出items
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化...
    Evtion阅读 5,839评论 12 18
  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 12,670评论 4 46
  • 本文希望达到以下目标: 简要介绍Scarpy 阅读官网入门文档并实现文档中的范例 使用Scarpy优豆瓣爬虫的抓取...
    Andrew_liu阅读 81,979评论 30 177
  • 绿海丛中挂串铃, 洁白无瑕内粉菁。 馨香吐艳蜂蝶羡, 醉卧游侠采蜜叮。 ――(游侠:蜜蜂、蝴蝶)
    六月天气阅读 608评论 9 12
  • 两个人都沉默了一会儿,温健锋问:“那你呢,和之前那位?”悦洋知道,这是今晚必聊的话题,她想了想:“就是三观差太多了...
    良仔lz阅读 216评论 0 0