概述

可关注微信订阅号 loak 查看实际效果。

代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider ,包括了项目的所有代码。

本文主要介绍以下内容:

  • 使用selenium 和 requests爬取王者荣耀官网、王者营地APP数据,使用BeautifulSoup和正则进行数据解析;
  • 爬取的IP代理使用了redis搭建的代理池;
  • 数据通过pymongo保存到Mongodb中,并且通过提供接口进行数据的查询与显示;
  • 项目运行日志记录功能logging;
  • centos服务器的定时任务;
  • 数据的查询与显示可通过订阅号进行方便的查询。

爬虫实现

跟上一篇博文类似,我们想要爬取某些数据,同样首先要分析网站、APP程序的页面,确定要爬取的元素->分析网站及元素的加载->抓取数据

  • 确定爬取的元素
    我们百度搜索王者荣耀,进入其官网,可以发现有一个下拉选项“英雄资料”,地址为:https://pvp.qq.com/web201605/herolist.shtmlweb201605/herolist.shtml , 在这里我们可以看到所有英雄。
    点击某个英雄,进入详情页面,我们可以发现官网有对该英雄的简介、技能、加点、出装、铭文的推荐。所以我们可以确定这些内容可以从这里进行爬取。再进行分析“王者营地”APP(博主分析时,版本为:3.44.204),选择 战绩->游戏工具->英雄榜,我们可以看到该APP对实时数据的显示,主要包括了上下中辅野的热度、胜率、登场率、ban率、技能、出装、铭文、克制、被克制的内容。
    通过上面的分析,确定了在王者官网爬取英雄的铭文、出装、技能,在王者营地APP爬取 热度、胜率、登场率、ban率、克制、被克制

  • 分析网站及元素的加载

    • 王者荣耀官网的数据
      王者荣耀官网的英雄列表:https://pvp.qq.com/web201605/herolist.shtml , 可以直接通过requests进行获取。
      至于英雄的详细页面,例如 曜 :https://pvp.qq.com/web201605/herodetail/522.shtml , 分析页面我们可以发现,其实页面的大多数信息都是可以直接使用requsts进行请求获取,但是通过分析发现,出装的推荐,是通过ajax进行动态加载的,为了简单,直接采用了selenium进行操作了
    • 王者营地
      王者营地app的数据抓取,我们首先要安装fiddler,通过桌面的fiddler的证书,安装到手机与电脑同一wifi的证书中,并进行代理的设置,及可以通过fiddler分析请求。具体操作可以看我之前写过的博客:https://blog.csdn.net/luoz_java/article/details/90340115 , 里面介绍有详细的操作步骤。
      设置完成后,打开APP,进入 战绩->游戏工具->英雄榜 ,分析fiddler显示的请求:
      https://ssl.kohsocialapp.qq.com:10001/hero/getdetailranklistbyid , 返回的json数据包含了 热度、胜率、登场率、ban率;
      https://ssl.kohsocialapp.qq.com:10001/hero/getheroextrainfo , 返回的json数据包含了克制、被克制等信息。
  • 抓取数据
    通过以上的分析,我们直接可以编写代码了。为了简单,王者荣耀官网数据的抓取,直接使用chrome selenium,而王者营地的则采用request模块。
    官网爬取某一英雄详细信息:

    def get_one_hero_detail(hero_url, gok_hero):proxy = get_proxy()chrome_options.add_argument('--proxy-server=http://' + proxy)browser.get(hero_url)tmp1 = wait.until(EC.presence_of_element_located((By.XPATH, '/html/body/div[3]/div[2]/div/div[2]/div[1]/div[2]/p[1]/span'))).texttmp2 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[2]/div[1]/div[2]/p[3]/span').textgok_hero.skill = ['主:' + tmp1, '副:' + tmp2]zh_skill = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[2]/div[1]/div[2]/p[5]/span').textgok_hero.zh_skill = zh_skillmingwen1 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[1]/p[1]/em').textmingwen2 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[2]/p[1]/em').textmingwen3 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[3]/p[1]/em').textgok_hero.mingwen = [mingwen1, mingwen2, mingwen3]browser.implicitly_wait(5)builds = browser.find_elements_by_xpath('//*[@id="Jname"]')list_tmp = []for item in builds:list_tmp.append(item.get_attribute("innerHTML"))gok_hero.first_build = list_tmp[:6]gok_hero.second_build = list_tmp[6:12]time.sleep(1)return gok_hero
    

    王者营地APP根据位置爬取数据及解析的代码:

    all_hero_msg = []def get_hero_rank(lu):retry_count = 5proxy = get_proxy()while retry_count > 0:try:# 获取英雄列表proxies = {"http": "http://" + proxy}data_tmp = gok_interface_log['post_data_20190623']data_tmp.update({'position': lu})herohtml = requests.post(url=gok_interface_log['post_url_20190623'],data=data_tmp,proxies=proxies).textreturn herohtmlexcept urllib.error.URLError as e:if isinstance(e.reason, socket.timeout):retry_count -= 1if retry_count == 2:# 出错3次, 删除代理池中代理delete_proxy(proxy)proxy = get_proxy()except Exception as e:log.logger.error('get_hero_rank爬取失败!' + str(e)+lu)if retry_count == 3:delete_proxy(proxy)proxy = get_proxy()return Nonedef parse_hero_rank(rank_data, version, position):rank_data = ast.literal_eval(rank_data)hero_rank_list = str(rank_data.get('data').get('list'))[1:-1]hero_items = ast.literal_eval(hero_rank_list)for item in hero_items:gok = GokClass()gok.version = versiongok.day = gok_config['GOK_INSERT_TIME']gok.heroid = item['heroId']gok.heroname = item['heroInfo'][0]['heroName']gok.herotype = position  # 英雄走哪路gok.herotypename = item['heroInfo'][0]['heroCareer']gok.tRank = item['tRank']gok.winpercent = item['winRate']gok.gameactpercnt = item['showRate']gok.banRate = item['banRate']all_hero_msg.append(gok)
    

    为了不报错,请各位还是直接查看仓库的完整代码吧~~

