بهینه سازی سئو سایت های ASP.NET MVC

بهینه سازی سئو سایت های ASP.NET MVC

برای اکثر سایت ها، نتایج جستجوی گوگل مهمترین منبع ایجاد ترافیک برای آنهاست. این به این معنی است که محتوای وب نقش مهمی در بالا بردن رتبه ی سایت در نتیجه جستجوهای موتورهای جستجو را دارد.

در این پست ما قصد داریم برخی از تکنیک های سئو را در پروژه ی ASP.Net MVC خود به کار ببریم تا صفحات به راحتی توسط گوگل و سایر موتورهای جستجو پیدا و ایندکس شود.

به عنوان مثال برشی از نتیجه یک جستجو توسط گوگل را در زیر می بینید که با استفاده از میکروفرمت schema.org به عنوان یک تکنیک سئو برای جلب توجه در نتیجه جستجوها استفاده شده است:

یا ممکن است شما هنگام جستجو عکس نویسنده و نام نویسنده را در کنار نتیجه جستجو ببینید:

این پست آموزش بهینه کردن سایت نوسط ASP.NET MVC می باشد.

کلاه سفیدها و کلاه سیاه های سئو:

از بسیاری از جهات، سئو شبیه میدان نبرد است. گوگل همیشه در تلاش است تا بهترین جستجوها برای کاربران را در بالاتر از سایر نتیجه جستجوها قرار دهد. (با تبلیغات در گوگل هم می توان در بالای نتایج جستجو قرار گرفت). گوگل با استفاده از الگوریتم های خاص و محاسبات خاص بهترین نتایج را پیدا می کند و این الگوریتم ها همیشه در حال تغییر و به روز رسانی می باشند. درک این الگوریتم ها و دستکاری نتایج جستجو یک کسب و کار بزرگ در دنیای تجارت بنام بهینه سازی سئو تبدیل شده است. برای سئو بهینه سازی کلاه سفید() و کلاه سیاه
Black-hat(white-hat) وجود دارد(البته خاکستری).

بهینه سازی کلاه سیاه سئو (مانند دنبال روی شرکت ها در انجمن ها در بستر اینترنت یا بحث در وبلاگ ها با درج لینک به سایت شما یا دنبال روی در توییتر و فیسبوک ) خطرناک است: چون اگر گوگل همیشه سایت شما را ببیند ممکن است در بعضی مواقع آن را از صفحه اول نتیجه جستجو حذف کند یا ممکن است یا ممکن است سایت شما به یک شی مسخره عمومی تبدیل شود(در بیشتر مباحث غیر مرتبط پیدا می شود). همچنین اینکار باعث فریب کاربران می شود.

توصیه ی ما این است که از هرگونه استفاده از روش های سئو که باعث اسپم شدن شدن سایت شما می شود بپرهیزید.

اگر محتوای سایت شما خوب باشد، اجرای صحیح اساس سئو به اندازه کافی برای حضور مطالب در صفحه اول نتایج جستجو کافی است. شما باید از قبل در مورد مخاطبان خود در دنیای اینترنت فکر کنید و اینکه چگونه آنها را به سایت خود جذب و تبدیل به مشتری کنید. اگر اساس سئو خواسته شما را برآورده نکرد تبلیغات را نیز در نظر داشته یاشد.

مباحثی که در این آموزش خواهیم دید:

  • ایجاد آدرس سئوی دوستانه(SEO friendly Slug) برای بهینه سازی آدرس ها (URL)
  • اضافه کردن کلمات کلیدی و خواص <meta> به تمام صفحات
  • مشخص کردن یک آدرس استاندارد یکتا برای همه مطالب
  • استفاده از schema.org یا میکروفرمت RDFa برای بهینه سازی نمایش نتایج جستجو
  • اضافه کردن عکس نویسنده برای نتایج جستجو
  • ایجاد یک robots.txt
  • ایجاد یک sitemap.xml (نقشه سایت)
  • استفاده از ابزار وب مستر گوگل به بررسی نتایج

پایگاه داده

