perl获取AppAnnie数据
来源:互联网 发布:淘宝假授权书处罚 编辑:程序博客网 时间:2024/05/20 16:12
。。。。。。
需求:
获取https://www.appannie.com/apps/ios/top/united-states/games/?device=iphone 排行榜数据,并得到每个game的大小和语言(从排行榜点击对应game名字进入详细页面)以及分类(详细页面中点击左侧history)。
本blog主要用来记录coding中遇到的一些问题,相关知识点如下:
LWP(Library for www in Perl )
UserAgent and Cookies
perl正则表达式
utf8 and Json
threads 多线程处理
Spreadsheet 写入Excel
配置文件等小细节
目录
用 [TOC]
来生成目录:
- 目录
- before start
- 简单开始
- html转义字符处理
- 处理登陆
- 获取chart数据
- json utf8 unicode
- Excel
- Threads 节约时间
- 细节和总结
- Annie Api
before start
对perl的了解并不多,写过几个文件操作的脚本来简化工作流程,这次主要是为了帮中国好室友提升工作效率,同时提升对perl的驾驭能力,有目的的学习才是最有效率的!
这篇blog的所有code都可以在我的github页面找到https://github.com/playscforever/my_perl_study/blob/master/final/login_appannie_final.pl
简单开始
刚开始处于一无所知的状态,直接google如何利用perl获取网页内容:
use strict; use warnings; use LWP::Simple qw(get); my $html = get( "https://www.appannie.com/apps/ios/top/united-states/games/?device=iphone" ); print $html;
bingo~ , 突然感觉好像成功了一半(虽然还没开始),需要的内容都出来了,接下来就要用正则来提取自己需要的信息,这里贴一下我学习perl和正则的两个教程,虽然大多数问题都是用google和百度来解决的,看看教程对perl了解个大概还是有必要的:
http://shouce.jb51.net/perl/index.html
http://qntm.org/files/perl/perl_cn.html简单分析$html里面的内容之后,写出正则提取需要的game名字和对应详细页面的url:
while($html =~ m/span title=\"(.*)\" class=\"oneline-info title-info\">\s*<a href=\"\/(.*)\">/g) { $app_name = $1; # 这里的$1对应第一个括号(.*) $app_url = $2;}
参数g (global)可以匹配所有符合要求的字符串,当遇到符合要求的字符串之后便进入while循环中,其实最开始用到的方法是把$html split成一行一行的,然后逐行分析,后来发现有global参数才改成上面这种简答的形式。
html转义字符处理
有些标题里面会有&这样的特殊字符,提取出来之后就变成了 & amp; (没有空格),直接用替换简单处理
$app_name =~ s/&/&/g; $app_name =~ s/'/'/g;
处理登陆
详细页面的url 需要稍微拼凑一下,还是蛮简单的
my $innerHtml = $ua->get("https://www.appannie.com/".$app_url);
好了,问题来了,这特么是一个需要登陆才能访问的页面啊我勒个擦!
ok,这里卡了一个星期 ,好了细节不描述了,直接贴代码说问题吧
#这里用到了UserAgent来模拟浏览器 my $ua = LWP::UserAgent->new(); #生成一个自动保存的cookie my $cookie_jar = HTTP::Cookies->new( file => "testcookies.txt", autosave => 1, ); $ua->cookie_jar( $cookie_jar ); $ua->agent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36"); #先获取一下登陆页面 my $res = $ua->get("https://www.appannie.com/account/login/", Referer => "https://www.appannie.com/"); #这里是重点啊,在firefox或者chrom中登陆,然后观察传递的数据,会发现除了用户名和密码是必须要传递的以外,还有个token也是需要传递的,否则死也登陆不上去啊!这个token可以在header中用正则获取,然后一起post给服务器。 my $c = $res->header('set-cookie' ); $c =~ m{csrftoken=(\w+);}; my $token = $1; $res = $ua->post("https://www.appannie.com/account/login/", Content => [ csrfmiddlewaretoken=>$token , next => "/", #这里需要替换成自己的用户名和密码,注意双引号中有些特殊字符是转义的,比如@ username => $cfg{username}, password=> $cfg{password}, ] ); print $res->content;
搞定了登陆就成功了一大半了,不同网站登陆方式都不太一样,按照总监的话来说,AppAnnie这里有点猥琐,一般网站都不会这么麻烦。这里附加一个美团登陆的例子:https://github.com/playscforever/my_perl_study/blob/master/login_success_meituan.pl 不需要用户名和密码,用浏览器登陆的时候看一下传递的token 复制过来就好,操作频繁会有验证码出现。
接着再用正则提取一下size 和 language
#这里的? 是惰性匹配,尽可能少的匹配my($size,$empty,$language) = ($innerHtml->content =~ m/Size:<\/b>(.*?)<\/p><p><b>(.*?)<\/b>(.*?)<\/p>/);
获取chart数据
接下来要做的事情就是进入history页面获取游戏的类型~ 在get到history页面的内容之后,发现并没有想要的数据, 进入firefox开发者工具,查看页面发送的请求,会发现chart数据是进入history页面之后通过ajax获取的,好了 根据ajax的url来获取对应的数据吧!!(还是需要拼凑一下url)
my $res = $ua->get("https://www.appannie.com/".$tempUrl."rank-chart");
too young too simple… 这样理所当然是获取不到任何数据的,得到回应是 ajax only,也就是说服务器判断你的请求不是ajax请求,OK,继续查看当前请求发送了哪些数据(firefox按f12然后点击网络,进入对应页面即可),然后模仿登陆添加一下:
my $res = $ua->get("https://www.appannie.com/".$tempUrl."rank-chart", Accept => "application/json, text/plain, */*", #对应请求的类型 'X-Requested-With' =>"XMLHttpRequest", 'X-NewRelic-ID' =>"VwcPUFJXGwEBUlJSDgc=", );
这样就能得到类似于这样的json数据(省略头尾) :
{“category”: {“name”: “\u8d5b\u8f66\u6e38\u620f (\u6e38\u620f)”, “id”: 7013}
json utf8 unicode
接下来就要解析一下json并处理一下unicode到中文的转换问题,关于中文乱码的问题,在前面加上use utf8::all;基本就ok 了 ,不然就encode decode什么的。
my $chart = $res->content; #这一步是unicode转换 $chart =~ s/\\u([0-9a-fA-F]{4})/pack("U",hex($1))/eg; my $json = new JSON; #解析json数据 my $obj = $json->decode($chart); my $type = ""; for (my $var = 0; $var <= $#{$obj->{data}}; $var++) { #把每个data的name都拿出来 $type = $type.$obj->{data}->[$var]->{category}->{name}; }#下面贴一下json数据 '{"meta": {"end": "2015-08-22", "vertical": "apps", "countries": "US", "f": null, "app_id": "648668184", "start": "2015-07-24", "market": "ios"}, "data": [{"category": {"name": "\\u52a8\\u4f5c\\u6e38\\u620f (\\u6e38\\u620f)", "id": 7001}, "country": {"code": "US", "name": "\\u7f8e\\u56fd"}, "ranks": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 3, 1, 0]}, {"category": {"name": "\\u6240\\u6709\\u7c7b\\u522b", "id": 36}, "country": {"code": "US", "name": "\\u7f8e\\u56fd"}, "ranks": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 984, 10, 1, 0]}, {"category": {"name": "\\u8d5b\\u8f66\\u6e38\\u620f (\\u6e38\\u620f)", "id": 7013}, "country": {"code": "US", "name": "\\u7f8e\\u56fd"}, "ranks": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 1, 1, 0]}, {"category": {"name": "\\u6e38\\u620f", "id": 6014}, "country": {"code": "US", "name": "\\u7f8e\\u56fd"}, "ranks": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 3, 1, 0]}], "events": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Initial release", "", "", "", ""], "success": true}';
Excel
excel的处理,这里用到的是 Spreadsheet::WriteExcel,还算是比较简单吧,需要注意的就是不能操作一个已经打开的excel,并且不能在程序中打开excel之后很久再操作,否则无法写入。(有一次用了代理IP,结果访问网络要很久,然后得到的数据死活写不进去excel,后来花了1个多小时才发现原来是这个问题)
sub writeExcel{ # 打开之后立刻读写,不要停留很长时间 my $workbook = new Spreadsheet::WriteExcel( $cfg{outfilename} ); my $worksheet = $workbook->add_worksheet( $cfg{firstsheetname} ); # 这里的data是全局的 print Dumper \@data; foreach my $i (0 .. $#data){ my @values = split(/\,\,\,/,$data[$i]); foreach my $j (0 .. $#values){ #excel左上角第一个是【0,0】 $worksheet->write($i,$j,$values[$j]); } }}
Threads 节约时间
因为AppAnnie是外国的站点,访问起来有点慢,一个网站打开要好几秒,想要获取100个数据就要十几分钟了,后来发现perl是可以实现多线程,然后果断加了进来:
#需要用到的模块 use threads; use threads::shared; 。。。 # 需要调用的函数名,后面是函数对应的参数,这句话放在循环体中,把所有需要访问的url都加入threads里面 threads->create('getInnerData',$app_name,$app_url,$count/3); 。。。 #然后再取出所有的thread,开始同步执行 foreach ( threads->list() ){ $_->join( ); } #。。。。 然后再getInnerData里面判断threads结束的方法: writeExcel() unless threads->list(threads::running);
很不辛的是,加了threads之后,ip被安妮给封掉了,不过发封邮件,第二天差不多就解封了。
另外就是,thread访问的数据必须是shared的,否则就会出错
my @data : shared;share(@data);
细节和总结
懒得写了,最后还是失败了,不过学到了很多东西,主要是加深了对perl的理解,正则也有进步,网络相关的只是也复习了一下,各方面都还是有待提高的,路漫漫啊,继续努力吧骚年!!!
一直都懒得写blog,总是把需要记住的东西写在云笔记,但是很少去总结去复习,以后还是要多写写blog,练练文笔的同时也可以梳理一下自己所学的东西。
还是提一下配置文件吧,我的配置文件比较简单,每行都是写成 key = value的形式,这样解析和访问起来也简单,直接正则加hash:
open I,"<yzj.cfg" or print("配置文件丢失 yzj.cfg 必须放在一起\n");# cfg中存放yzj.cfg中的键值对 等号周围空格可有可无my %cfg = map{m/(\w+?)\s*=\s*(.+)/} <I>;close I;
Annie Api !!!!!!
在经历一连串失利之后(主要是需要登录才能访问的页面检测太严格了),上网查了一下发现安妮居然是有提供Api的,https://support.appannie.com/hc/en-us/categories/200261564
不过悲剧的是,这个api只能对自己的account和app才有用,卧槽这不是坑爹吗!!!
所以最后的结论是,获取排行榜的名单很容易,想要获取具体app/game的内容,那就只能用非常慢的速度去爬了,或者考虑用不同ip不同账号分布式同时进行,有待研究。。。
- perl获取AppAnnie数据
- perl脚本获取网络数据
- 关注AppAnnie对iOS游戏营收数据分析
- Appannie公布第二季度全球应用下载数据
- perl获取机器ip
- PERL: 获取system输出
- perl获取当前环境
- Perl 获取cpu使用率
- perl 获取网页内容
- perl 获取路径Cwd
- Perl获取数组长度
- perl 获取cookie
- perl获取时间
- perl weixin 获取uuid
- perl 获取虚拟机信息
- perl读取彩票数据
- perl 数据清洗 实例
- perl常用数据分析
- [BZOJ1046][HAOI2007]上升序列
- Linux环境下【mysql】修改mysql5.5默认编码
- AS1.3 及其以上预览版新插件-实验版(NDK)
- 猜拳游戏--最高效版本
- 简单演示Exploit SEH原理(未开启SafeSEH模块)
- perl获取AppAnnie数据
- 欢迎使用CSDN-markdown编辑器
- Swift 语言基础算法
- 一次重定向引起的异步IO
- 推荐两个比较全的android在线源代码
- 启动maven项目报错:java.lang.ClassNotFoundException: ContextLoaderListener解决方法
- 华为上机试题5(整数排序并删除一些元素)
- UVa-679 Dropping Balls
- JAVA基础8(代码剖析)