python爬虫---酷安(安卓app的聚集地)

12 篇文章 2 订阅
4 篇文章 0 订阅

酷安

最近app用荒了,想要看看有些哪些好玩的app可以安装下来玩玩,酷安就是安卓应用的聚集地

网页内容

网页内容

模块

  • import requests--网页请求
  • import time--延时
  • import traceback--输出异常
  • from multiprocessing import Pool--多进程
  • from lxml import etree--解析数据
  • from pymysql import connect--数据库连接
  • from pymysql import cursors--数据库游标
  • from PIL import Image--图像读取
  • import wordcloud as wc--词云
  • import numpy as np--图像转换
  • import jieba--分词
  • from pyecharts import Line--折线图
  • from pyecharts import Overlap--图形整合
  • from pyecharts import Bar--柱状图
  • 网页结构分析

来到酷安应用的网页,一如既往的打开chrome的开发者工具,对网页请求进行分析,右键->检查->netword->xhr->刷新,看看数据是不是异步加载的,结果发现并不是,刷新并不能看到什么东西。
chrome
既然不是异步加载的情况,转而研究网页的源代码,看网页源代码里面有没有我们想要的数据,有没有什么字体加密的过程等,右键->查看网页源代码->搜索网易云音乐,就出现结果,而且数据并没有出现乱码,很可能请求该网页,后台直接发过来了,减少了我们很多的麻烦,直接进行网页抓取,然后提取数据,清洗数据即可。
源代码分析

xpath提取

这里主要是利用xpath来提取数据,通过快速定位,然后对数据进行提取,或者目标元素的文本,超链接等,很多时候我们自己写的xpath表达式比较容易出错,提取不了想要的数据,但是我们可以借助浏览器给我们提供的xpath表达式的提取,可以不用自己写了,

右键->检查->选择浏览器里面的小图标(选择元素)->出现相应的网页代码->右键->copy->copy xpath.
xpath提取

数据提取

利用浏览器获取相应的xpath表达式之后,我们就可以进行提取数据、清洗数据和存储数据了,下一步还得进行总页面的提取,方便我们去构造url

网页获取

def get_html(url):
	time.sleep(2)
	#	请求头设置
	headers={
		'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
	}
	try:
		resp=requests.get(url,headers=headers)
		if resp.status_code==200:
			return resp.text
	except Exception:
		print("获取网页失败,",Exception)
		return None

总页数的提取
由于要抓取酷安提供的全部app的信息,所以需要提取app总的页面(需要提取列表的a标签的href即可),方便进行url的构建,以及多进程的实现。
总页数

#	获取最后一页
def get_last_page(content):
	if content:
		html = etree.HTML(content)
		href = html.xpath('/html/body/div/div[2]/div[2]/div[3]/div[2]/ul/li[9]/a/@href')
		page = str(href).split("=")[1].split("'")
		return page[0]
	else:
		return None

app的具体信息

主要提取酷安应用页面的app列表信息和点进每个app里面的分类标签和评分个数
数据

def parse_html(detail):
	if(detail==None):
		print("不可用--detail")
		return None
	html = etree.HTML(detail)
	root = html.xpath('/html/body/div/div[2]/div[2]/div[3]/a')
	for children in root:
		#	app名字
		name = children.xpath('./div/div/div/p[1]/text()')[0]
		grade_size = children.xpath('./div/div/div/p[2]/text()')
		temp = grade_size[0].split(' ')
		#	app评分
		grade = temp[0][:-1]
		#	下载次数
		downlaod_count = children.xpath('./div/div/div/p[2]/span/text()')[0].split(" ")[0].split("次")[0]
		#	数据清洗‘万’转化为数字
		if '万' in  downlaod_count:
			downlaod_count = float(downlaod_count.split('万')[0])*10000

		yield{
			'name':name,
			'grade':grade,
			'download_count':downlaod_count,
			'href':children.xpath('./@href')
		}