ابتدا، ما نیاز به افزودن ستون به پایگاه داده برای ذخیره فیلدهای سئو داریم. چون ما از FluentMigrator استفاده کرده ایم، ما اینکار را با افزودن فیلدهای جدید به کلاس Migration انجام می دهیم تا به صورت خودکار در زمان اجرای برنامه به پایگاه داده اضافه شود. نام جدول ContentPage خواهد بود. این جدول شامل فیلد “Slug” نیز می باشد که این فیلد شامل آدرس بهینه برای سئو خواهد بود که از عنوان مطالب مشتق خواهد شد.این آدرس در رتبه سئو بسیار مهم است. همچنین ما یک فیلد متنی کلیدواژه ها(Keywords)، یک فیلد متنی دیگر برای ذخیره شناسه گوگل پلاس نویسنده(GooglePlusId)، فیلد برای ذخیره تاریخ ویرایش(Modified) و یک فیلد بولی برای اینکه مطلب توسط موتور جستجو مشاهده شود یا نه(NoSearch).با استفاده از یک تریگر، زمان ویرایش به صورت خودکار تنظیم خواهد شد.

ContentPage.cs

using FluentMigrator;
using System;
 
namespace Auction.Web.Domain.Migrations
{
    [Migration(3)]
    public class ContentPage : Migration
    {
        public override void Up()
        {
            Create.Table("ContentPage")
                .WithColumn("Id").AsInt32().NotNullable().PrimaryKey().Identity()
                .WithColumn("Slug").AsString(120).NotNullable().Indexed("IX_slug")
                .WithColumn("Title").AsString(150).NotNullable()
                .WithColumn("Author").AsString(100).Nullable()
                .WithColumn("SortOrder").AsFloat().NotNullable().WithDefaultValue(0.0f)
                .WithColumn("Markdown").AsString(8000).Nullable();
 
 
            Execute.Sql("SET IDENTITY_INSERT [ContentPage] ON");
            Insert.IntoTable("ContentPage").Row(new { Id = 1, Slug = "about", Title = "About this site", Author = "Reza", MarkDown = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
Quisque posuere laoreet dui sit amet euismod. Morbi laoreet laoreet justo eu auctor. Suspendisse facilisis dui non elit blandit faucibus. Proin nec malesuada augue. Quisque ut sollicitudin mi. 
Ut eleifend quam a massa gravida scelerisque. Cras commodo, risus a fermentum tristique, mi nibh vulputate dui, id rhoncus quam sem vitae orci. Sed et eros condimentum, dictum urna sit amet, imperdiet massa. 
Nunc sed fermentum nibh, ut tristique augue. Curabitur in nisl a purus luctus porta. Sed tellus augue, hendrerit scelerisque enim sed, euismod facilisis purus. 
Integer sed leo nec tellus elementum egestas ac ut magna. Donec risus libero, cursus quis mauris ut, tristique porttitor nulla.
 
[More information](http://www.8np.ir)" });
 
            Execute.Sql("SET IDENTITY_INSERT [ContentPage] OFF");
        }
 
        public override void Down()
        {
            Delete.Table("ContentPage");
        }
 
    }
}

SeoMigration.cs

using FluentMigrator;
 
namespace Auction.Web.Domain.Migrations
{
    [Migration(5)]
    public class Seo : Migration
    {
        public override void Down()
        {
            Execute.Sql(@"drop trigger SetContentPageModified");
            Delete.Column("Modified").FromTable("ContentPage");
            Delete.Column("Keywords").FromTable("ContentPage");
            Delete.Column("GooglePlusId").FromTable("ContentPage");
            Delete.Column("NoSearch").FromTable("ContentPage");
        }
 
        public override void Up()
        {
            Alter.Table("ContentPage")
                .AddColumn("Modified").AsDateTime().WithDefault(SystemMethods.CurrentUTCDateTime)
                .AddColumn("Keywords").AsString(120).Nullable()
                .AddColumn("GooglePlusId").AsString(30).Nullable()
                .AddColumn("NoSearch").AsBoolean().WithDefaultValue(false);
 
            Execute.Sql(@"update ContentPage set NoSearch = 0, Modified = getutcdate(), GooglePlusId = '109374356863565148764'");
 
            Execute.Sql(@"create trigger SetContentPageModified on ContentPage for update as
begin
    if not update(Modified)
    begin
        update ContentPage
        set Modified=getutcdate()
        from ContentPage inner join inserted i
        on ContentPage.Id = i.Id
    end
end"
                );
        }
    }
}

مدل مربوط به ContentPage را در زیر می بینید(این مدل بر روی پایگاه داده سمت سرور نگاشت می شود).

ContentPage.cs

using SqlFu;
using System;
using System.ComponentModel.DataAnnotations;
 
namespace Auction.Web.Domain.Entities
{
    [Table("ContentPage", PrimaryKey = "Id", AutoGenerated = true)]
    public class ContentPage : Entity
    {
        [Required]
        [RegularExpression(@"^[a-z0-9-]+$")]
        [Display(Name = "SEO friendly url: only lowercase, number and dash (-) character allowed")]
        public string Slug { get; set; }
 
        [Required]
        public string Title { get; set; }
        
        [Required]
        public string Author { get; set; }
        
        [RegularExpression(@"^[0-9]*$")]
        [Display(Name = "Google+ Id of the author (for picture in search results)")]
        public string GooglePlusId { get; set; }
        
        [Required]
        public float SortOrder { get; set; }
 
        [Display(Name = "Comma-separated list of keywords for search engines")]
        public string Keywords { get; set; }
 
        [Required]
        [Display(Name = "Content in Markdown format")]
        public string Markdown { get; set; }
        
        [Display(Name = "Last Modified On")]
        [QueryOnly]
        public DateTime Modified { get; set; }
 
        [Required]
        [Display(Name = "Exclude this content from search engine results")]
        public bool NoSearch { get; set; }
    }
}

همچنین، ما فیلدهای ویرایشی را به قسمت ادمین اضافه کردیم تا بتوانیم مطالب را اضافه کنیم. صفحه ویرایش به شکل زیر می باشد:

خواص متا

اکنون ما می توانیم View مربوط به صفحه مطالب را ایجاد کنیم. ما از فیلدهای اضافی برای کمک به گوگل برای ایندکس مطالب و نمایش بهینه صفحه استفاده می کنیم.

صفحه _Layout دارای یک بخش(section) به نام “meta” خواهد بود که در قسمت <head> صفحه قرار خواهد گرفت.در این بخش تمام خواص متای مربوط به هر صفحه که در زیر می بینید را قرار خواهیم داد:

  • Description: این متا تگ شامل خلاصه ی کوتاهی از مطلب می باشد. ما نمی خواهیم این تک را به صورت دستی وارد کنیم، پس این تک ۵۰ کاراکتر اول مربوط به مطلب خواهد بود.
  • خواص og: عمدتا این خواص تگ ها توسط فیسبوک هنگام به اشتراک گذاری مطالب استفاده میشود(برای دریافت نام نویسنده، خلاصه ای از مطلب و تصویر مطلب و …).به نظر می رسد گوگل نیز برای اشتراک مطالب در گوگل پلاس از آنها استفاده می کند.
  • <link rel=”canonical”>:این خاصیت خیلی مهم است و از آن برای جلوگیری از دریافت مطالب از محتوای تکراری توسط گوگل استفاده می شود. گوگل مطالب تکراری را دوست ندارد و رتبه آنها را کاهش می دهد.با این حال، گوگل آدرس های http و https را به شکل آدرس های متفاوت می بیند.همچنین جستجوی گوگل حساس به حروف می باشد. یعنی آدرس /Page/About را با آدرس /page/about متفاوت می بیند. ASP.Net MVC به طور پیشفرض غیر حساس به حروف می باشد. برای جلوگیری از آن، ما نیاز به مشخص کردن یک آدرس یکتا برای هر صفحه می باشیم. آدرس هایی واجد شرایط هستند که با http:// یا https:// شروع شود.
  • برای آدرس سرورهای خارجی از تنظیمات برنامه web.config استفاده کنید. با این روش شما می توانید مطمئن شوید آدرس متعارف همان است، حتی اگر از DNS های متعددی استفاده کنید(domain.com و
    www.domain.com).

تصویر نویسنده

گوگل از میکروفرمت های پشتیبانی می کند که آنها را در نتیجه جستجوی خود نشان می دهد. میکروفرمت ها ویژگی های اضافی در نشانه گذاری هستند که معنایی را مشخص می کنند. مثلا برای نمایش رتبه محصول یک کالا به شکل ستاره در نتیجه جستجو و یا نمایش هزینه کالا در کنار کالای جستجو شده. در وبلاگها و نشریات دیگر، گوگل قادر به نمایش تصویر نویسنده در کنار پیوند می باشد. گوگل این کارها را با استفاده از میکروفرمت ها انجام می دهد. همچنین گوگل از میکروفرمت های بسیاری پشتیبانی می کند.

برای نمایش تصویر نویسنده در کنار محتوای مطالب، ما باید چند کار را انجام بدهیم:

  • راه اندازی یک پروفایل گوگل پلاس
  • اطلاعات پروفایل خود را تکمیل کنید(همچنین تصویری را که می خواهد در جستجو به نمایش درآید، نیز وارد کنید.) و خود را در یک یا چند حلقه(Circles) اضافه نمایید.
  • ثبت مشخصات خود به عنوان صاحب یک سایت یا وبلاگ در وب مستر گوگل
  • لینک از مطالب خود به پروفایل گوگل خود با یک نحو خاص: <a href=”https://plus.google.com/[googleplusid]?rel=”author”>Google+</a>. استفاده از rel=author بسیار مهم است، چون گوگل نویسنده مطلب را با آن تشخیص می دهد.

View مربوط به مطالب به شکل زیر خواهد شد:

ContentPage.cshtml

@using Auction.Web.Utility
@using Auction.Web.Utility
@model Auction.Web.Domain.Entities.ContentPage
 
@{
    ViewBag.Title = Model.Title;
    ViewBag.Description = Model.Markdown.Substring(0, Math.Min(Model.Markdown.Length - 1, 50));
}
 
@section meta {
    
    
    
    
    
    
    
}
 
@Html.Markdown(Model.Markdown)

اگر شما تمام مراحل را به درستی طی کرده باشید، تصویر شما بعد از چند روز در کنار نتایج جستجو نمایش داده خواهد شد.

ROBOTS.TXT و Sitemap.xml

قبل از ایندکس سایت، رباتهای جستجو به یک فایل متنی به نام ROBOTS.TXTنگاه می کنند. در این فایل مالک سایت مشخص می کند که کدام قسمت های سایت نمی تواند توسط موتورهای جستجو، مورد جستجو قرار گیرد و کدام قسمت ها می تواند. محدود کردن جستجو نکردن دارای محدودیت های است، این محدودیت سبب اختراع نقشه سایت شد. نقشه سایت یک فایل XML است. مالک سایت با استفاده از نقشه سایت به ربات های جستجو کمک می کند که تا بتوانند به راحتی مطالب را پیدا کرده و ایندکس نمایند. این کار با ارائه لینک مربوط به مطالب در نقشه سایت صورت می گیرد. همچنین نقشه سایت ممکن است شامل زمان بعدی به روزرسانی نقشه سایت استفاده کرد، همچنین اهمیت نسبی صفحات با اعداد مابین ۰ و ۱ نمایش داده می شود.

ما از این دو فایل در پروژه استفاده خواهیم کرد. ابتدا ما روترها رو ثیت می کنیم:

RouteConfig.cs

using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace Auction.Web
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapHubs();
 
            routes.MapRoute(
                name: "sitemap.xml",
                url: "sitemap.xml",
                defaults: new { controller = "Site", action = "SitemapXml" },
                namespaces: new[] { "Auction.Web.Controllers" }
            );
 
            routes.MapRoute(
                name: "robots.txt",
                url: "robots.txt",
                defaults: new { controller = "Site", action = "RobotsText" },
                namespaces: new[] { "Auction.Web.Controllers" }
            );
 
            routes.MapRoute(
                name: "googleid.html",
                url: "google{id}.html",
                defaults: new { controller = "Site", action = "Google" },
                namespaces: new[] { "Auction.Web.Controllers" }
            );
 
            routes.MapRoute(
                name: "ContentPage",
                url: "Page/{slug}",
                defaults: new { controller = "Page", action = "ContentPage" },
                namespaces: new[] { "Auction.Web.Controllers" }
            );
 
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                namespaces: new[] { "Auction.Web.Controllers" }
            );
        }
    }
}

