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

5a8acf7b9c78?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

前言

本文利用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数据库中。

5a8acf7b9c78?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

频道分类页面

5a8acf7b9c78?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

ajax异步请求对应的链接

代码

items

在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() #爬取时间

pipelines

在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中

middlewares

在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/

settings

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

}

spider

在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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《C++ Primer》7.3.4节练习

练习7.32: 要想让clear函数作为Screen的友元&#xff0c;只需要在Screen类中做出友元声明即可。本题的真正关键之处是程序的组织结构&#xff0c;我们必须首先定义Window_mgr类&#xff0c;其中声明clear函数&#xff0c;但是不能定义它&#xff1b;接下来定义Screen类&#xf…

.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记

29 | 定义仓储&#xff1a;使用EF Core实现仓储层首先定义仓储层的接口&#xff0c;以及仓储层实现的基类&#xff0c;抽象类仓储层的接口namespace GeekTime.Infrastructure.Core {/// <summary>/// 包含普通实体的仓储/// 约束 TEntity 必须是继承 Entity 的基类&#…

ueditor单图上传iframe跨域_UEditor单图上传(simpleupload)跨域问题解决方案

代码实现首先我们需要在ueditor.all.js文件中找到原本的单图上传部分的代码搜索关键字 simpleupload&#xff0c;如下图所示&#xff1a;然后找到上传图片的代码片段&#xff0c;如下图所示&#xff1a;然后把 domUtils.on的 input 绑定的事件注释掉或删除掉替换成以下代码:inp…

StringBuilder内存碎片对性能的影响

TL;DR:StringBuilder内部是由多段 char[]组成的半自动链表&#xff0c;因此频繁从中间修改 StringBuilder&#xff0c;会将原本连续的内存分隔为多段&#xff0c;从而影响读取/遍历性能。连续内存与不连续内存的性能差&#xff0c;可能高达 1600倍。背景用 StringBuilder的用户…

java 双击_利用java开发一个双击执行的小程序

之前我们利用java写了很多东西&#xff0c;但是好像都没有什么实际意义。因为有意义桌面小程序怎么都得有个界面&#xff0c;可是界面又不太好搞。或者 了解到这一层的人就少之又少了。呀&#xff0c;是不是还得开辟一些版面来介绍awt和 swing。。。算了 先把这个 双击执行的小…

开发人员如何学习 Kubernetes

虽然“容器编排平台”还没有被整个行业大范围采用&#xff0c;但在这一领域 Kubernetes 已经战胜其他选手&#xff0c;成为了事实标准。近两年的 Web 开发技术社区&#xff0c;随便打开一两个群&#xff0c;你都能看到人们在谈 Kubernetes。很多开发人员&#xff0c;包括曾经的…

安装 java decompiler_Eclipse离线安装Java Decompiler插件(反编译)

Java Decompiler是Java语言的反编译工具&#xff0c;具体介绍见博客Java Decompiler(Java反编译工具)1、下载插件Eclipe的Java Decompiler插件名为JD-Eclipse&#xff0c;2、安装插件Ecipse安装JD-Eclipse(即Java Decompiler)插件步骤如下&#xff1a;打开Help --> Install …

给 ABP vNext 应用安装私信模块

在上一节五分钟完成 ABP vNext 通讯录 App 开发 中&#xff0c;我们用完成了通讯录 App 的基础开发。这本章节&#xff0c;我们会给通讯录 App 安装私信模块&#xff0c;使不同用户能够通过相互发送消息&#xff0c;并接收新私信的通知。在章节的最后&#xff0c;笔者将演示模块…

《C++ Primer》7.5.2节练习

练习7.41: #include <iostream> #include <string> using namespace std;class Sales_data {friend std::istream &read(std::istream &is, Sales_data &item);friend std::ostream &print(std::ostream &os, const Sales_data &item);pu…

零基础玩视频号?创作运营变现,你要的干货都在这了!