获取app的分类标签和评分人数
在分类标签和评分人数的xpath提取的时候,会发现xpath的表达式会出现不一样的,这是有些app详细的页面上面存在“酷安点评”这部分,而有的不存在这部分,而且还有就是,有些app详细页面上不存在“新版特性”这部分,所以造就我们在提取xpath表达式的时候,要分类提取出来。

  • 没有"酷安点评的xpath"
  • 没有"新版特性"的xpath
  • 没有"分类标签"的xpath(不存在的情况直接为空好了)
    xpath
def get_detail(app_detail):
	html = etree.HTML(app_detail)
	comment = html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[4]/div/div[1]/p/text()')
	#	对获取的节点进行判断
	if len(comment) == 0:
		comment= html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[5]/div/div[1]/p/text()')
		if len(comment) == 0:
			comment= html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[3]/div/div[1]/p/text()')
			
	#	对获取的节点进行判断
	tags = html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[5]/p[2]/a/@href')
	if len(tags) == 0:
		tags = html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[6]/p[2]/a/@href')
		if len(tags) == 0:
			tags = html.xpath('/html/body/div/div[2]/div[2]/div[2]/div/div[4]/p[2]/a/@href')

	comment_count = comment[0].split('个')[0][1:]
	if '万' in comment_count:
		comment_count = float(comment_count.split('万')[0])*10000
	
	return comment_count,"".join(tag.split('/')[-1]+"/" for tag in tags)

数据存储

把抓取的数据存储到MySQL数据库中去,再从数据库中提取相关的数据进行分析

数据库和表的创建

def create_database_table():
	data_base = 'app_db'
	base_sql = "create database if not exists "+data_base
	table_sql =	'''
					CREATE TABLE IF NOT EXISTS `app_table` (
   					 	`id` INT(11) NOT NULL AUTO_INCREMENT,
    					`name` VARCHAR(100) COLLATE utf8_bin NOT NULL,
   						`grade` DECIMAL(2,1) NOT NULL,
						`grade_count` VARCHAR(50) NOT NULL,
    					`download_count` VARCHAR(50) NOT NULL,
						`tag` VARCHAR(400) COLLATE utf8_bin NOT NULL,
    					`href` VARCHAR(300) COLLATE utf8_bin NOT NULL,
    					PRIMARY KEY (`id`)
					) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
					AUTO_INCREMENT=1
				'''
	#	连接数据库
	con  = connect(host="localhost",user="root",password="root",charset="utf8mb4")
	try:
		#	创建游标
		cur = con.cursor()
		# 	创建数据库app_db
		cur.execute(base_sql)
		#	选择数据库
		con.select_db(data_base)
		#	创建表
		cur.execute(table_sql)
		#	提交事务
		con.commit()
		print("创建数据库和表成功啦!")
	except Exception:
		print("创建数据库或者表出错啦!",traceback.print_exc())
		#	回滚事务
		con.rollback()
	finally:
		cur.close()
		con.close()

数据的插入

def insert_to_table(msg_dict):
	con = connect(host="localhost",user="root",password="root",db="app_db",charset="utf8mb4")
	#	python默认插入的字符串类型,字段都是字符串类型
	sql = "insert into `app_table` (`name`,`grade`,`grade_count`,`download_count`,`tag`,`href`) values(%s,%s,%s,%s,%s,%s)"
	try:
		#	创建游标
		cur = con.cursor()
		#	执行sql语句
		cur.execute(sql,(msg_dict['name'],msg_dict['grade'],msg_dict['grade_count'],msg_dict['download_count'],msg_dict['tag'],msg_dict['href']))
		#	提交事务
		con.commit()
		print('插入成功,%s'%msg_dict['name'])
	except Exception:
		print("插入数据失败,",traceback.print_exc())
	finally:
		cur.close()
		con.close()

抓取数据的程序入口