مطمئن شوید که در فایل web.config مقدار مقابل را وارد کرده باشید: <modules runAllManagedModulesForAllRequests=”true”>.

در غیر اینصورت نمی توان از روترها استفاده کرد. اکنون SiteController.cs را پیاده سازی می کنیم:

SiteController.cs

using Auction.Web.Domain.Queries;
using Auction.Web.Models;
using Auction.Web.Utility;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.Mvc;
using System.Xml.Linq;
 
namespace Auction.Web.Controllers
{
    [AllowAnonymous]
    public class SiteController : BaseController
    {
        private const string SitemapsNamespace = "http://www.sitemaps.org/schemas/sitemap/0.9";
 
        public ActionResult AllowCookies(string ReturnUrl)
        {
            CookieConsent.SetCookieConsent(Response, true);
            return RedirectToLocal(ReturnUrl);
        }
 
        public ActionResult NoCookies(string ReturnUrl)
        {
            CookieConsent.SetCookieConsent(Response, false);
            // if we got an ajax submit, just return 200 OK, else redirect back
            if (Request.IsAjaxRequest())
                return new HttpStatusCodeResult(System.Net.HttpStatusCode.OK);
            else
                return RedirectToLocal(ReturnUrl);
        }
 
 
        [OutputCache(Duration = 60 * 60 * 24 * 365, Location = System.Web.UI.OutputCacheLocation.Any)]
        public ActionResult FacebookChannel()
        {
            return View();
        }
 
