curl 抓取网页(抓取googleplay全球12国的游戏TOP排名要怎么做)
优采云 发布时间: 2021-11-03 14:04curl 抓取网页(抓取googleplay全球12国的游戏TOP排名要怎么做)
上一篇博客讲了Linux如何抓取网页。有两种方法:curl 和 wget。本文将以Linux抓取网页为例——抓取google play在全球12个国家的顶级游戏排名。
爬取google play游戏排名网页,首先需要分析网页的特点和规律:
1、Google play游戏排名页面是一种“总分”形式,即一个页面URL显示了几个排名(比如24),这样的页面有几个构成了所有游戏的总排名
2、 在每个页面URL中,点击每个单独的游戏链接可以查看该游戏的属性信息(如评分星级、发布日期、版本号、SDK版本号、游戏类别、下载量等)
需要解决的问题:
1、如何抓取所有游戏的总排名?
2、 抓取整体排名后,如何拼接网址抓取各个单独的游戏页面?
3、获取各个单独的游戏网页后,如何提取网页中游戏的属性信息(即评分星级、发布日期...)?
4、提取出各个游戏的属性信息后,如何保存(mysql)、生成日报(html)、发送日报(email)?
5、根据抓取到的游戏属性信息资源,如何查询贵公司的游戏排名(JSP)以及如何清晰的展示游戏排名(JFreeChart图表)?
6、 更难的是google play游戏排名没有全球统一的排名。谷歌采用本地化策略。数十个国家/地区都有自己的一套排名算法和规则。如何在12个国家实现游戏排名?
设计方案与技术选择
分析了上述问题和需求后,如何一一解决并分解,这是我们需要思考、设计和解决的问题(模块流程和技术实现)?
基于以上分析提出的问题,将一一进行模块设计和技术方案选择如下:
1、为了抓取12个国家的游戏排名,需要分别租用12个国家的代理服务器来抓取各个国家的游戏排名(12个国家的游戏排名算法和语言是不同,包括中文、英文、日文、俄文、*敏*感*词*文...)
2、 抓取网页,使用curl+proxy代理;提取下载的网页信息,使用awk文本分析工具(需要对html语法标签、id等元素有很好的理解,才能准确使用awk提取游戏属性信息)
3、由于IP代理筛选系统、网页抓取程序、游戏属性信息提取等模块都是用脚本完成的,为了保持编程语言的一致性,数据库创建和记录插入也用脚本来实现外壳脚本。
4、 抓取的每款游戏的属性信息,采用html+table的网页形式展示,清晰直观。一个shell脚本用于拼接html字符串(table + tr + td + info)
5、 生成的html网页以每日邮件的形式定期发送给产品总监、PM、RD、QA,了解公司发布的游戏排名以及全球上升最快、最受欢迎的游戏趋势
6、 开发JSP网页查询系统,根据输入的游戏名称或游戏包名称查询游戏的排名和趋势,并在趋势图下显示该游戏的所有详细属性信息
模块技术实现
1、IP代理过滤
成本考虑,每个国家租用代理服务器(VPN),以市场最低价1000元/月计算,一年12000元,12个国家的总费用为12x12000=144000,也就是大约140000/年的VPN租用费
基于成本考虑,我后来通过对代理服务器和免费ip的深入研究,提出设计开发一套免费ip代理服务器筛选系统,抓取12个国家的游戏排名。
免费代理IP主要来自上一篇博客介绍的两个网站:和
IP代理筛选系统,由于文本预处理和筛选逻辑实现的复杂性,将在下一篇博文中单独介绍
2、 抓取排名页面
仔细分析google play游戏排名页面,可以发现有一定的规律可循:
第一页的Top24 URL:
第二页的Top48 URL:
第三页的Top72网址:
. . .
此时,观察每个页面URL的最后一串?start=24&num=24,你已经找到了模式^_^ 其实网页的第一页是从start=0开始的,也可以写成:
第一页的Top24 URL:
根据上面的规则,可以使用curl+proxy通过循环拼接字符串(start ='expr $start + 24')来爬取排名网页
3、提取游戏链接
排名页面,每个页面收录24个游戏网址超链接,如何提取这24个游戏网址超链接?
当时就考虑用xml解析,因为html是一种分层组织的类似xml的格式,但是有些网页并不是所有标准的html格式(比如左括号后没有右括号闭包),这样会导致xml不被正确解析
后来结合自己学到的html和js知识,分析了爬行排名网页的内容结构,发现每个游戏链接前面都有一个唯一的class="title"。具体格式如下(以篮球投篮为例):
Basketball Shoot
这样就可以通过awk顺利提取出class="title"附近的文本内容,具体实现如下:
# split url_24
page_key='class="title"'
page_output='output_page.log'
page_output_url_start='https://play.google.com/store/apps/'
page_output_url='output_top800_url.log'
function page_split(){
grep $page_key $(ls $url_output* | sort -t "_" -k6 -n) > tmp_page_grepURL.log # use $url_output
awk -F'[]' '{for(i=1;i> $log
echo "================= $date ================" >> $log
# mysql database and table to create
HOST='localhost'
PORT='3306'
USER='root'
PWD='xxxxxx'
DBNAME='top800'
TABLENAME='gametop800'
mysql_login=''
mysql_create_db=''
mysql_create_table=''
function mysql_create(){
echo "login mysql $HOST:$PORT ..." >> $log
mysql_login="mysql -h $HOST -P $PORT -u $USER -p$PWD" # mysql -h host -P port -u root -p pwd
echo | ${mysql_login}
if [ $? -ne 0 ]; then
echo "login mysql ${HOST}:${PORT} failed.." >> $log
exit 1
else
echo "login mysql ${HOST}:${PORT} success!" >> $log
fi
echo "create database $DBNAME ..." >> $log
mysql_create_db="create database if not exists $DBNAME"
echo ${mysql_create_db} | ${mysql_login}
if [ $? -ne 0 ]; then
echo "create db ${DBNAME} failed.." >> $log
else
echo "create db ${DBNAME} success!" >> $log
fi
echo "create table $TABLENAME ..." >> $log
mysql_create_table="create table $TABLENAME(
id char(50) not null,
url char(255),
top int,
name char(100),
category char(50),
rating char(10),
ratingcount char(20),
download char(30),
price char(20),
publishdate char(20),
version char(40),
filesize char(40),
requireandroid char(40),
contentrating char(40),
country char(10) not null,
dtime date not null default \"2011-01-01\",
primary key(id, country, dtime)
)"
echo ${mysql_create_table} | ${mysql_login} ${DBNAME}
if [ $? -ne 0 ]; then
echo "create table ${TABLENAME} fail..." >> $log
else
echo "create table ${TABLENAME} success!" >> $log
fi
}
脚本功能说明:
首先登录mysql数据库,判断mysql服务器、端口号、用户名和密码是否正确。如果不正确,则登录失败退出(exit 1);如果正确,则登录成功,继续下一步
然后,创建数据库名,判断数据库是否存在,如果不存在,则创建;如果存在,继续下一步(注意:创建数据库时,需要验证是否登录数据库成功,否则无法操作)
最后创建数据库表,首先设计数据库表的各个字段,然后创建数据库表,具体判断方法与创建数据库名称相同
# Author : yanggang
# Datetime : 2011.10.24 21:45:09
# ============================================================
#!/bin/sh
# insert mysql
file_input='output_top800_url_page'
file_output='sql_output'
HOST='localhost'
PORT='3306'
USER='root'
PWD='xxxxxx'
DBNAME='top800'
TABLENAME='gametop800'
col_id=''
col_url=''
col_top=1
col_name=''
col_category=''
col_rating=''
col_ratingcount=''
col_download=''
col_price=''
col_publishdate=''
col_version=''
col_filesize=''
col_requireandroid=''
col_contentrating=''
col_country=''
col_dtime=''
sql_insert='insert into gametop800 values("com.mobile.games", "url", 3, "minesweeping", "games", "4.8", "89789", "1000000-5000000000", "free", "2011-2-30", "1.2.1", "1.5M", "1.5 up", "middle", "china", "2011-10-10")'
function mysql_insert(){
rm -rf $file_output
touch $file_output
DBNAME=$1
col_dtime=$2
col_country=$3
echo 'col_dtime========='$col_dtime
while read line
do
col_id=$(echo $line | cut -f 1 -d "%" | cut -f 1 -d "&" | cut -f 2 -d "=")
col_url=$(echo $line | cut -f 1 -d "%")
col_name=$(echo $line | cut -f 2 -d "%")
col_category=$(echo $line | cut -f 3 -d "%")
col_rating=$(echo $line | cut -f 4 -d "%")
col_ratingcount=$(echo $line | cut -f 5 -d "%")
col_download=$(echo $line | cut -f 6 -d "%")
col_price=$(echo $line | cut -f 7 -d "%")
col_publishdate=$(echo $line | cut -f 8 -d "%")
col_version=$(echo $line | cut -f 9 -d "%")
col_filesize=$(echo $line | cut -f 10 -d "%")
col_requireandroid=$(echo $line | cut -f 11 -d "%")
col_contentrating=$(echo $line | cut -f 12 -d "%")
sql_insert='insert into '$TABLENAME' values('"\"$col_id\", \"$col_url\", $col_top, \"$col_name\", \"$col_category\", \"$col_rating\", \"$col_ratingcount\", \"$col_download\", \"$col_price\", \"$col_publishdate\", \"$col_version\", \"$col_filesize\", \"$col_requireandroid\", \"$col_contentrating\", \"$col_country\", \"$col_dtime\""');'
echo $sql_insert >> $file_output
mysql -h $HOST -P $PORT -u $USER -p$PWD -e "use $DBNAME; $sql_insert"
col_top=`expr $col_top + 1`
done < $file_input
}
脚本功能说明:
插入数据库脚本比较简单,主要实现两个功能:游戏排名号(col_top)和数据库语句插入($sql_insert)
通过while read line循环,读取模块5提取的游戏属性信息文本文件,分割每一行得到对应的字段(cut -f 2 -d "%"),赋值给insert语句(sql_insert)
最后通过mysql -h $HOST -P $PORT -u $USER -p$PWD -e "use $DBNAME; $sql_insert",登录mysql数据库,执行插入语句$sql_insert
7、生成HTML报告
shell 通过连接字符串 table + tr + td + info 来生成 html web 报告。详情请参考我之前的博客:shell将txt转换为html
8、通过电子邮件发送报告
邮件发送模块主要采用/usr/bin/mutt的方式。邮件正文显示一个html报告(默认为美国),其他国家以附件形式发送。详细请参考我之前的博客:linux shell发送邮件附件
使用 crontab 命令定期发送电子邮件。具体配置和使用可以参考我之前写的博客:linux定时运行命令script-crontab
9、网页查询报告
使用JSP提取MySQL中存储的游戏属性信息,循环生成游戏排名的网页信息,请参考我之前的博客:Linux JSP连接MySQL数据库
10、 排名趋势图
趋势图,使用第三方JFreeChart图表生成工具,请参考我之前的博客:JFreeChart学习实例
游戏排名趋势图生成后,需要嵌套到JSP网页中进行展示。完整的排名趋势图请参考我之前的博客:JFreeChart Project Example
自动化主控制脚本
12国游戏排名系统,免费ip代理过滤——“网络爬虫——”数据库保存——“生成排名报告——”及时发送邮件报告——“游戏排名查询——”趋势图生成
都实现了整个流程的自动化,以下是各个模块的脚本实现和功能说明:
通过配置服务器的crontab定时运行进程命令,在早上00:01:00(早上0:01:00),主控脚本top10_all.sh会自动启动。
每天生成的日报使用主控脚本自动生成当天的文件夹,保存当天的抓包数据、分析数据、结果数据,如下图所示:
注:以上文件夹数据为去年测试数据的副本,排名并非本人笔记本截图
因为通过远程代理爬取12个国家排名前800的TOP800需要消耗网络资源、内存资源和时间,严重影响我的上网体验~~~~(>_