整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

多语言网站如何将网页的本地化版本录入 Google - html标记

语言网站为了让谷歌收录,更好的服务某语言本地客户搜索,可以有几种方法让谷歌收录。这集中方法包括HTML、HTTP 标头和站点地图。

本文主要介绍用html标记的方式将网站内容加入谷歌搜索收录。下面是谷歌的官方方法介绍。

HTML 标记

您可以通过在网页标头中添加 <link rel="alternate" hreflang="lang_code"... > 元素,将网页的所有语言和区域变体告知 Google。如果您没有站点地图或无法为您的网站指定 HTTP 响应标头,此方法非常有用。

对于网页的每个变体,您都应在 <head> 元素中添加一组 <link> 元素,并使每个网页变体(包括网页自身)分别对应一个链接。网页的每个版本中,这组链接都应是相同的。查看其他准则。

以下是每个 link 元素的语法:

<link rel="alternate" hreflang="lang_code" href="url_of_page" />

语法

lang_code 此网页版本所针对的受支持的语言/区域代码,或 x-default(以与该网页上的 hreflang 标记未明确列出的任何语言匹配)。

url_of_page 此网页的特定语言/区域版本对应的完全限定网址。

请将 <link> 标记放在 <head> 元素顶部附近。最起码,<link> 标记必须位于格式正确的 <head> 部分内,或位于可能会导致 <head> 过早结束的任何项目(例如 <p> 或跟踪像素)前面。如果不确定,请将所呈现的网页中的代码粘贴到 HTML 验证工具中,确保相关链接位于 <head> 元素内。

示例

例如,Widgets, Inc 有一个面向美国、英国和德国境内用户的网站。以下网址包含的内容大致相同,但具有区域性差异:

具有区域性差异的网址

http://en.example.com/page.html 通用英语版首页,包含与从美国运至全球各地的运费相关的信息。

http://en-gb.example.com/page.html 英式英语版首页,显示以英镑为单位的价格。

http://en-us.example.com/page.html 美式英语版首页,显示以美元为单位的价格。

http://de.example.com/page.html 德语首页。

Example Domain 不针对任何语言或语言区域的默认页;它具有选择器,供用户选择自己的语言和区域。

请注意,Google 不会根据这些网址中针对特定语言的子网域(en、en-gb、en-us、de)确定网页的目标受众群体;您必须将网页明确地关联到对应的目标受众群体。

您应将下面的 HTML 粘贴到上方列出的所有网页的 <head> 部分中。这会将美式英语、英式英语、通用英语和德语用户引导至对应的本地化网页,并将所有其他用户引导至通用首页。Google 搜索会根据用户的浏览器设置为他们返回适当的结果。

<head>

<title>Widgets, Inc</title>

<link rel="alternate" hreflang="en-gb"

href="http://en-gb.example.com/page.html" />

<link rel="alternate" hreflang="en-us"

href="http://en-us.example.com/page.html" />

<link rel="alternate" hreflang="en"

href="http://en.example.com/page.html" />

<link rel="alternate" hreflang="de"

href="http://de.example.com/page.html" />

<link rel="alternate" hreflang="x-default"

href="Example Domain" />

</head>

通常抓取级联数数据情况不多,但要是真需要时,确多了一些麻烦,比如抓取商品分类级别信息等。本内容将讲述如何采集无限级联联数据,并以GoldData来抓取2019年最新的省市县三级为示例。

创建数据集

在数据集管理里,添加数据集area。如下图所示:

数据集相当于数据库中的表,只是字段是灵活的,可以随着需要而添加和变更。

创建规则

在规则管理里,添加规则arearule,并将地址http://xzqh.mca.gov.cn/map 填为抓取入口地址。

我们通过分析可知,我们可从http://xzqh.mca.gov.cn/map 获取省级数据,然后通过级级数据请求http://xzqh.mca.gov.cn/selectJson 带有省级名称来获得市级数据,最后再通过市级数据分别请求http://xzqh.mca.gov.cn/selectJson 带有市级数据来获得县级数据。

并且发现请求 http://xzqh.mca.gov.cn/selectJson 是需要发POST请求的,因此我们需要将url加个前缀fake:,然后用规则里用JavaScript去请求URL。

我们在此编写的area数据集,有以下字段:

名称说明sn取区域编码作为记录唯一字段name名称code取区域编码abbr省名简写parent_code取父区域编码