        [OutputCache(Duration = 60 * 60 * 24, Location = System.Web.UI.OutputCacheLocation.Any)]
        public FileContentResult RobotsText()
        {
            var content = new StringBuilder("User-agent: *" + Environment.NewLine);
 
            if (string.Equals(ConfigurationManager.AppSettings["SiteStatus"], "live", StringComparison.InvariantCultureIgnoreCase))
            {
                content.Append("Disallow: ").Append("/Account" + Environment.NewLine);
                content.Append("Disallow: ").Append("/Error" + Environment.NewLine);
                content.Append("Disallow: ").Append("/signalr" + Environment.NewLine);
 
                // exclude content pages with NoSearch set to "true"
                var items = Query(new GetSeoContentPages(noSearch: true));
                foreach (var item in items)
                {
                    content.Append("Disallow: ").Append(Url.Action("ContentPage", "Page", new { area = "", slug = item.Slug })).Append(Environment.NewLine);
                }
                content.Append("Sitemap: ").Append("https://").Append(ConfigurationManager.AppSettings["HostName"]).Append("/sitemap.xml" + Environment.NewLine);
 
            }
            else
            {
                // disallow indexing for test and dev servers
                content.Append("Disallow: /" + Environment.NewLine);
            }
 
 
            return File(
                    Encoding.UTF8.GetBytes(content.ToString()),
                    "text/plain");
        }
 
