关于网站运营方面一些小看法

一直从事偏技术工作,年末了对公司网站运营的状态做了一点分析,将自己对于网站运营的一些看法整理了一下。

网站运营目标一般来说应该是侧重于两个方面

其他衍生出一些Alex排名,PageRank之类的

网站用户来源看,访问网站的用户一般分为:

一般会采用如下一些网站运营方式来达到以上的目标

在开发一个新功能或做一次具体营销时要考虑的问题

Popularity: 5% [?]

Random Posts

如何计算网站访问的有效PV量

为了更好的分析得到的PV量, 使自己由日志文件得到的PV量更接近Google
由日志中统计出的PV量要排除掉如下一些:

注意点: 以上排除项对一些地址会有重复,比如既是搜索引擎的访问,又是内嵌的小页面地址,这样在处理时要注意,以防造成PV量的重复剔除

Popularity: 5% [?]

Related

Nginx服务器的upload上传模块

在Nginx网站的模块列表页中发现一个Nginx服务器的上传模块http://www.grid.net.ru/nginx/upload.en.html

该模块通过Nginx服务器来接受用户上传的文件,在Nginx接受完文件以后再转给后端的程序做处理。

它自动分析客户端的上传请求,将上传的文件保存到 upload_store 所指向的目录位置. 然后这些文件信息将被从原始的请求中剔除,重新组装好上传参数后转到后端由 upload_pass 指定的位置去处理,这样就可以任意处理上传的文件。
每一个上传的 file 字段值将可以由upload_set_form_field 指定的值替换. 文件的内容可以由$upload_tmp_path 变量读到或简单的移到其他位置. 将文件删除由 upload_cleanup 指定控制。
upload_set_form_field可以使用的几个变量

upload_aggregate_form_field 可以多使用的几个变量,文件接收完毕后生成的

官方的设置举例

  1.         # 上传表单应该提交到这个地址
  2.         location /upload {
  3.             # 将请求体转到这个位置
  4.             upload_pass   /test;
  5.  
  6.             # 将上传的文件保存到这个目录下
  7.             # 目录是被散列化的,应该存在子目录 0 1 2 3 4 5 6 7 8 9
  8.             upload_store /tmp 1;
  9.            
  10.             # 允许上传的文件被用户 user 只读
  11.             upload_store_access user:r;
  12.  
  13.             # 设置请求体的字段(添加自己后端处理的信息)
  14.             upload_set_form_field "${upload_field_name}_name" $upload_file_name;
  15.             upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;
  16.             upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;
  17.  
  18.             # 指示后端关于上传文件的md5值和文件大小
  19.             upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5;
  20.             upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size;
  21.  
  22.             # 指示原样转到后端的参数,可以正则表达式表示
  23.             upload_pass_form_field "^submit$|^description$";
  24.         }
  25.  
  26.         # 将请求转到后端的地址处理
  27.         location /test {
  28.             proxy_pass   http://localhost:8080;
  29.         }
  30.     }

将模块添加到Nginx中的方法
下载源代码解压后
为nginx配置额外模块(需要重新编译):

  1. tar xvzf nginx_upload_module-2.0.7.tar.gz
  2. cd nginx631
  3. ./configure –add-module=/usr/local/nginx_upload_module-2.0.7
  4. make
  5. make install

Popularity: 6% [?]

Related

几种常见WEB日志分析方法中被统计的用户访问量差异产生原因分析

这几天一直根据原始的weblog文件做日志分析,发现如下一些ip访问量和页面访问量方面的问题。
通常我们会用如下的一些工具来做WEB网站访问日志的分析。

这几者之间方法机制的存在着差别,就需要好好分析一些,提升一下自己在通过方式 3 得到的数据的精确性,同时也有助于发现一些存在的问题。

工具 ip量 唯一访问者数 访问人次 pv量
google X X X
awstat X X X
yahoo X X X X

注: awstats也有唯一访问数的统计,不过只能在摘要按月历史统计中才能看到,不能以按天的方式看到,为了方便这边对比看到某一天的数据,我单独以某天的日志做一个配置,得到的按月数据等同于按天。

