可关注微信订阅号 loak 查看实际效果。
代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider ,包括了项目的所有代码。
本文主要介绍以下内容:
跟上一篇博文类似,我们想要爬取某些数据,同样首先要分析网站、APP程序的页面,确定要爬取的元素->分析网站及元素的加载->抓取数据
确定爬取的元素
我们百度搜索王者荣耀,进入其官网,可以发现有一个下拉选项“英雄资料”,地址为:https://pvp.qq.com/web201605/herolist.shtmlweb201605/herolist.shtml , 在这里我们可以看到所有英雄。
点击某个英雄,进入详情页面,我们可以发现官网有对该英雄的简介、技能、加点、出装、铭文的推荐。所以我们可以确定这些内容可以从这里进行爬取。再进行分析“王者营地”APP(博主分析时,版本为:3.44.204),选择 战绩->游戏工具->英雄榜,我们可以看到该APP对实时数据的显示,主要包括了上下中辅野的热度、胜率、登场率、ban率、技能、出装、铭文、克制、被克制的内容。
通过上面的分析,确定了在王者官网爬取英雄的铭文、出装、技能,在王者营地APP爬取 热度、胜率、登场率、ban率、克制、被克制
分析网站及元素的加载
抓取数据
通过以上的分析,我们直接可以编写代码了。为了简单,王者荣耀官网数据的抓取,直接使用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'))).text
tmp2 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[2]/div[1]/div[2]/p[3]/span').text
gok_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').text
gok_hero.zh_skill = zh_skill
mingwen1 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[1]/p[1]/em').text
mingwen2 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[2]/p[1]/em').text
mingwen3 = browser.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div[3]/div[2]/ul/li[3]/p[1]/em').text
gok_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 = 5
proxy = 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).text
return herohtml
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
retry_count -= 1
if 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 None
def 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 = version
gok.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)
为了不报错,请各位还是直接查看仓库的完整代码吧~~
可以查看上一篇博文,里面有完整的操作。https://blog.csdn.net/luoz_java/article/details/92741358
mongodb的可视化,我发现了一款很好的工具,就是 robo3t ,百度搜索即可,免费版的。
官网地址:https://docs.mongodb.com/
pymongo的操作主要为以下内容:
#!/usr/bin/python3
import pymongo
client = pymongo.MongoClient(mongo_config['MONGO_URL'])
db = client[mongo_config['MONGO_DB']]
在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
#!/usr/bin/python3
import pymongo
client = pymongo.MongoClient(mongo_config['MONGO_URL'])
db = client[mongo_config['MONGO_DB']]
collist = db. list_collection_names()
if "sites" in collist: # 判断 sites 集合是否存在
print("集合已存在!")
插入集合
#!/usr/bin/python3
import pymongo
client = 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
#!/usr/bin/python3
import pymongo
client = 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 值。
#!/usr/bin/python3
import pymongo
client = 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]
查询
#!/usr/bin/python3
import pymongo
client = 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})
#!/usr/bin/python3
import pymongo
client = 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/python3
import pymongo
client = 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/python3
import pymongo
client = 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 handlers
class 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')
编辑定时任务:crontab -e
查看定时任务:crontab -l
如果是命令需要先后执行,可以使用 &&
如果是后台运行并且多命令,记得先运行命令在nohup。
例如以下是先杀进行,在启动进程:
#定时重启服务
2 0 * * * ps -ef | grep wxWeb.py | grep -v grep | awk '{print $2}' | xargs kill -9
4 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详解