#!/usr/bin/perl #VGSEO2 indexOutput - AI+Do not publish Version. #v1.0.0 release! #v1.0.1 ampのリンク処理をfix #v1.0.2 deleteリンク処理実装 #v1.0.3 コピーライト、メニューナビ、画像の左右を乱数化 #v1.0.4 favicon.icoの導入 #v1.0.5 検索エンジンにキャッシュ(インデクシング)されてないドメインはリンクを出力しないスパム防止機能を追加 #v1.0.6 自動サイトマップXML送信機能を追加(1日1回のロック制御付き) #v1.0.7 URL置換機能にバグあり。Aタグ内のURLに対象キーワードが入っていると置換されてしまうバグを修正しました。 #v1.0.8 not foundページを301トップに変更してクローラービリティの正常化を図る #v1.0.9 ペナ回避施策 sponsored #v1.1.0 ld+json(構造化マークアップのdatePublishedとdateModified)拡張 #v1.1.1 アンカーテキスト部分のnull, follow, ugc, sponsoredの乱数化 #CSVファイルの読み込み $csvfileMainData = './csvfiles/mainData.csv'; # コンテンツ用 $csvfileDatasets = './csvfiles/htmlDataSets.csv'; $csvfileCssDatasets = './csvfiles/cssDataSets.csv'; ###### リンク消去を行いたいドメインのリスト $delLog = "http://www.kaseo.net/temp/delete.txt"; ###### nofollowを行いたいドメインのリスト $nofollowdomains = "http://www.kaseo.net/temp/nofollowdomains.txt"; ###### sponsoredを行いたいドメインのリスト $sponsoreddomains = "http://www.kaseo.net/temp/sponsoreddomains.txt"; ###### ugcを行いたいドメインのリスト $ugcdomains = "http://www.kaseo.net/temp/ugcdomains.txt"; ###### dofollowを行いたいドメインのリスト $dofollowdomains = "http://www.kaseo.net/temp/dofollowdomains.txt"; ###### yahooにキャッシュされているかチェックテキスト $indexCountLog = "./indexCount.txt"; use utf8; use CGI; use Cwd; use LWP::UserAgent; use POSIX qw(strftime); use File::Glob ':glob'; use File::Basename; ###### 初期処理 &decodem; if ($form{page} =~ /^\s*?$/){ $pageName = '/'; } else { $pageName =$form{page}; } ###### メイン処理 local $HTML_Contents; local @HTML_Contents; ##### HTMLのデータセットを呼び出す local @OUTPUT_HTML; # タグデータのCSVファイルをオープン open( FH, "<:encoding(utf8)", "$csvfileDatasets") or die "Can't open $csv_file: $!"; my @csv_tag_data = ; close(FH); # 複数データが入ったCSVを二次元配列で戻す一括処理(タグデータ) local @tag_database = &get_array(@csv_tag_data); #タグ情報の行の最大数をカウントする my $maxRowHTML = &count_row_elements(@tag_database); srand(); for (my $i =0; $maxRowHTML > $i;$i++){ # の列の要素数をカウントする my $target_column = $i; # 0-based indexなので、2列目は1になります。 my $count = &count_column_elements(\@tag_database, $target_column); $count = int(rand($count)); $tag_database[$count][$target_column] =~ s/^"//; $tag_database[$count][$target_column] =~ s/"$//; push @OUTPUT_HTML, $tag_database[$count][$target_column] . "\n"; last if $i == 99; } ##### CSSのデータセットを呼び出す local @OUTPUT_CSS; # CSSデータのCSVファイルをオープン open( FH, "<:encoding(utf8)", "$csvfileCssDatasets") or die "Can't open $csv_file: $!"; my @csv_css_data = ; close(FH); # 複数データが入ったCSVを二次元配列で戻す一括処理(タグデータ) local @css_database = &get_array(@csv_css_data); #タグ情報の行の最大数をカウントする my $maxRowCSS = &count_row_elements(@css_database); srand(); for (my $i =0; $maxRowCSS > $i;$i++){ # の列の要素数をカウントする my $target_column = $i; # 0-based indexなので、2列目は1になります。 my $count = &count_column_elements(\@css_database, $target_column); $count = int(rand($count)); $css_database[$count][$target_column] =~ s/^"//; $css_database[$count][$target_column] =~ s/"$//; push @OUTPUT_CSS, $css_database[$count][$target_column] . "\n"; last if $i == 99; } # print $tag_database[0][0]; # print $tag_database[1][0]; # print $tag_database[2][0]; # print $tag_database[0][1]; # print $tag_database[1][1]; # print $tag_database[2][1]; # メインコンテンツのCSVファイルをオープン open( FH, "<:encoding(utf8)", "$csvfileMainData") or die "Can't open $csv_file: $!"; my @csv_data = ; close(FH); # 複数データが入ったCSVを二次元配列で戻す一括処理(メインコンテンツ) local @database = &get_array(@csv_data); #データベースの列を取得して、このページのデータを確保する(メインコンテンツ) local ($colPoint) = &getData(@database); #もし該当する$colPoint(ディレクトリ名)が存在しなければNot Foundとする if ($colPoint =~ /^\s*?$/){ &print_header_redirect_to_domain_top(); #&print_header_notfound(); #print "404 Not Found."; exit; } # ヘッダーのサブルーチン呼び出し local $link; @OUTPUT_HTML = &print_header($colPoint, \@OUTPUT_HTML, \@OUTPUT_CSS); my $delx = LWP::UserAgent->new(); my $delxtemp = $delx->get($delLog); local $dellines = $delxtemp->content, "\n"; local @dellists = split ("\n", $dellines); foreach my $line (@OUTPUT_HTML){ next unless $line; $line =~ s/(\r\n|\n|\r)+/\n/ig; push @HTML_Contents, $line; } #naviメニューの乱数生成 srand; local $navRand = int(rand(2)); push @HTML_Contents, "