redis维护IP代理池

可以查看上一篇博文,里面有完整的操作。https://blog.csdn.net/luoz_java/article/details/92741358

pymongo

mongodb的可视化,我发现了一款很好的工具,就是 robo3t ,百度搜索即可,免费版的。
官网地址:https://docs.mongodb.com/
pymongo的操作主要为以下内容:

  • 创建集合
 #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])db = client[mongo_config['MONGO_DB']]

在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。

  • 判断集合是否已存在
 #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])db = client[mongo_config['MONGO_DB']]collist = db. list_collection_names()if "sites" in collist:   # 判断 sites 集合是否存在print("集合已存在!")
  • 插入集合

    • insert_one()
    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    search_set = db[mongo_config['USER_FIND_TYPE']]
    tmp = search_set.insert_one({'user_id': user_id, 'game_type': type})
    # insert_one() 方法返回 InsertOneResult 对象,该对象包含 inserted_id 属性,它是插入文档的 id 值。
    print(tmp.inserted_id) # 5b2369cac315325f3698a1cf
    if tmp:return 'success'
    return None
    
    • insert_many()
    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]search_set = db[mongo_config['USER_FIND_TYPE']]mylist = [{ "name": "Taobao", "alexa": "100", "url": "https://www.taobao.com" },{ "name": "QQ", "alexa": "101", "url": "https://www.qq.com" },{ "name": "Facebook", "alexa": "10", "url": "https://www.facebook.com" },{ "name": "知乎", "alexa": "103", "url": "https://www.zhihu.com" },{ "name": "Github", "alexa": "109", "url": "https://www.github.com" }]x = search_set.insert_many(mylist)# 输出插入的所有文档对应的 _id 值
    print(x.inserted_ids)
    # [ObjectId('5b236aa9c315325f5236bbb6'), ObjectId('5b236aa9c315325f5236bbb7'), ObjectId('5b236aa9c315325f5236bbb8'), ObjectId('5b236aa9c315325f5236bbb9'), ObjectId('5b236aa9c315325f5236bbba')]
    # insert_many() 方法返回 InsertManyResult 对象,该对象包含 inserted_ids 属性,该属性保存着所有插入文档的 id 值。
    
    • 插入指定 _id 的多个文档
    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    search_set = db[mongo_config['USER_FIND_TYPE']]mylist = [{ "_id": 1, "name": "RUNOOB", "cn_name": "菜鸟教程"},{ "_id": 2, "name": "Google", "address": "Google 搜索"},{ "_id": 3, "name": "Facebook", "address": "脸书"},{ "_id": 4, "name": "Taobao", "address": "淘宝"},{ "_id": 5, "name": "Zhihu", "address": "知乎"}]x = mycol.insert_many(mylist)# 输出插入的所有文档对应的 _id 值
    print(x.inserted_ids)
    # [1, 2, 3, 4, 5]
    
  • 查询

    • find_one()
    • find()
    • 查询指定字段的数据
    • 根据指定条件查询
    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    mycol = mydb[mongo_config['USER_FIND_TYPE']]x = mycol.find_one()list_col = mycol.find()# 将要返回的字段对应值设置为 1
    # 除了 _id 你不能在一个对象中同时指定 0 和 1,如果你设置了一个字段为 0,则其他都为 1,反之亦然。
    for x in mycol.find({},{ "_id": 0, "name": 1, "alexa": 1 }):print(x)# 同时指定了 0 和 1 则会报错
    for x in mycol.find({},{ "name": 1, "alexa": 0 }):print(x)# 根据条件查找
    mydoc = mycol.find_one({'user_id': user_id, 'game_type': type})
    
    • 高级查询
      与或非,与不用说了。下面说或、非
      limit()
      排序
    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    mycol = mydb[mongo_config['USER_FIND_TYPE']]# 或
    x = mycol.find_one({"$or": [{"name": hero_another_name}, {"another1": hero_another_name}]})# 非
    # 读取 name 字段中第一个字母为 "R" 的数据,正则表达式修饰符条件为 {"$regex": "^R"} :
    x = mycol.find({ "name": { "$regex": "^R" } })# $in,常用于判断列表name是否存在luozheng元素
    x = mycol.find_one({"name": {'$in':['luozheng']}})# $regex,也可以写正则
    # 适用于匹配,如果字段a的值为'abc',如果我们想知道name的值是否包含‘b’,可以这样做find({'name':{'$regex':'b'}})
    x = mycol.find_one({"name": {'$regex':['luo']}})# limit()
    myresult = mycol.find().limit(3)# 同时指定了 0 和 1 则会报错
    for x in mycol.find({},{ "name": 1, "alexa": 0 }):print(x)# sort  升序:pymongo.ASCENDING ( 1 ) 、 降序:pymongo.DESCENDING ( -1 )
    mydoc = mycol.sort([("field1",pymongo.ASCENDING), ("field2",pymongo.DESCENDING)])
    
  • 更新
    update_one()
    update_many()

    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    mycol = mydb[mongo_config['USER_FIND_TYPE']]# update_one 该方法第一个参数为查询的条件,第二个参数为要修改的字段。
    mycol.update_one({ "alexa": "10000" }, { "$set": { "alexa": "12345" } })# 实例将查找所有以 F 开头的 name 字段,并将匹配到所有记录的 alexa 字段修改为 123:
    myquery = { "name": { "$regex": "^F" } }
    newvalues = { "$set": { "alexa": "123" } }x = mycol.update_many(myquery, newvalues)
    
  • 删除
    delete_one()
    delete_many()
    drop()

    #!/usr/bin/python3import pymongoclient = pymongo.MongoClient(mongo_config['MONGO_URL'])
    db = client[mongo_config['MONGO_DB']]
    mycol = mydb[mongo_config['USER_FIND_TYPE']]# delete_one
    mycol.delete_one({ "name": "Taobao" })# delete_many
    myquery = { "name": {"$regex": "^F"} }x = mycol.delete_many(myquery)# delete_many() 方法如果传入的是一个空的查询对象,则会删除集合中的所有文档
    x = mycol.delete_many({})# 删除集合
    # 如果删除成功 drop() 返回 true,如果删除失败(集合不存在)则返回 false。
    mycol.drop()
    