对几种工具的统计的情况对比(以实际统计中某几天的数据做对比)
统计结果呈现出: ip量 < 唯一人数 < 访问人次
对PV量的统计
google 和 yahoo 差不多, awstats 统计出的 页面数 比google和yahoo统计出来的数量要多出约两倍左右。产生这个现象的原因: google和yahoo都是采用的在页面嵌入脚本的方式,这样统计出来的就都是在用户浏览器上显示的页面,所以他们统计出的结果差不多。和awstat统计结果发生大偏差的原因:

访问人次:
这个项只有 google和awstas有,其中google得到的人次要比awstats多。这个可以解释为他们用来识别两次访问间的时间间隔不同。google的判断时间间隔小,所以得到的总访问次数多。
唯一人数:
这个的结果是 yahoo < awstats < google
这个结果的不同可能是由于采用了不同的鉴别唯一用户的算法。估计采用了公网ip+ua+内网ip等的不同组合,从而得到的唯一用户数也不同。

IP访问量
这个只有 yahoo 提供,其他方式中没有该项,但是是可以反映出 ip地址量少于 唯一用户数的 。

对方式3自己写shell脚本做分析,在pv上得到的数据和awstats更接近一些,因为所使用的数据源和分析方式类似。
awstats使用缓冲记录运算获得的用户停留时间等信息不好获得,但是可以做出其他的一些按照ip为基准而awstat等工具未提供的统计,比如

Popularity: 6% [?]

Related

NotPageList的设置会引起Awstats用户访问量统计发生变化

为了方便业务做分析,给awstats添加了一下 ExtraSection 的设置, 结果隔天同事反映awstats统计的访问用户数量一下子少掉好多。

想想ExtraSection的设置不应该影响到Awstats的访问用户数统计,但是打开看awstats的统计量,pv,文件数都没有什么变化,但确实用户访问量发生了大的变化。

于是努力回想在给Awstats设置ExtraSection的时候,还对awstats配置的什么地方做了改动,终于在对比后想起来是修改了 NotPageList 的设置,将zip, swf, wma 等后缀的也做了NotPageList的过滤。

于是猜想是否由于这个选项的设置变化导致了awstat的统计结果不一样,立马开始做验证,将其中几天的日志转到测试服务器上,建立两个配置,一个NotPageList是使用默认的设置,另外一个NotPageList中添加了额外扩展名的设置。
然后分别对两个配置做 update 后再对数据进行查看比较,发现添加额外的扩展名后,访问用户数变少,网页数的统计有变少,文件数没有太大变化。

结论: NotPageList的设置确实会影响AWSTATS对 访问用户数, 页面数,文件数等的统计结果。

带来的问题: AWstats是怎么计算用户访问数的?
根据以上对比和对其中部分代码的分析有如下一些初步结论:
参与访问用户量分析的只有那些被识别为网页的记录
唯一用户数: 用ip 地址 + ua 信息的组合来识别不同的用户访问
访问人次: 被识别的唯一用户在最后一次页面访问后又间隔一定时间(30分钟?)后再次访问,算是该用户的第二次访问。

根据这个分析回过来看前面由于NotPageList的设置变化而导致的统计用户数上差别的原因就可能是:
网页上有对额外添加的这些扩展名文件的连接,对这些文件的打开采用了不同于浏览器ua的插件或工具打开,造成新的ip+ua的组合和用户正在打开页面的ip+浏览器ua不一样,被awstats识别为一个新的用户访问。

Popularity: 5% [?]

Related

http返回代码的一般含义

因为要做web日志做分析,http的返回代码所代表的意思也要再搞清一下,方便分析。

1XX 代码

这些状态代码表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个1xx 响应

2XX 代码

这类状态代码表明服务器成功地接受了客户端请求,一般日志中记录下的应该200的为多

3XX 代码

表示重定向,客户端浏览器必须采取更多操作来实现请求,比如 header Location: /anotherurl 这样的命令。