因此编写规则如下:

(注:具体规则内容请见文章最后)

然后编写完成后,我们就可以启动抓取器进行抓取。

查看数据

打开数据管理,选择area数据集查看如下图所示:

导出数据

回到数据管理,选择条件,选择需要导出的字段,进行导出。像这里数据比较多,GoldData将会以打包excel文件并压缩为zip文件下载。解压到本地,然后打开excel就可以看到抓取的数据,如下图所示:

结尾

通过这节内容,可以了解了如何通过GoldData 抓取级联数据。但是下一个问题是如何将数据导入自关联列表呢,且看下一往篇

咐录:

(抓取规则)

[

{

__sample: http://xzqh.mca.gov.cn/map

match0: http\:\/\/xzqh\.mca\.gov\.cn\/map

fields0:

{

__model: true

__node: js

__js:

'''

var exp11=/json\s=\s(.+)\s+$\(doc/

var ret=exp11.exec(html)

var ss=eval(ret[1])

for(var i=0;i<ss.length;i++){

var ele=ss[i]

var exp12=/\((.+)\)/.exec(ele.shengji)

var exp13=/(.+)\(/.exec(ele.shengji)

out.add({sn:ele.quHuaDaiMa,name:exp13[1],code:ele.quHuaDaiMa,abbr:exp12[1],

parent_code:null,

})

}

'''

name:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

abbr:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

sn:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

__dataset: area

parent_code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

}

fields1:

{

__node: js

__js:

'''

var exp11=/json\s=\s(.+)\s+$\(doc/

var ret=exp11.exec(html)

var ss=eval(ret[1])

for(var i=0;i<ss.length;i++){

var ele=ss[i]

var url='fake:http://xzqh.mca.gov.cn/selectJson?shengji='+ele.shengji+"&code="+ele.quHuaDaiMa

//var $ajax(url,[__method:'POST',data:'shengji='+ele.shengji]).content

out.add({href:url})

}

'''

href:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

}

}

{

__sample: fake:http://xzqh.mca.gov.cn/selectJson?shengji=北京市(京)&code=110000

match0: fake\:http\:\/\/xzqh\.mca\.gov\.cn\/selectJson\?shengji=.+\&code=\d+

fields0:

{

__model: true

__dataset: area

sn:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

__node: js

__js:

'''

var urlRet= /http\:\/\/xzqh\.mca\.gov\.cn\/selectJson\?shengji=(.+)\&code=(\d+)/.exec(baseUri);

var shengji=urlRet[1]

var sjCode=urlRet[2]

var url='http://xzqh.mca.gov.cn/selectJson'

var content=$ajax(url,{__method:'POST',data:'shengji='+shengji }).content

var arr=eval(content);

for(var i=0;i<arr.length;i++){

var dj=arr[i];

var area={

name:dj.diji,

code:dj.quHuaDaiMa,

parent_code:sjCode,

sn:dj.quHuaDaiMa,

}

out.add(area);

}

'''

name:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

abbr:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

sn:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

parent_code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

}

fields1:

{

__node: js

__js:

'''

var urlRet= /http\:\/\/xzqh\.mca\.gov\.cn\/selectJson\?shengji=(.+)\&code=(\d+)/.exec(baseUri);

var shengji=urlRet[1]

var sjCode=urlRet[2]

var url='http://xzqh.mca.gov.cn/selectJson'

var content=$ajax(url,{__method:'POST',data:'shengji='+shengji }).content

var arr=eval(content);

for(var i=0;i<arr.length;i++){

var dj=arr[i];

var url='fake:http://xzqh.mca.gov.cn/selectJson?shengji='+shengji+"&sjcode="+sjCode+"&diji="+dj.diji+"&djcode="+dj.quHuaDaiMa

out.add({href:url})

}

'''

href:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

}

}

{

__sample: fake:href :fake:http://xzqh.mca.gov.cn/selectJson?shengji=北京市(京)&sjcode=110000&diji=北京市&djcode=110000

match0: fake\:http\:\/\/xzqh\.mca\.gov\.cn/selectJson\?shengji=([^&]+)\&sjcode=([^&]+)\&diji=([^&]+)\&djcode=([^&]+)

fields0:

{

__model: true

__dataset: area

__node: js

sn:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

__js:

'''

var urlRet=/fake\:http\:\/\/xzqh\.mca\.gov\.cn\/selectJson\?shengji=([^\&]+)\&sjcode=([^\&]+)\&diji=([^\&]+)\&djcode=([^\&]+)/.exec(baseUri)

var shengji=urlRet[1]

var sjCode=urlRet[2]

var dj=urlRet[3]

var djCode=urlRet[4]

var url='http://xzqh.mca.gov.cn/selectJson'

var content=$ajax(url,{__method:'POST',data:'shengji='+shengji+'&diji='+dj }).content

var arr=eval(content);

for(var i=0;i<arr.length;i++){

var dj=arr[i];

var area={

name:dj.xianji,

code:dj.quHuaDaiMa,

parent_code:djCode,

sn:dj.quHuaDaiMa,

}

out.add(area);

}

'''

name:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

abbr:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

sn:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

parent_code:

{

expr: ""

attr: ""

js: ""

__label: ""

__showOnList: false

__type: ""

down: "0"

accessPathJs: ""

uploadConf: ""

}

}

}

]