日志记录功能

参照博文: https://www.cnblogs.com/nancyzhu/p/8551506.html

  • 介绍
    logging提供了一组便利的函数,用来做简单的日志。它们是 debug()、 info()、 warning()、 error() 和 critical()。

    默认等级是WARNING,这意味着仅仅这个等级及以上的才会反馈信息,除非logging模块被用来做其它事情。
    logging函数根据它们用来跟踪的事件的级别或严重程度来命名。标准级别及其适用性描述如下(以严重程度递增排序):

级别 何时使用
DEBUG 详细信息,一般只在调试问题时使用。
INFO 证明事情按预期工作。
WARNING 某些没有预料到的事件的提示,或者在将来可能会出现的问题提示。例如:磁盘空间不足。但是软件还是会照常运行。
ERROR 由于更严重的问题,软件已不能执行一些功能了。
CRITICAL 严重错误,表明软件已不能继续运行了。
  • 代码实例
import logging
from logging import handlersclass Logger(object):level_relations = {'debug':logging.DEBUG,'info':logging.INFO,'warning':logging.WARNING,'error':logging.ERROR,'crit':logging.CRITICAL}#日志级别关系映射def __init__(self,filename,level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):self.logger = logging.getLogger(filename)format_str = logging.Formatter(fmt)#设置日志格式self.logger.setLevel(self.level_relations.get(level))#设置日志级别sh = logging.StreamHandler()#往屏幕上输出sh.setFormatter(format_str) #设置屏幕上显示的格式th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')#往文件里写入#指定间隔时间自动生成文件的处理器#实例化TimedRotatingFileHandler#interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种:# S 秒# M 分# H 小时、# D 天、# W 每星期(interval==0时代表星期一)# midnight 每天凌晨th.setFormatter(format_str)#设置文件里写入的格式self.logger.addHandler(sh) #把对象加到logger里self.logger.addHandler(th)
if __name__ == '__main__':log = Logger('all.log',level='debug')log.logger.debug('debug')log.logger.info('info')log.logger.warning('警告')log.logger.error('报错')log.logger.critical('严重')Logger('error.log', level='error').logger.error('error')