4XX 代码

表示客户端有问题,比如请求了一个不存在的文件。

5XX 代码

表示服务器端有问题,比如服务器端内存不足,响应超时等。

红颜色的代码是我们经常看到的。

更细的含义这边有一份HTTP返回代码表

Popularity: 5% [?]

Related

根据weblog分析某个业务下的ip访问量

目标:
对日志做分析,获取各个模块的ip访问量。
计划方法:
1. 首先获得符合条件的访问日志记录
2. 对符合条件的记录做排序做uniq得到模块目录下唯一ip列表
3. 再使用uniq 获得各个模块的ip数量
实施步骤:
1. 提取符合条件的日志行,输出ip地址和url

  1.     cat www.access.log |awk '($7 ~ /\.php/|| $7 ~/\/mod1\//){print $1 " " $7}'

2. 通过sed从url中提取模块目录的名称

  1.     |sed -e 's/\(^\/[^\/?"]*\).*/\1/'

3. 排序求每个模块下唯一的ip

  1.     | awk '{print $2 " " $1}'  |sort |uniq

4. 对每个模块做统计倒序排列

  1.     | awk '{print $1}' | uniq -c |sort -nr

合起来:

  1. cat www.access.log |awk '($7 ~ /\.php/|| $7 ~/\/mod1\//){print $1 " " $7}'  \
  2.   |sed -e 's/\(^\/[^\/?"]*\).*/\1/'  \
  3.   | awk '{print $2 " " $1}'  |sort |uniq
  4.   | awk '{print $1}' | uniq -c |sort -nr

Popularity: 5% [?]

Related

为了Awstats给Nginx添加FastCGI方式的Perl支持

有个应用(awstats)需要Perl脚本支持,但是用的Nginx服务器,对Perl支持不好,于是通过FastCGI方式来使用Perl。

首先安装Perl的FCGI模块

  1. wget http://www.cpan.org/modules/by-module/FCGI/FCGI-0.67.tar.gz
  2. tar -zxvf FCGI-0.67.tar.gz
  3. cd FCGI-0.67
  4. perl Makefile.PL
  5. make && make install

还可以使用如下方法安装:

perl -MCPAN -e 'install FCGI'

安装FCGI-ProcManager

  1. wget http://search.cpan.org/CPAN/authors/id/G/GB/GBJK/FCGI-ProcManager-0.18.tar.gz
  2. tar -xzxf FCGI-ProcManager-0.18.tar.gz
  3. cd FCGI-ProcManager-0.18
  4. perl Makefile.PL
  5. make
  6. make install

Perl的FastCGI启动脚本

参考这篇文章 http://bbs.chinaunix.net/archiver/?tid-1224968.html