        [NonAction]
        private IEnumerable GetSitemapNodes()
        {
            List nodes = new List();
 
            nodes.Add(new SitemapNode(this.ControllerContext.RequestContext, new {  area = "", controller = "Home", action = "Index"} ) 
            { 
                Frequency = SitemapFrequency.Always,
                Priority = 0.8
            });
 
            var items = Query(new GetSeoContentPages(false));
            foreach (var item in items)
            {
                nodes.Add(new SitemapNode(this.ControllerContext.RequestContext, new { area = "", controller = "Page", action = "ContentPage", id = item.Slug })
                {
                    Frequency = SitemapFrequency.Yearly,
                    Priority = 0.5,
                    LastModified = item.Modified
                });
            }
 
            return nodes;
        }
 
        [NonAction]
        private string GetSitemapXml()
        {
            XElement root;
            XNamespace xmlns = SitemapsNamespace;
 
            var nodes = GetSitemapNodes();
 
            root = new XElement(xmlns + "urlset");
 
 
            foreach (var node in nodes)
            {
                root.Add(
                new XElement(xmlns + "url",
                    new XElement(xmlns + "loc", Uri.EscapeUriString(node.Url)),
                    node.Priority == null ? null : new XElement(xmlns + "priority", node.Priority.Value.ToString("F1", CultureInfo.InvariantCulture)),
                    node.LastModified == null ? null : new XElement(xmlns + "lastmod", node.LastModified.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")),
                    node.Frequency == null ? null : new XElement(xmlns + "changefreq", node.Frequency.Value.ToString().ToLowerInvariant())
                    ));
            }
 
            using (var ms = new MemoryStream())
            {
                using (var writer = new StreamWriter(ms, Encoding.UTF8))
                {
                    root.Save(writer);
                }
 
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }
 
 
        [HttpGet]
        [OutputCache(Duration = 24 * 60 * 60, Location = System.Web.UI.OutputCacheLocation.Any)]
        public ActionResult SitemapXml()
        {
            Trace.WriteLine("sitemap.xml was requested. User Agent: " + Request.Headers.Get("User-Agent"));
 
            var content = GetSitemapXml();
            return Content(content, "application/xml", Encoding.UTF8);
        }
 
        public ActionResult Google(string id)
        {
            if (ConfigurationManager.AppSettings["GoogleId"] == id)
                return View(model: id);
            else
                return new HttpNotFoundResult();
        }
    }
}

SitemapNode.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
 
namespace Auction.Web.Models
{
    public class SitemapNode
    {
        public string Url { get; set; }
        public DateTime? LastModified { get; set; }
        public SitemapFrequency? Frequency { get; set; }
        public double? Priority { get; set; }
 
 
        public SitemapNode(string url)
        {
            Url = url;
            Priority = null;
            Frequency = null;
            LastModified = null;
        }
 