centos服务器的定时任务

编辑定时任务:crontab -e
查看定时任务:crontab -l
如果是命令需要先后执行,可以使用 &&
如果是后台运行并且多命令,记得先运行命令在nohup。
例如以下是先杀进行,在启动进程:

  #定时重启服务2 0 * * * ps -ef | grep wxWeb.py | grep -v grep | awk '{print $2}' | xargs kill -94 0 * * * cd /home/LOLGokSpider/Web && nohup /home/LOLGokEnv/bin/python /home/LOLGokSpider/Web/wxWeb.py > /home/LOLGokSpider/Web/wxRun.log 2>&1 &

个人博客:Loak 正 - 关注人工智能及互联网的个人博客
文章地址:爬虫实战(二)—利用requests、selenium爬取王者官网、王者营地APP数据及pymongo详解

爬虫实战(二)—利用requests、selenium爬取王者官网、王者营地APP数据及pymongo详解相关推荐

  1. requests+selenium 爬取企查查网,1000+条数据轻轻松松

    对于这个我们就很简答了, 我们需要获取公司的具体链接 我们根据如下链接去爬取公司的具体链接: https://blog.csdn.net/qq_44936246/article/details/120 ...

  2. python使用selenium爬取联想官网驱动(一):获取遍历各驱动的下载网址

    python使用selenium爬取联想官网驱动(一):获取遍历各驱动的下载网址然后wget命令试验下载 由于初期学习,所以先拿一个型号的产品驱动试验. (1)以下为在联想某型号产品获取相关驱动下载的 ...

  3. Python爬虫实战01:Requests+正则表达式爬取猫眼电影

    1 目标站点的分析 2 流程框架 抓取单页内容 利用requests请求目标站点,得到单个网页HTML代码,返回结果. 正则表达式分析 根据HTML代码分析得到电影的名称.主演.上映.时间.评分.图片 ...

  4. python 爬虫实战六:用 selenium 爬取豆瓣电影

    今天帮朋友爬取豆瓣电影的数据,以便进行社交网络分析. 首先打开豆瓣电影,然后点击 分类 ,选择要爬取的特定电影 这里以国产喜剧片为例:依次点击 电影.喜剧.中国大陆 然后点击一个小的列表按键 找到我们 ...

  5. Python网络爬虫实战:世纪佳缘爬取近6万条小姐姐数据后发现惊天秘密

    翻着安静到死寂的聊天列表,我忽然惊醒,不行,我们不能这样下去,光羡慕别人有什么用,我们要行动起来,去找自己的幸福!!! 我也想"谈不分手的恋爱" !!!内牛满面!!! 注册登陆一气 ...

  6. denied 登陆后access_Python网络爬虫实战:世纪佳缘爬取近6万条小姐姐数据后发现惊天秘密...

    翻着安静到死寂的聊天列表,我忽然惊醒,不行,我们不能这样下去,光羡慕别人有什么用,我们要行动起来,去找自己的幸福!!! 我也想"谈不分手的恋爱" !!!内牛满面!!! 注册登陆一气 ...

  7. Python爬虫实战:世纪佳缘爬取近6万条小姐姐数据!脱单的有望!

    马上又是一年双十一了,不知道从什么时候开始,双十一从"光棍节"变成了"双十一购物狂欢节",最后一个属于单身狗的节日也成功被攻陷,成为了情侣们送礼物秀恩爱的节日. ...

  8. Python爬虫实战之利用Scrapy框架爬取传智播客课程数据

    1.文件结构: 2.lesson.py代码 import scrapy from ts.items import TsItem from scrapy.http import Requestclass ...

  9. 爬虫实战2(下):爬取豆瓣影评

       上篇笔记我详细讲诉了如何模拟登陆豆瓣,这次我们将记录模拟登陆+爬取影评(复仇者联盟4)实战.本文行文结构如下: 模拟登陆豆瓣展示 分析网址和源码爬取数据 进行面对对象重构 总结   一.模拟登陆 ...