\n" unless $navRand; push @HTML_Contents, "

" . $database[1][$colPoint] . "

\n"; push @HTML_Contents, "

" . $database[2][$colPoint] . "

\n"; # print $database[3][$colPoint] . ":3
\n"; $database[3][$colPoint] =~ s/^"//; $database[3][$colPoint] =~ s/"$//; #local @mainContents = split /\r\n|\r|\n/, $database[3][$colPoint]; #対策キーワードの取得 my @kwds = split /,/, $database[5][$colPoint]; #コンテンツ内にある対策キーワードのリンク化処理 my %replaced; local $mflag = 0; #インデクス数が格納してあるファイルを読み込む open (IN, $indexCountLog); local @indexCount = ; local $indexCount = join '', @indexCount; $indexCount =~ s/\s//ig; $indexCount = 1 unless (-f "$indexCountLog"); foreach my $kwd (@kwds) { my $escaped_kwd = quotemeta($kwd); next if $replaced{$kwd}; if ($database[3][$colPoint] =~ /$escaped_kwd/ and $indexCount) { $database[3][$colPoint] =~ s/$escaped_kwd/###A###$kwd###\/A###/i; $replaced{$kwd} = 1; $mflag = 1; } } #### 被リンク - rel属性チェンジ local $reltype; # スポンサード $sponsoreddomains my $spox = LWP::UserAgent->new(); my $spoxtemp = $spox->get($sponsoreddomains); local $spolines = $spoxtemp->content, "\n"; local @spolists = split ("\n", $spolines); foreach $spolists (@spolists){ next unless $spolists; local ($spoline, $spoflag) = split ("<>", $spolists); chomp $spoline; $spoline =~ s/\s//ig; if ($database[6][$colPoint] =~ /$spoline/i){ $reltype = 'sponsored'; last; } } # ユーザージェネレーティブコンテンツ $ugcdomains my $ugcx = LWP::UserAgent->new(); my $ugcxtemp = $ugcx->get($ugcdomains); local $ugclines = $ugcxtemp->content, "\n"; local @ugclists = split ("\n", $ugclines); foreach $ugclists (@ugclists){ next unless $ugclists; local ($ugcline, $ugcflag) = split ("<>", $ugclists); chomp $ugcline; $ugcline =~ s/\s//ig; if ($database[6][$colPoint] =~ /$ugcline/i){ $reltype = 'ugc'; last; } } # ノーフォロー $nofollowdomains my $nofx = LWP::UserAgent->new(); my $nofxtemp = $nofx->get($nofollowdomains); local $noflines = $nofxtemp->content, "\n"; local @noflists = split ("\n", $noflines); foreach $noflists (@noflists){ next unless $noflists; local ($nofline, $nofflag) = split ("<>", $noflists); chomp $nofline; $nofline =~ s/\s//ig; if ($database[6][$colPoint] =~ /$nofline/i){ $reltype = 'nofollow'; last; } } # ドゥフォロー $dofollowdomains my $dofx = LWP::UserAgent->new(); my $dofxtemp = $dofx->get($dofollowdomains); local $doflines = $dofxtemp->content, "\n"; local @doflists = split ("\n", $doflines); foreach $doflists (@doflists){ next unless $doflists; local ($dofline, $dofflag) = split ("<>", $doflists); chomp $dofline; $dofline =~ s/\s//ig; if ($database[6][$colPoint] =~ /$dofline/i){ $reltype = 'follow'; last; } } # いずれにも該当しないランダム化 my @values = ('sponsored', 'ugc', 'nofollow'); if (!$reltype) { $reltype = $values[int(rand(@values))]; } $database[3][$colPoint] =~ s/###A###(.*?)###\/A###/$1<\/a>/g; my $random_width = int(rand(11)) + 15; my $random_minsize = int(rand(51)) + 150; my $random_margin = int(rand(6)) + 15; #画像マージンの乱数生成 srand; local $floatRand = int(rand(2)); local $floatImage; if ($floatRand){ $floatImage = "right"; }else{ $floatImage = "left"; } if ($mflag){ push @HTML_Contents, "\n" if $database[4][$colPoint]; } else { if ($indexCount){ push @HTML_Contents, "<\/a>\n" if $database[4][$colPoint]; } else{ push @HTML_Contents, "\n" if $database[4][$colPoint]; } } #