外的题果然考得与众不同

[secrypt_cen.html]

这次是HTML网页,然后JS加密判断

翻看JS代码

{width="5.75in" height="3.375in"}

很显然,关键的代码在checkPassword


JS混淆是必备的

去混淆一条龙走起

先将关键代码提取出来


  JavaScript
  function _0x4857(_0x398c7a, _0x2b4590) { const _0x104914 =
  _0x25ec(); _0x4857 = function (_0x22f014, _0x212d58) { _0x22f014 =
  _0x22f014 - (0x347 + 0x46a * -0x7 + 0x1cc6); let _0x321373 =
  _0x104914[_0x22f014]; return _0x321373; }; return
  _0x4857(_0x398c7a, _0x2b4590); } (function (_0x414f9c, _0x3d4799)
  {
  //...................省略大量代码
  } function safe_add(a, b) { var c = (65535 & a) + (65535 & b); return
  (a >> 16) + (b >> 16) + (c >> 16) << 16 | 65535 & c } function

  bit_rol(a, b) { return a << b | a >>> 32 - b }

使用在线的javascript去混淆即可

deobfuscate.relative.im


得到去混淆后的结果


  function checkPassword(_0x38d32a) {
  try {
  if (_0x38d32a.length !== 21) {
  return false
  }
  if (
  //......省略大量代码
  return [c, d, j, k]
  }
  function md5_cmn(a, b, c, d, e, f) {
  return safe_add(bit_rol(safe_add(safe_add(b, a), safe_add(d, f)), e),
  c)
  }
  function md5_ff(a, b, c, d, e, f, g) {
  return md5_cmn((b & c) | (~b & d), a, b, e, f, g)
  }
  function md5_gg(a, b, c, d, e, f, g) {
  return md5_cmn((b & d) | (c & ~d), a, b, e, f, g)
  }
  function md5_hh(a, b, c, d, e, f, g) {
  return md5_cmn(b ^ c ^ d, a, b, e, f, g)
  }
  function md5_ii(a, b, c, d, e, f, g) {
  return md5_cmn(c ^ (b | ~d), a, b, e, f, g)
  }
  function safe_add(a, b) {
  var c = (65535 & a) + (65535 & b)
  return (((a >> 16) + (b >> 16) + (c >> 16)) << 16) | (65535 &
  c)
  }
  function bit_rol(a, b) {
  return (a << b) | (a >>> (32 - b))

  }


flag长度21


发现了MD5加密,和两个MD5字符串


看起来无关联?

后来审计整个代码发现,对输入的flag分部分进行判断比较


写出对应的部分,在控制台console输出相关信息是一个不错的选择



  function checkPassword(_0x38d32a) {
  try {
  // Password length is 21.
  if (_0x38d32a.length !== 21) {
  return false;
  }
  if (
  _0x38d32a.slice(1, 2) !==
  (String.fromCodePoint + "")[
  parseInt((parseInt + "").charCodeAt(3), 16) - 147
  ] /* password[1] = 'o' */ ||
  _0x38d32a[(parseInt(41, 6) >> 2) - 2] !==
  String.fromCodePoint(123) /* password[4] = '{' */ ||
  _0x38d32a[4].charCodeAt(0) !==
  _0x38d32a[7].charCodeAt(0) + 72 /* password[7] = '3'. */ ||
  JSON.stringify(
  Array.from(
  _0x38d32a.slice(5, 7).split("").reverse().join(),
  (_0x2d4d73) => _0x2d4d73.codePointAt(0)
  ).map((_0x5b85c5) => _0x5b85c5 + 213)
  ) !==
  JSON.stringify([
  285, 257, 297,
  ]) /* password[5] = 'T', password[6] = 'H' password[7] =
  '3'*/
  ) {
  return false;
  }
  /* For password[8], password[9], password[10], password[11]
  */
  let _0x3c7a5c = _0x38d32a.slice(8, 12).split("").reverse();
  
  try {
  for (let _0x396662 = 0; _0x396662 < 5; _0x396662++) {
  _0x3c7a5c[_0x396662] =
  _0x3c7a5c[_0x396662].charCodeAt(0) + _0x396662 +
  getAdder(_0x396662);
  }
  } catch (_0x1fbd51) {
  _0x3c7a5c = _0x3c7a5c.map(
  (_0x24cda7) => (_0x24cda7 += _0x1fbd51.constructor.name.length -
  4)
  );
  }
  
  if (
  MD5(String.fromCodePoint(..._0x3c7a5c)) !==
  "098f6bcd4621d373cade4e832627b4f6" /* password[8] = '0',
  password[9] = 'R', password[10] = '3', password[11] = 'M'
  */
  ) {
  return false;
  }
  
  if (
  MD5(_0x38d32a.charCodeAt(12) + "") !==
  "812b4ba287f5ee0bc9d43bbf5bbe87fb" /* password[12] = '_' */
  ) {
  return false;
  }
  _0x3c7a5c = (_0x38d32a[8] + _0x38d32a[11]).split("");
  _0x3c7a5c.push(_0x3c7a5c.shift());
  if (
  _0x38d32a.substring(14, 16) !==
  String.fromCodePoint(
  ..._0x3c7a5c.map((_0x5b5ec8) =>
  Number.isNaN(+_0x5b5ec8) ? _0x5b5ec8.charCodeAt(0) + 5 : 48
  )
  ) /* password[14] = 'R' password[15] = '0' */ ||
  _0x38d32a[_0x38d32a[7] - _0x38d32a[10]] !==
  atob("dQ==") /* password[0] = 'u' */ ||
  _0x38d32a.indexOf(String.fromCharCode(117)) !==
  _0x38d32a[7] - _0x38d32a[17] /* password[17] = '3' */ ||
  JSON.stringify(
  _0x38d32a
  .slice(2, 4)
  .split("")
  .map(
  (_0x7bf0a6) =>
  _0x7bf0a6.charCodeAt(0) ^
  getAdder.name[_0x38d32a[7]].charCodeAt(0)
  )
  ) !==
  JSON.stringify(
  [72, 90].map(
  (_0x40ab0d) =>
  _0x40ab0d ^
  String.fromCodePoint.name[_0x38d32a[17] - 1].charCodeAt(0)
  )
  ) /* password[2] = 'f', password[3] = 't' */
  ) {
  return false;
  }
  if (
  String.fromCodePoint(
  ..._0x38d32a
  .split("")
  .filter(
  (_0x5edfac, _0x2965d2) => _0x2965d2 > 15 && _0x2965d2 % 2 == 0
  )
  .map(
  (_0x2ffa6d) =>
  _0x2ffa6d.charCodeAt(0) ^ (_0x38d32a.length + _0x38d32a[7])
  )
  ) !==
  atob(
  "g5Go"
  ) /* password[16] = 'V', password[18] = 'D', password[20] =
  '}' */
  ) {
  return false;
  }
  if (
  _0x38d32a[_0x38d32a.length - 2] !==
  String.fromCharCode(Math.floor((({} + "").charCodeAt(0) + 9) / 3))
  ||
  _0x38d32a[1 + _0x38d32a[7]] !== giggity()[5] /* password[19]
  = ! */
  ) {
  return false;
  }
  return true;
  } catch (_0x4d4983) {
  return false;
  }
  }
  function getAdder(_0x430c9d) {
  switch (_0x430c9d) {
  case 0:
  return 34;
  case 1:
  return 44;
  case 2:
  return 26;
  case 3:
  return 60;
  }
  return 101;
  }
  function giggity() {
  return giggity.caller.name;

  }

得到flag

uoft{TH30R3M_PR0V3D!}