最新文章

  1. 高级数据结构讲解与案例分析
  2. WebService客户端添加SOAPHeader信息
  3. 简述python程序结构_python架构的概念讲解
  4. 百度地图android自定义文字,androidsdk | 百度地图API SDK
  5. scp传输文件,自动填充密码
  6. 统计学习方法 第一章 学习心得
  7. 机器学习-算法背后的理论与优化(part2)--广义线性模型
  8. 《C++ Primer》14.2.1节练习
  9. 浙江省计算机二级aoa成绩,浙江省计算机二级AOAEXCEL函数列表
  10. 案例解释图像傅里叶变换的幅度谱和相位谱的以及反变换
  11. new relic_新的Relic的Centurion Docker部署工具,DIY大脑扫描仪等
  12. 开源项目到底归谁管?
  13. c语言 机器语言 汇编语言_多语言机器人新闻记者
  14. 就业swot分析计算机,程序员应聘SWOT分析法
  15. 实践一:mipsel-栈溢出漏洞_开启telnet服务_反弹shell
  16. Java 从键盘输入数据到一维double数组中
  17. 工学硕士翻译计算机,赫瑞瓦特大学中英翻译与计算机辅助翻译工具理科硕士入学条件及实习就业...
  18. Web-网上在线支付
  19. Qt 仿360悬浮球
  20. 以太零MPOS共识机制介绍

热门文章

  1. 一阶电路中的时间常数_一阶电路的三要素法公式及例题
  2. JS作用域 , 作用域链
  3. amd c6 support_求助C3/C6/C7 State Support 含义
  4. rtthread posix接口支持
  5. 协同-管理软件界的最后一片沃土
  6. 浅析云服务器常见的维护技巧
  7. 《基于网络软件开发综合实习》实习报告
  8. 小程序对七牛云文件上传删除批量删除生成token封装无需服务器一个小程序搞定
  9. 2021中山大学光华口腔医学院、一战上岸考研经验
  10. 1521端口 mysql_Linux开放1521端口允许网络连接Oracle Listener