点击蓝字“大白技术控”关注我哟加个“星标★”&#xff0c;每日良时&#xff0c;好文必达&#xff01;不少小伙伴应该已经听说过视频号这个新功能了&#xff0c;视频号是微信内测的短视频功能&#xff0c;本人已经在视频号里刷了2个月了。3月中旬正式开通了视频号 「大白技术控…

Asp.Net Core 中IdentityServer4 实战之 Claim详解

一、前言由于疫情原因&#xff0c;让我开始了以博客的方式来学习和分享技术&#xff08;持续分享的过程也是自己学习成长的过程&#xff09;&#xff0c;同时也让更多的初学者学习到相关知识&#xff0c;如果我的文章中有分析不到位的地方&#xff0c;还请大家多多指教&#xf…

程序员还有35岁的坎吗?

昨天晚上和多年未见的前同事聊天&#xff0c;提到了程序员的年龄歧视问题&#xff1a;自己年龄也 30 出头了&#xff0c;在思考 IT 届流传的 35 岁是一个坎的问题&#xff1b;开始注重提升管理能力&#xff0c;担心35岁之后&#xff0c;一线写代码的岗位不能胜任&#xff1b;公…

java 左移 返回值_java左移右移运算符详解

在阅读源码的过程中&#xff0c;经常会看到这些符号<< &#xff0c;>>&#xff0c;>>>&#xff0c;这些符号在Java中叫移位运算符&#xff0c;在写代码的过程中&#xff0c;虽然我们基本上不会去写这些符号&#xff0c;但需要明白这些符号的运算原理&…

人与人的差距在于认知

作者介绍findyi&#xff0c;腾讯、360码农&#xff0c;前哒哒少儿英语技术VP&#xff0c;现任土豆教育CTO。工作和生活中不光要埋头干活&#xff0c;还要抬头看天。思考总结方法论是提升认知的必备途径&#xff0c;是将碎片化知识总结为动态的智慧的过程。认知有多重要&#xf…

.NET5来了你别慌

近日微软.Net大咖Scott在博客中对外宣传.NET5首个预览版&#xff0c;并且我们可以通过微软的官网下载SDK5和运行库。很多朋友感觉.NetCore3.1还没搞明白&#xff0c;.NET5就来了感觉一下子慌了神。在这里我提醒朋友们&#xff0c;瞬息万变的世界中&#xff0c;总有相对不变的真…

java8 stream 最大值_JDK8-Stream流常用方法

Stream流的使用流操作是Java8提供一个重要新特性&#xff0c;它允许开发人员以声明性方式处理集合&#xff0c;其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中&#xff0c;能 让代码更加简…

周三晚6点半!盛派首席架构师“苏老师”在线解密内部系统框架!

工作中有些事&#xff0c;看起来只用一会会儿就能完成&#xff0c;但真正完成起来&#xff0c;总会遇到一些意想不到的困难&#xff01;你一定碰到过这样的情况——开发时间 2 周的项目&#xff0c;搭框架就要用 1 周&#xff0c;刚开发完&#xff0c;各种调试和修 bug又花去 2…

给微软的日志框架写一个基于委托的日志提供者

动手造轮子&#xff1a;给微软的日志框架写一个基于委托的日志提供者Intro微软的日志框架现在已经比较通用&#xff0c;有时候我们不想使用外部的日志提供者&#xff0c;但又希望提供一个比较简单的委托就可以实现日志记录&#xff0c;于是就有了后面的探索和实现。Solution基于…

C++分析使用拷贝控制成员和调用构造函数的时机

我们来分析下面这段代码&#xff1a; #include <iostream> #include <vector>using namespace std;struct X {X() {cout << "构造函数X()" << endl;}X(const X &) {cout << "拷贝构造函数X(const X&)" << en…

《C++ Primer》13.1.4节练习

练习13.14: 这是一个典型的应该定义拷贝控制成员的场合。如果不定义拷贝构造函数和拷贝赋值运算符&#xff0c;依赖合成的版本&#xff0c;则在拷贝构造和赋值时&#xff0c;会简单复制数据成员。对本问题来说&#xff0c;就是将序号简单复制给新对象。 因此&#xff0c;代码中…