vi fcgi_perl
  1. #!/usr/bin/perl -w
  2. use FCGI;
  3. use Socket;
  4. use FCGI::ProcManager;
  5. sub shutdown { FCGI::CloseSocket($socket); exit; }
  6. sub restart { FCGI::CloseSocket($socket); &main; }
  7. use sigtrap 'handler', \&shutdown, 'normal-signals';
  8. use sigtrap 'handler', \&restart, 'HUP';
  9. require 'syscall.ph';
  10. use POSIX qw(setsid);
  11.  
  12. #&daemonize; we don't daemonize when running under runsv
  13. #this keeps the program alive or something after exec'ing perl scripts
  14. END() { }
  15. BEGIN() { }
  16. {
  17. no warnings;
  18. *CORE::GLOBAL::exit = sub { die "fakeexit\nrc=" . shift() . "\n"; };
  19. };
  20. eval q{exit};
  21. if ($@) {
  22. exit unless $@ =~ /^fakeexit/;
  23. }
  24. &main;
  25.  
  26. sub daemonize() {
  27. chdir '/' or die "Can't chdir to /: $!";
  28. defined( my $pid = fork ) or die "Can't fork: $!";
  29. exit if $pid;
  30. setsid() or die "Can't start a new session: $!";
  31. umask 0;
  32. }
  33.  
  34. sub main {
  35. #如果使用 IP sockets
  36. #$socket = FCGI::OpenSocket( "127.0.0.1:8999", 10 );
  37. #如果使用 UNIX sockets
  38. #$socket = FCGI::OpenSocket( "/var/run/perl_cgi-dispatch.sock", 10 );
  39.  
  40. #foreach $item (keys %ENV) { delete $ENV{$item}; }
  41. #设置fastcgi进程数,默认四个
  42. my $n_processes = $ENV{FCGI_NPROCESSES} || 4;
  43. $proc_manager = FCGI::ProcManager->new( {n_processes => $n_processes} );
  44. #使用unix socket
  45. $socket = FCGI::OpenSocket( "$ENV{FCGI_SOCKET_PATH}", 10 );
  46. #设置Socket权限
  47. chmod 0777, $ENV{FCGI_SOCKET_PATH};
  48.  
  49. ; #use UNIX sockets – user running this script must have w access to the 'nginx' folder!!
  50. $request =
  51. FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket,
  52. &FCGI::FAIL_ACCEPT_ON_INTR );
  53. $proc_manager->pm_manage();
  54. if ($request) { request_loop() }
  55. FCGI::CloseSocket($socket);
  56. }
  57.  
  58. sub request_loop {
  59. while ( $request->Accept() >= 0 ) {
  60. $proc_manager->pm_pre_dispatch();
  61.  
  62. #processing any STDIN input from WebServer (for CGI-POST actions)
  63. $stdin_passthrough = '';
  64. { no warnings; $req_len = 0 + $req_params{'CONTENT_LENGTH'}; };
  65. if ( ( $req_params{'REQUEST_METHOD'} eq 'POST' ) && ( $req_len != 0 ) )
  66. {
  67. my $bytes_read = 0;
  68. while ( $bytes_read < $req_len ) {
  69. my $data = '';
  70. my $bytes = read( STDIN, $data, ( $req_len$bytes_read ) );
  71. last if ( $bytes == 0 || !defined($bytes) );
  72. $stdin_passthrough .= $data;
  73. $bytes_read += $bytes;
  74. }
  75. }
  76.  
  77. #running the cgi app
  78. if (
  79. ( -x $req_params{SCRIPT_FILENAME} ) && #can I execute this?
  80. ( -s $req_params{SCRIPT_FILENAME} ) && #Is this file empty?
  81. ( -r $req_params{SCRIPT_FILENAME} ) #can I read this file?
  82. )
  83. {
  84. pipe( CHILD_RD, PARENT_WR );
  85. pipe( PARENT_ERR, CHILD_ERR );
  86. my $pid = open( CHILD_O, "-|" );
  87. unless ( defined($pid) ) {
  88. print("Content-type: text/plain\r\n\r\n");
  89. print
  90. "Error: CGI app returned no output – Executing $req_params{SCRIPT_FILENAME} failed !\n";
  91. next;
  92. }
  93. $oldfh = select(PARENT_ERR);
  94. $| = 1;
  95. select(CHILD_O);
  96. $| = 1;
  97. select($oldfh);
  98. if ( $pid > 0 ) {
  99. close(CHILD_RD);
  100. close(CHILD_ERR);
  101. print PARENT_WR $stdin_passthrough;
  102. close(PARENT_WR);
  103. $rin = $rout = $ein = $eout = '';
  104. vec( $rin, fileno(CHILD_O), 1 ) = 1;
  105. vec( $rin, fileno(PARENT_ERR), 1 ) = 1;
  106. $ein = $rin;
  107. $nfound = 0;
  108.  
  109. while ( $nfound =
  110. select( $rout = $rin, undef, $ein = $eout, 10 ) )
  111. {
  112. die "$!" unless $nfound != -1;
  113. $r1 = vec( $rout, fileno(PARENT_ERR), 1 ) == 1;
  114. $r2 = vec( $rout, fileno(CHILD_O), 1 ) == 1;
  115. $e1 = vec( $eout, fileno(PARENT_ERR), 1 ) == 1;
  116. $e2 = vec( $eout, fileno(CHILD_O), 1 ) == 1;
  117.  
  118. if ($r1) {
  119. while ( $bytes = read( PARENT_ERR, $errbytes, 4096 ) ) {
  120. print STDERR $errbytes;
  121. }
  122.  
  123. if ($!) {
  124. $err = $!;
  125. die $!;
  126. vec( $rin, fileno(PARENT_ERR), 1 ) = 0
  127. unless ( $err == EINTR or $err == EAGAIN );
  128. }
  129. }
  130. if ($r2) {
  131. while ( $bytes = read( CHILD_O, $s, 4096 ) ) {
  132. print $s;
  133. }
  134. if ( !defined($bytes) ) {
  135. $err = $!;
  136. die $!;
  137. vec( $rin, fileno(CHILD_O), 1 ) = 0
  138. unless ( $err == EINTR or $err == EAGAIN );
  139. }
  140. }
  141. last if ( $e1 || $e2 );
  142. }
  143. close CHILD_RD;
  144. close PARENT_ERR;
  145. waitpid( $pid, 0 );
  146. } else {
  147. foreach $key ( keys %req_params ) {
  148. $ENV{$key} = $req_params{$key};      
  149. }
  150.  
  151. # cd to the script's local directory
  152. if ( $req_params{SCRIPT_FILENAME} =~ /^(.*)\/[^\/]+$/ ) {
  153. chdir $1;
  154. }
  155. close(PARENT_WR);
  156.  
  157. #close(PARENT_ERR);
  158. close(STDIN);
  159. close(STDERR);
  160.  
  161. #fcntl(CHILD_RD, F_DUPFD, 0);
  162. syscall( &SYS_dup2, fileno(CHILD_RD), 0 );
  163. syscall( &SYS_dup2, fileno(CHILD_ERR), 2 );
  164.  
  165. #open(STDIN, "<&CHILD_RD");
  166. exec( $req_params{SCRIPT_FILENAME} );
  167. die("exec failed");
  168. }
  169. } else {
  170. print("Content-type: text/plain\r\n\r\n");
  171. print
  172. "Error: No such CGI app – $req_params{SCRIPT_FILENAME} may not exist or is not executable by this process.\n";
  173. }
  174. }
  175. }