def main(offset):
	content=get_html('https://www.coolapk.com/apk?p='+str(offset))
	details = parse_html(content)
	for detail in details:
		#	`name`,`grade`,`grade_count`,`tag`,`download_count`,`href`
		content = get_html('https://www.coolapk.com'+detail['href'][0])
		grade_tage = get_detail(content)
		app_msg = {
					"name":detail['name'],
					"grade":detail['grade'],
					"grade_count":grade_tage[0],
					"download_count":detail['download_count'],
					"tag":grade_tage[1],
					"href":detail['href'][0]
					}
		#	print(app_msg)
		insert_to_table(app_msg)


	print("完成时间,%s"%time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time())))

if __name__ == '__main__':
	create_database_table()
	content = get_html("https://www.coolapk.com/apk/")
	last_page = get_last_page(content)
	# 创建进程池
	pool=Pool()
	offset=([x for x in range(1,int(last_page)+1)])
	# 第一个参数是函数,第二个参数是一个迭代器
	pool.map(main,offset)
	# 关闭进程池
	pool.close()
	pool.join()

数据分析

利用pyecharts库进行数据的绘画,对数据进行分析。

数据查询

从数据库中获取相关的数据

def get_data(sql):
    con = connect(host="localhost",user="root",password="root",db="app_db",charset="utf8mb4")
    try:
        cur = con.cursor()
        cur.execute(sql)
        result = cur.fetchall()
        #   print(result)
        return result
    except Exception:
        print("连接数据库出错",traceback.print_exc())
    finally:
        cur.close()
        con.close()

词云生成

#   词云生成
def draw_tag_wordcloud():
    sql = "select tag from app_table"
    datas = get_data(sql)
    tags = []
    if len(datas) > 0:
        for data in datas:
            if len(data) == 0:
                continue
            else:
                res = data[0].split('/')
                for r in res:
                    tags.append(r)
    else:
        print('没有获取到数据')
        return
    #   设置停用词
    stop_word = ['小编推荐','装机必备']
    android = np.array(Image.open('Android.png'))
    res = wc.WordCloud(
        font_path='‪C:/Windows/Fonts/SIMYOU.TTF',
        background_color='white',
        max_words=2000,
        max_font_size=120,
        mask=android,
        random_state=40,
        stopwords=stop_word)

    #   ---------------------记住拼接字符串需要加空格--------------------
    words = ' '.join(tags)
    words=jieba.cut(words)
    words=' '.join(words)
    res.generate(words)
    res.to_file('app.png')

柱状图和折线的生成
从数据库中查询app下载次数的前50排名,对下载次数前50的app进行评分和下载次数的折线图和柱状图的绘画

def draw_table():
    #   选择下载量前50的app数据
    '''
    sql语句中的(download_count+0)是因为下载次数在数据库中是字符串类型,+0进行类型的转换,这样才有可比性
    '''
    sql = 'SELECT NAME,grade,download_count FROM app_table ORDER BY (download_count+0) DESC LIMIT 0,50'
    datas = get_data(sql)
    name = []
    grade = []
    download = []
    if len(datas) > 0:
        for data in datas:
            name.append(data[0])
            grade.append(float(data[1]))
            download.append(float(data[2]))
    else:
        print('没有结果输出')
        return

    bar = Bar()
    bar.add('app',name,download, yaxis_formatter="次",yaxis_interval=200)
    line = Line()
    line.add('评分',name,grade,yaxis_formatter="分",yaxis_interval=1,xaxis_rotate=30)
    #   使用overlap进行折线图和柱状图的混合
    overlap = Overlap(width=1200, height=600)
    overlap.add(bar)
    
    overlap.add(line,yaxis_index=1, is_add_yaxis=True)
    overlap.render('app_top50.html')

程序入口

def main():
    draw_table()
    draw_tag_wordcloud()

if __name__ == '__main__':
    main()

实现效果

折线图和柱状图

折线图
词云
android
项目源代码:链接:https://pan.baidu.com/s/1t7LNR4HdSDOkTezSaca2ng 提取码:d6ur
总结:项目虽然简单,但其中的坑还有的,涉及多方面的使用,如MySQL、词云、折线图、柱状图等,还是得自己实现,发现以往不常出现的问题。本项目这是用于学习,不存在商业用途,如有侵权必删。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值