        public SitemapNode(RequestContext request, object routeValues)
        {
            Url = GetUrl(request, new RouteValueDictionary(routeValues));
            Priority = null;
            Frequency = null;
            LastModified = null;
        }
 
        private string GetUrl(RequestContext request, RouteValueDictionary values)
        {
            var routes = RouteTable.Routes;
            var data = routes.GetVirtualPathForArea(request, values);
 
            if (data == null)
            {
                return null;
            }
 
            var baseUrl = request.HttpContext.Request.Url;
            var relativeUrl = data.VirtualPath;
 
            return request.HttpContext != null &&
                   (request.HttpContext.Request != null && baseUrl != null)
                       ? new Uri(baseUrl, relativeUrl).AbsoluteUri
                       : null;
        }
 
 
 
    }
 
    public enum SitemapFrequency
    {
        Never,
        Yearly,
        Monthly,
        Weekly,
        Daily,
        Hourly,
        Always
    }
}

بعد از اجرا ROBOTS.TXT به صورت زیر است:

همچنین نقشه سایت نیز به شکل زیر خواهد شد:

برای دریافت شناسه گوگل باید سایت خود را گوگل وب مستر ثبت کنید.این شناسه در web.config در قسمت AppSetting وارد کنید تا گوگل سایت شما را تایید کند. این کار توسط اکشن گوگل در SiteController انجام می شود. شما با این کار به گوگل ثابت می کنید که مالک سایت خودتان هستید. حال شما می توانید سایت خود را مدیریت کنید. گوگل خطاهای مربوط به ROBOTS و نقشه سایت را نشان می دهد.

منبع:

  1. macaw
  2. github
+8
-1


هرگونه انتشار مطالب اختصاصي و محصولات اين سايت بجز با درج لينک مستقيم شرعا حرام بوده و پيگرد قانوني دارد.
طبق ماده 12 فصل سوم قانون جرائم رايانه هرگونه کپي برداري ممنوع بوده و پيگرد قانوني دارد. براي اطلاعات بيشتر کليک کنيد.

درباره‌ی رضا

کارشناس نرم افزار. با هفت سال سابقه برنامه نویسی. در خدمت شما دوستان عزیز...

۶ دیدگاه ها

  1. از وب سایت فوق العادتون و نویسنده های حرفه ایتون تشکر ویژه دارم – من مطالب زیادی از سایتتون یاد گرفتم – وظیفه دونستم برای یکبار هم که شده ازتون تشکر کنم – همیشه موفق باشید .

    • ما هم از شما کاربر گرامی که سایت ما رو انتخاب کردید کمال تشکر را داریم و امیدواریم بتوانیم همچنان رضایت شما را تا بقای سایت داشته باشیم.

  2. خیلی اموزنده بود مطلبتون. ممنون از سایت خوبتون

دیدگاه خود را برای سئو سایت بنویسید انصراف از پاسخ

آدرس پست الکترونیک شما منتشر نخواهد شد.خانه های ضروری نشانه گذاری شده اند. *

*


+ سه = 8

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>