بازیابی پست‌های وبلاگ بلاگفا


تا مدتی می‌توانیم پست‌های وبلاگ‌های مسدود شده بلاگفا را به کمک اطلاعات موجود در کش گوگل دریافت کنیم. اینجا یک سری اسکریپت برای گرفتن اطلاعات یکی از وبلاگ‌هایی که به این سرنوشت مغموم دچار شده، می‌آید.

خب. فرض بر این که blog.blogfa.com آدرس وبلاگ مرحوم باشد. در این صورت آرشیو وبلاگ (لیست ماهانه مطالب) از آدرس http://blog.blogfa.com/archive.aspx قابل دسترسی بود. بنابراین از آرشیو کش شده گوگل استفاده می‌کنم: http://webcache.googleusercontent.com/search?q=cache:blog.blogfa.com/archive.aspx

آدرس پست‌های هر ماه واضح است. مثلاً blog.blogfa.com/8803.aspx یعنی مطالب خرداد ماه سال ۸۸. برای گرفتن صفحات ماه‌های اردیبهشت تا بهمن سال ۸۸ از این اسکریپت استفاده می‌کنم:

getindex.sh

#!/bin/env sh

mkdir index
for month in `seq 8802 8811`; do
	wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1" \
	"http://webcache.googleusercontent.com/search?q=cache:blog.blogfa.com/$month.aspx" -O index/$month.html
done

فایل‌هایی که در پوشه index ساخته می‌شوند؛ شبیه فایل زیر هستند:

index/8805.html

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<base href="http://blog.blogfa.com/8805.aspx">
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>  وبلاگ من</title>
</head>
<body>
<div class="page" dir="rtl" >
	<div class="post">
	    <a name="61"></a>
		<div class="title"><a href="/post-61.aspx">پست مرحوم من</a></div>
		<div class="body">
		<p>
		  متن دل‌نشین من
		</p>
		    <div class ="date">نوشته شده در دو شنبه بیست و پنجم مرداد 1388 ساعت 17:21 توسط من</div>
		</div>
	</div>
</div>
</body>
</html>

البته خیلی خیلی بزرگ‌تر و کثیف‌تر. این برای استخراح مطالب کافی نیست. چون احتمالاً فقط چند خط اول مطلب آمده و صفحه اصلی مطلب نیست. صفحه اصلی مطلب blog.blogfa.com/post-61.aspx بوده که در <div class="title"> آمده.

پس یک پارسر برای گرفتن این آدرس‌ها از صفحات آرشیو لازم است تا بعد از طریق گوگل، اصل مطلب دریافت شود. صفحات xhtml نیستند که با کمک libxml به سادگی پارس شوند؛ در واقع طبق هیچ استاندارد html هم معتبر نیستند. خوشبختانه PHP Simple HTML DOM Parser می‌تواند صفحات خیلی درهم را هم پارس کند. پس می‌توان با کمک یک اسکریپت PHP نشانی اصلی مطالب را فهمید:

getpostaddr.php

<?php
include 'simple_html_dom.php';

$dir = opendir( 'index' );
while( false !== ( $file = readdir( $dir ) ) ) {
	if( $file == '.' || $file == '..' )
		continue;
	$html = file_get_html( "data/$file" );
	foreach( $html->find( 'div[class=title] a') as $title_link )
		echo substr( $title_link->getAttribute( 'href' ), 1 ).' ';
}
?>

خروجی این اسکریپت چیزی است شبیه به: post-138.aspx post-196.aspx post-250.aspx post-310.aspx post-371.aspx ... باید نگه داشته شود:

posts=`php getpostaddr.php`

برای گرفتن مطالب از گوگل:

mkdir posts
cd !$

for post in $posts; do
	[ -s $post ] ||
	wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1" \
	"http://webcache.googleusercontent.com/search?q=cache:blog.blogfa.com/$post" -O $post
done

find . -size 0 -exec rm '{}' \;

بعد از گرفتن حدود هر ۱۰۰ صفحه، گوگل نشانی IP گیرنده را مسدود می‌کند، پس حتماً از پراکسی استفاده کنید و http_proxy قبلاً export شود.

فایل‌های posts/post-*.aspx فایل‌هایی شبیه فایل‌های صفحات فهرست (index) هستند که فقط یک پست دارند. به کمک یک پارسر دیگر عنوان، محتوی پست و تاریخ را از این فایل‌ها می‌گیرم و به صورت xml چاپ‌شان می‌کنم:

generatexml.php

<?php
include 'simple_html_dom.php';

$pdf = new IntlDateFormatter("en_US@calendar=persian", 0, 0, 'Asia/Tehran', IntlDateFormatter::TRADITIONAL, 'yyyy MM dd HH mm');
$gdf = new IntlDateFormatter("en_US", 0, 0, 'GMT',IntlDateFormatter::TRADITIONAL, 'EEE, dd MMM yyyy HH:mm:00 V');

$ri = "^نوشته شده در ";
$by = 'توسط .*$';
$week_days = "(.*شنبه)|جمعه";
$month_literals = array(
		'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور',
		'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'
		);
$number_literals = array(
		'یکم', 'دوم', 'سوم', 'چهارم', 'پنجم', 'ششم', 'هفتم', 'هشتم',
		'نهم', 'دهم', 'یازدهم', 'دوازدهم', 'سیزدهم', 'چهاردم', 'پانزدهم',
		'شانزدهم', 'هفدهم', 'هجدهم', 'نوزدهم', 'بیستم', 'سی ام'
		);

function date_to_rfc822( $local_date ) {
	global $gdf, $pdf, $ri, $by, $week_days, $month_literals, $number_literals;


	$date = preg_replace( "/$by|$ri|$week_days/", "", $local_date);
	$gar = array();

	preg_match( '/(\d{1,2}):(\d{1,2})/', $date, $gar );
	$hour = $gar[1];
	$minute = $gar[2];
	$date = preg_replace( '/ساعت .*$/', '', $date );
	preg_match( '/\d{4}$/', $date, $gar );
	$year = $gar[0];
	$months = implode( $month_literals, '|' );
	preg_match( "/$months/", $date, $gar );
	$month = array_search( $gar[0],$month_literals ) + 1;
	if ( array_search( $gar[0], $month_literals ) === False )
		die( "month $gar[0]" );

	$date = preg_replace( array("/$months/", '/\d{4}$/'), '', $date );
	$days = implode( $number_literals, '|' );
	preg_match( "/$days/", $date, $gar );
	$day = array_search( $gar[0],$number_literals ) + 1;
	if ($day == 21)
		$day = 30;
	if( preg_match( '/بیست و .*/', $date ) )
		$day += 20;
	else if ( preg_match('/سی و .*/', $date ) )
		$day += 30;
	$timestamp = $pdf->parse( "$year $month $day $hour $minute" );

	return $gdf->format($timestamp);
}


$dir = opendir('posts/');
echo "<rss>\n";

while( false !== ( $file = readdir( $dir ) ) ) {
	if( $file == '.' || $file == '..' )
		continue;

	$html = file_get_html( "posts/$file" );

	foreach ( $html->find('div[class=post]') as $post ) {
		echo "\t<item>\n";
		foreach( $post->find(' div[class=date] ' ) as $date )
			echo "\t\t<pubDate>".date_to_rfc822($date->plaintext)."</pubDate>\n";

		foreach( $post->find('div[class=title] a') as $title)
			printf("\t\t<title>%s</title>\n", $title->plaintext);

		foreach( $post->find('div[class=body]') as $body)
			printf("\t\t<content:encoded>%s</content:encoded>\n", nl2br($body->plaintext) );
		echo "\t\t</item>\n";
	}
}

echo "</rss>\n";
?>

کدهای تابع date_to_rfc822 و هر چیزی که بیرون while قرار دارند، برای تبدیل:

نوشته شده در دو شنبه بیست و پنجم مرداد 1388 ساعت 17:23 توسط من
به صورت:
Mon, 16 Aug 2010 12:53:00 GMT

به کار می‌آید. تبدیل تاریخ شمسی به میلادی و حذف نام نویسنده و مانند این‌ها که در بلاگ‌های مختلف هم فرق می‌کنند و نیاز به کد متفاوت دارند.

php generatexml.php > posts.xml
posts.xml
   
<rss>
  <item>
    <pubDate>Mon, 16 Aug 2010 12:53:00 GMT</pubDate>
    <title>پست مرحوم من</title>
    <content:encoded><p>متن دلنشین من</p></content:encoded>
  </item>
  <item>
    ..........
</rss>

این فایل را می‌توان به ورد پرس وارد کرد.