再设置启动的脚本

vi startfcgiperl
  1. export FCGI_SOCKET_PATH="/tmp/perl_fcgi.socket"
  2. export FCGI_NPROCESSES=4
  3. ./fcgi_perl &

为Nginx添加FastCGI的Perl支持

编辑 nginx.conf 脚本,添加如下内容

location ~* .*\.pl$
{
include awstats.conf;
}
location /awstatsicon/
{
   alias /var/www/awstats-6.8/wwwroot/icon/;
}
vi awstats.conf
fastcgi_pass unix:/tmp/perl_fcgi.socket;
fastcgi_index awstats.pl;
#fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fastcgi_param SCRIPT_FILENAME /var/www/awstats-6.8/wwwroot/cgi-bin$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME /var/www/awstats-6.8/wwwroot/cgi-bin/awstats.pl;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_read_timeout 60;

配置Awstats

运行 /var/www/awstats-6.8/tools/awstats_configure.pl 设置好一些变量

mkdir -p /etc/awstats
cp /var/www/awstats-6.8/wwwroot/awstats.model.conf /etc/awstats/awstats.my.site.com.conf

使用 vi /etc/awstats.my.site.com.conf 进行一些参数修改,设置icon目录,DirData数据存放目录,来源log日志文件路径等
使用 /var/www/awstats-6.8/tool/awstats_updateall.pl now -awstatsprog=/var/www/awstats-6.8/wwwroot/cgi-bin/awstats.pl 命令进行初始数据更新
使用 http://my.site.com/awstats.pl 查看数据

后续