タグ入れてコンテンツメイン生成 local @contents = split /\r\n|\r|\n/, $database[3][$colPoint]; foreach my $contents (@contents){ next unless $contents; $contents = "

$contents<\/p>\n"; push @HTML_Contents, $contents; } push @HTML_Contents, "

\n" if $navRand; #コピーライト生成 local $rand; srand; $rand = int(rand(3)); if ($rand == 1){ $copy01 = 'Copyright'; } elsif($rand == 2) { $copy01 = 'copyright'; } else { $copy01 = ''; } srand; $rand = int(rand(3)); if ($rand == 1){ $copy02 = '(c)'; } elsif($rand == 2) { $copy01 = '©'; } else { $copy01 = '(C)'; } srand; $rand = int(rand(2)); if ($rand){ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); $year += 1900; $mon++; $copy03 = $year; } else { $copy03 = ''; } srand; $rand = int(rand(2)); if ($rand){ $copyA = "

"; $copyB = "
"; } else { $copyA = "
"; $copyB = "
"; } push @HTML_Contents, "$copyA$copy01 $copy02 $copy03 $database[1][$colPoint]$copyB\n"; push @HTML_Contents, "

\n

\n"; #出力 $HTML_Contents = join '', @HTML_Contents; $HTML_Contents = &ampmake($HTML_Contents) if $form{amp} == 1; foreach $delline (@dellists){ next unless $delline; local ($delline, $delflag) = split ("<>", $delline); chomp $delline; $delline =~ s/\s//ig; $HTML_Contents =~ s/(.+?)<\/a>/$3/ig; } print $HTML_Contents; &sitemapPing(); exit; ###### サイトマップxmlを送信する最終処理 sub sitemapPing(){ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); $year += 1900; $mon++; mkdir ('./lock/'); open (FH, './lock/sitemap.lock'); local $lockdate = ; chomp $lockdate; if ($lockdate eq "$year/$mon/$mday"){ #print "済"; }else { #print "未"; open (OUT, '> ./lock/sitemap.lock'); print OUT "$year/$mon/$mday"; local $sitemapurl = 'https://' . ($ENV{HTTP_HOST} || $ENV{SERVER_NAME}) . '/sitemap.xml'; my $ping_url = "https://www.google.com/ping?sitemap=" . $sitemapurl; my $result = `curl -s -o /dev/null -w "%{http_code}" $ping_url`; if ($result != 200) { open (OUT, '>> ./lock/pingError.log'); print OUT "$year/$mon/$mday,Error: received status code $result\n"; } } } ###### サブルーチン: 環境変数から現在のURLパスを取得し、JSON-LD形式のパンくずリストの構造化データを生成 sub generate_breadcrumbs_json_ld_from_env { # 環境変数から現在のURLパスとホスト名を取得 my $request_uri = $ENV{REQUEST_URI} || '/'; my $host = $ENV{HTTP_HOST} || $ENV{SERVER_NAME}; # クエリストリングを排除するため、URLパスの"?"以前の部分を取得 $request_uri =~ s/\?.*//; # URLパスを"/"で分割してパンくずリスト情報を作成 my @path_parts = split('/', $request_uri); my @breadcrumbs; # ホームディレクトリ(トップページ)を追加 my $url = "https://$host/"; push @breadcrumbs, { name => "Home", url => $url }; # 以降のディレクトリを追加 foreach my $part (@path_parts) { next if $part eq ""; $url .= "$part/"; push @breadcrumbs, { name => $part, url => $url }; } # JSON-LD構造化データを生成 my $json_ld = qq({\n "\@context": "https://schema.org",\n "\@type": "BreadcrumbList",\n "itemListElement": [\n); foreach my $index (0 .. $#breadcrumbs) { my $position = $index + 1; my $name = $breadcrumbs[$index]{name}; my $item_url = $breadcrumbs[$index]{url}; $json_ld .= qq( {\n "\@type": "ListItem",\n "position": $position,\n "item": {\n "\@id": "$item_url",\n "name": "$name"\n }\n }); $json_ld .= "," if $index < $#breadcrumbs; $json_ld .= "\n"; } $json_ld .= qq( ]\n}); #一緒にデートも local $DateJsonList = &generate_date_json_ld_from_yesterday(); my $json_fix_data = $json_ld . $DateJsonList; return ($json_fix_data); } ###### amp生成 sub ampmake(){ local $HTML_Contents = $_[0]; local ($nowDir) = $ENV{'REQUEST_URI'}; ($nowDir) = split /\?/, $nowDir; # AMP HTMLに適したdoctypeに置換 $HTML_Contents =~ s/\s*?//i; #スタイル消す $HTML_Contents =~ s/'; $HTML_Contents =~ s//\n$ampcanonical\n$ampviewpos\n \n"; } push @makesitemapbaseurl, "https://$ENV{HTTP_HOST}$line"; $i++; } #サイトマップ出力 if ($form{sitemap} == 1){ &xmlhead; my $sitemap = &create_sitemap_from_urls(@makesitemapbaseurl); print $sitemap; exit; } #Content-Typeの200出力設定 my $cgi = CGI->new; # ステータスコード404を設定 my $status = "200 OK"; # ヘッダーを出力 print $cgi->header( -status => $status, -type => 'text/html', -charset => 'UTF-8' ); #リンクバーの色変更 local $stylebgcolor = &random_light_color; local $stylelinkcolor = &random_dark_color; local $breakpoint = &random_breakpoint; $style =~ s/{_(.*?)_}/$$1/ig; #最終連打リンク foreach my $line (@HeaderHTML) { next unless $line; $line =~ s/{_(.*?)_}/$$1/ig; push @lines, $line; } return @lines; } ###### ブレークポイントのランダム生成 sub random_breakpoint { my @breakpoints = (576, 767, 991, 1199, 1200); return $breakpoints[int(rand(scalar @breakpoints))]; } ###### 濃い色のランダム生成 sub random_dark_color { my $max_brightness = 51; # 最高輝度 (16進数で33) return sprintf("%02X%02X%02X", int(rand($max_brightness + 1)), int(rand($max_brightness + 1)), int(rand($max_brightness + 1))); } ###### 明るい色のランダム生成 sub random_light_color { my $min_brightness = 204; # 最低輝度 (16進数でCC) return sprintf("%02X%02X%02X", $min_brightness + int(rand(256 - $min_brightness)), $min_brightness + int(rand(256 - $min_brightness)), $min_brightness + int(rand(256 - $min_brightness))); } ###### xml用ヘッダーの出力 sub xmlhead(){ #Content-Typeの200出力設定 my $cgi = CGI->new; # ステータスコード404を設定 my $status = "200 OK"; # ヘッダーを出力 print $cgi->header( -status => $status, -type => 'application/xml', -charset => 'UTF-8' ); } ###### デバッグ用ヘッダーの出力 sub minihead(){ #Content-Typeの200出力設定 my $cgi = CGI->new; # ステータスコード404を設定 my $status = "200 OK"; # ヘッダーを出力 print $cgi->header( -status => $status, -type => 'text/html', -charset => 'UTF-8' ); } ###### 404ヘッダーの出力 sub print_header_notfound{ my $cgi = CGI->new; # ステータスコード404を設定 my $status = "404 Not Found"; # ヘッダーを出力 print $cgi->header( -status => $status, -type => 'text/html', -charset => 'UTF-8' ); } ###### 404ヘッダーの出力 sub print_header_redirect_to_domain_top { my $location = 'https://' . ($ENV{HTTP_HOST} || $ENV{SERVER_NAME}); my $cgi = CGI->new; # ステータスコード301を設定 my $status = "301 Moved Permanently"; # リダイレクト先のURL(ドメイントップ) # ヘッダーを出力 print $cgi->header( -status => $status, -location => $location, -type => 'text/html', -charset => 'UTF-8' ); } #--------------------------------------- #URL-Decode #--------------------------------------- sub decodem(){ eval{ my $cgi = new CGI(); %form = $cgi->Vars; }; while (($key, $value) = (each %form)){ $value =~ s/</g; $value =~ s/>/>/g; $form{$key} = $value; } } ####### データベースの列を取得して、このページのデータを確保するサブルーチン sub getData(){ local @multdatabasei_array = @_; #タイトルを付ける タイトルは[0,x] / 多重配列をfor文で閲覧 my $count = 0; my $point = 0; for my $row (@multdatabasei_array) { if ($count == 0){ for my $element (@$row) { chomp $element; chomp $pageName; return $point if $element eq $pageName; # 該当ディレクトリ文字列にマッチ $point++; } } $count++; } } ##### ターゲットの行を取得 sub getTargetRow(){ my $row = $_[0]; my $two_dimensional_array = $_[1]; # 二次元配列の1行目を取得 local @two_dimensional_array = @{$two_dimensional_array}; #ループ前の初期設定 local @flattened_array; local $cnt = 0; foreach my $row_ref (@two_dimensional_array) { foreach my $element (@{$row_ref}) { push @flattened_array, $element if $cnt eq $row; } $cnt++; } return @flattened_array; }