将 ./awstats_updateall.pl now -awstatsprog=/var/www/awstats-6.8/wwwroot/cgi-bin/awstats.pl 放到crontab 中做定时更新,可以做一个脚本和日志文件的处理一起做。

碰到问题
在使用unix socket时,碰到两个问题
1.写socket的目录没有写权限,添加上解决
2.nginx连不上socket, 看日志提示没有权限,在创建了socket后添加一句
chmod 0777, $ENV{FCGI_SOCKET_PATH};
问题解决。

遗留对awstats.pl访问密码限制问题尚未解决好。
解决 在 nginx 中按如下配置方式

location /awstats {
  root /var/www/awstats-6.8/;
  include awstats.conf;
  auth_basic "awstats";
  auth_basic_user_file /var/www/.passwd;
}

其中 将 awstats-6.8 目录下的 wwwroot 修改为 awstats, /var/www/.passwd 为访问目录要用来验证的账号密码文件,用apache的提供的工具htpasswd制作而成

Popularity: 8% [?]

Related

安装配置Nginx 0.6.32 +php 5.2.6(fastcgi) + Mysql 5.0.51

安装过程参考: http://blog.s135.com/read.php/314.htm

安装 mysql 5.0.51

安装 php 5.2.6

安装 nginx

nginx的中文Wiki站点 http://wiki.codemongers.com/NginxChs
niginx的英文站点 http://nginx.net

配置开机启动Mysql+Phpcgi+Nginx

因为没有安装服务,需要添加一些命令到 /etc/rc.local 中用来开机启动

  1. ulimit -SHn 51200
  2. /bin/sh /usr/local/mysql5051/bin/mysqld_safe –defaults-file=/usr/local/mysql5051/my.cnf &
  3. /usr/local/php526/spawn-fcgi -a 127.0.0.1 -p 10080 -C 30 -u www -f /usr/local/php526/bin/php-cgi
  4. /usr/local/nginx632/sbin/nginx

设置iptables

在防火墙中放开相关服务端口。

  1. #web port
  2. -A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT
  3. #fastcgi port
  4. -A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 10080 -s 127.0.0.1 -j ACCEPT
  5. #mysql 本地通过unix socket访问, 不用设置
  6. -A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp -s 127.0.0.1 –dport 3306 -j ACCEPT

使用 /sbin/service iptables restart 重新应用防火墙规则

验证服务状态

验证 nginx + php 运行是否正常。
在 /var/www/xxt.igrow.cn 目录下建一个test.php,里面存放内容

  1. <?
  2.      echo phpinfo();
  3. ?>

通过 curl http://127.0.0.1/test.php 可以看到输出的php信息,表示 nginx + php 运行正常。

Popularity: 4% [?]

Related

Nignx配合Memcached提升400%性能(阅读笔记)

原始文章: http://www.igvita.com/2008/02/11/nginx-and-memcached-a-400-boost/

原文引用了 http://nubyonrails.com/articles/about-this-blog-memcached

http://blog.leetsoft.com/2007/5/22/the-secret-to-memcached

都是讲述怎样在Rails中使用memcached的(主要是key的设定方式和怎样对memcached的使用做类的包装)

文章在两篇引用文章的基础上直接将memcached的使用提升到url层,通过nginx对memcached的原生支持,讲述了一种利用url rewrite机制来直接将url请求地址作为key给memcached做处理,用以提升访问速度。

首先在编译 nginx 时要添加 memcached 模块编译,nginx 的 memcached 模块配置参数说明见 http://wiki.codemongers.com/NginxHttpMemcachedModule

文章讲述利用 url rewrite 机制来做key validate 的自动化处理,主要讲了一下url rewrite和mime type的配合问题。
本文启发
1.网站url地址要好规划,这样可以将不同类别的资源做分组,方便以后的统一处理,添加缓冲之类的。
2.怎样确定内容的修改频度,自动话的处理?如果是动态的对数据敏感新强的页面。
3.有没有方法可以在url层做validate time 的规范话处理(资源的更新频度,每小时,每天等等)。

Popularity: 3% [?]

Related

← Previous PageNext Page →