前几个月用jeesite4开发了个网站,上线运行了几个月,因为自己搞过SEO,所有,想把站点提交到百度收录,遇到了一些很尴尬的事,就是百度链接提交通过sitemap提交,一直提示XML格式错误,提示这个也就算了,问题是没有任何错误信息,无语。。
检查了好几遍,没有任何问题,上面提示着XML错误,点击去就提示网页找不到。。
本来想着反馈下问题,没想到,百度那边半个多月了没有答案。。
自己也不急,没事就等等呗,直到今天晚上,有时间了,把他解决掉了,解决思路,用的是测试法,先将百度提供的示例完整的复制一份放到服务器上,改下链接,再次提交,百度就没有提示xml格式错误了。。。出错的原因很简单。。。。就是sitemap索引文件格式有问题。。以下是我生成sitemap索引文件的格式:
问题就出在xml命名空间。。在百度的示例中,没有用命名空间。。用了会报错。这。。
关于xml格式错误的注意点:
1.最后现将百度提供的示例,复制一份,将loc改成自己的链接,测试下。
2.xml格式要utf-8格式,我用大写UTF-8测试了一下。。也报xml格式错误。。
3.示例中的lastmod说是选填,我这里如果没有的话,貌似还是会报xml格式错误。
4.还有个比较奇怪的问题就是。。xml格式的缩进。。在vim中查看下缩进。
先说下什么是sitemap?
Sitemap是站长告诉搜索引擎他们网站上可供抓取的网页有哪些。最简单的Sitemap形式就是XML文件。在其中列出网站中的网址以及关于每个网址的其他元数据(上次更新的时间、更改的频率以及相对于网站上其他网址的重要程度为何等),以便搜索引擎可以更加智能地抓取网站。
百度的链接提交总的分为两种:
1.自动提交:自动提交又分为三种,实时,自动,sitemap,我这里用的是主动推送+sitemap两种组合方式。
2.手动提交
主动推送比较简单,就是将你想要提交的链接通过POST提交到百度就行了,代码如下,比较简单,通过URLConnection提交:
/**
* 从百度站长平台获取
*/
private static String baiduApiUrl = "http://data.zz.baidu.com/urls?site=www.xxxx.com&token=xxxx";
/**
* 百度链接实时推送
* @param Parameters
* @return
*/
public static String Post(String[] Parameters){
if(null == Parameters || Parameters.length ==0){
return null;
}
String result="";
PrintWriter out=null;
BufferedReader in=null;
OutputStreamWriter outs = null;
try {
//建立URL之间的连接
URLConnection conn=new URL(baiduApiUrl).openConnection();
//设置通用的请求属性
conn.setRequestProperty("Host","data.zz.baidu.com");
conn.setRequestProperty("User-Agent", "curl/7.12.1");
conn.setRequestProperty("Content-Length", "83");
conn.setRequestProperty("Content-Type", "text/plain");
//发送POST请求必须设置如下两行
conn.setDoInput(true);
conn.setDoOutput(true);
//获取conn对应的输出流
outs = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
//发送请求参数
String param = "";
for(String s : Parameters){
param += s+"\n";
}
//进行输出流的缓冲
outs.write(param); // post的关键所在
outs.flush();
//通过BufferedReader输入流来读取Url的响应
in=new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while((line=in.readLine())!= null){
result += line;
}
System.out.println(result);
} catch (Exception e) {
System.out.println("发送post请求出现异常!"+e);
e.printStackTrace();
} finally{
try{
if(out != null){
out.close();
}
if(in!= null){
in.close();
}
}catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
请求成功之后,返回如下数据:
{
"remain":4999998,
"success":2
}
注意,我这里用的是Sitemap索引文件而不是普通的xml,区别如下:https://ziyuan.baidu.com/college/courseinfo?id=267&page=2#04
百度Sitemap协议支持三种格式:文本格式、xml格式、Sitemap索引格式,可以根据自己情况来选择任意一种格式组织sitemap。具体格式说明及示例如下:
在一个txt文本列明需要向百度提交的链接地址,将txt文本文件通过站长平台进行提交
http://www.example.com/repaste/101562698_5230191316.html
http://www.example.com/repaste/101586283_5230215075.html
http://www.example.com/repaste/101639435_5230310576.html
此文本文件需要遵循以下指南:
· 文本文件每行都必须有一个网址。网址中不能有换行。
· 不应包含网址列表以外的任何信息。
· 您必须书写完整的网址,包括 http。
· 每个文本文件最多可包含 50,000 个网址,并且应小于10MB(10,485,760字节)。如果网站所包含的网址超过 50,000 个,则可将列表分割成多个文本文件,然后分别添加每个文件。
· 文本文件需使用 UTF-8 编码或GBK编码。
单个xml数据格式如下:
http://www.yoursite.com/yoursite.html
2009-12-14
daily
0.8
http://www.yoursite.com/yoursite2.html
2010-05-01
daily
0.8
上述Sitemap向百度提交了一个url:http://www.yoursite.com/yoursite.html
若有多条url,按照上述格式重复
如需提交大量sitemap文件,则可将其列在sitemap索引文件中,然后将该索引文件提交,您无需分别提交每个文件。
http://example.com/ext/xmlsitemap/add/201201/index_20120106.xml
2009-12-14
有多个Sitemap,按上述格式重复
注意:
第一,一个Sitemap文件包含的网址不得超过 5 万个,且文件大小不得超过 10 MB。如果您的Sitemap超过了这些限值,请将其拆分为几个小的Sitemap。这些限制条件有助于确保您的网络服务器不会因提供大文件而超载。
第二,一个站点支持提交的sitemap文件个数必须小于5万个,多于5万个后会不再处理,并显示“链接数超”的提示。
第三,如果验证了网站的主域,那么Sitemap文件中可包含该网站主域下的所有网址。
在JAVA中创建xml格式的文件有以下几种方式:
1.手动拼接文件,组装格式,写入文件
2.通过jdom创建
3.通过sitemapgen4j
我这里提供2,3方式的代码,添加如下依赖:
com.github.dfabulich
sitemapgen4j
1.1.1
org.jdom
jdom
1.1.3
public void createSiteMap(){
String WEBSITE = "http://www.xxxx.com";
Element urlset = new Element("urlset");
Document document = new Document(urlset);
List cList;
if(cList == null || cList.size() < 1 ){
return ;
}
int i=1;
for (String category : cList) {
System.out.println(category + "生成中..." + i);
i++;
//
Element url = new Element("url");
//
Element loc = new Element("loc");
loc.setText(WEBSITE + "/seo/" + category +"/sitemap.xml");
url.addContent(loc);
urlset.addContent(url);
}
XMLOutputter XMLOut = new XMLOutputter();
try {
Format f = Format.getPrettyFormat();
//default=UTF-8
f.setEncoding("UTF-8");
XMLOut.setFormat(f);
String path = "D:\\tmp\\sitemap\\sitemap_index.xml";
XMLOut.output(document, new FileOutputStream(path));
} catch (Exception e) {
e.printStackTrace();
}
}
sitemapgen4j方式
URL类
@XmlRootElement(name = "url")
public class Url {
private String ioc;
public Url() {
}
public Url(String ioc) {
super();
this.ioc = ioc;
}
@XmlElement(name = "ioc")
public String getIoc() {
return ioc;
}
public void setIoc(String ioc) {
this.ioc = ioc;
}
}
Sitemap类
@XmlRootElement(name = "sitemap")
public class Sitemap {
private String ioc;
public Sitemap() {
}
public Sitemap(String ioc) {
super();
this.ioc = ioc;
}
@XmlElement(name = "ioc")
public String getIoc() {
return ioc;
}
public void setIoc(String ioc) {
this.ioc = ioc;
}
}
工具类
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import javax.xml.bind.DataBindingException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
public class SiteMapUtils {
protected final static String URLSET_START = "\n"
+ "\n";
protected final static String URLSET_END = "\n ";
protected final static String SITEMAPINDEX_START = "\n"
+ "\n";
protected final static String SITEMAPINDEX_STARTS = "\n"
+ "\n";
protected final static String SITEMAPINDEX_END = "\n ";
public static void writeSitemapIndex(Writer writer, Iterator extends Sitemap> mappings) throws IOException {
writeXml(writer, SITEMAPINDEX_STARTS, mappings, SITEMAPINDEX_END);
}
public static long writeUrlset(Writer writer, Iterator urls) throws IOException {
return writeXml(writer, URLSET_START, urls, URLSET_END);
}
private static long writeXml(Writer writer, String start, Iterator> it, String end) throws IOException {
writer.write(start);
long count = writeSubtree(writer, it);
writer.write(end);
return count;
}
public static long writeSubtree(Writer writer, Iterator> it) throws IOException {
long size = 0;
Marshaller m;
try {
JAXBContext jc = JAXBContext.newInstance(Sitemap.class, Url.class);
m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
} catch (PropertyException e) {
throw new DataBindingException(e);
} catch (JAXBException e) {
throw new DataBindingException(e);
}
boolean first = true;
while (it.hasNext()) {
if (first) {
first = false;
} else {
writer.write("\n");
}
try {
m.marshal(it.next(), writer);
} catch (JAXBException e) {
throw new IOException(e);
}
size++;
}
return size;
}
}
/**
* 动态生成sitemap
* 目录,web根目录,就是你的站点首页 index.html所在同级目录
*/
public void createSiteMap(){
try {
long start = System.currentTimeMillis();
String WEBSITE = "http://www.xxxx.com";
String realPath = "D:\\tmp\\";
List cList = findAllCategory();
if(cList == null || cList.size() < 1 ){
return ;
}
//存放子sitemap路径的list集合
List siteMapList = Lists.newArrayList();
for(Category category:cList){
/** 查询每个分类下对应的文章 **/
System.out.println("查询每个分类下对应的文章" + category.getId());
List aList = findArticleBy(category.getId());
if(aList != null && aList.size() > 0){
String path = realPath + category.getId();
//判断文件夹是否存在,不存在则创建
fileExists(path);
WebSitemapGenerator sitemapGenerator = WebSitemapGenerator.builder(WEBSITE, new File(path)).gzip(false).build();
//遍历取出来的文章
for(Article article:aList){
//文章详情页的url地址
String url = StringUtils.join("http://www.xxxx.com/detail/",article.getId(),".html");
WebSitemapUrl sitemapUrl = new WebSitemapUrl.Options(url).lastMod(formatDate(article.getAddTime(), DATE_FORMAT)).priority(0.9).changeFreq(ChangeFreq.HOURLY).build();
sitemapGenerator.addUrl(sitemapUrl);
}
sitemapGenerator.write();
//主索引文件需要这个路径
siteMapList.add(WEBSITE + "/seo/" + category.getId() +"/sitemap.xml");
}
}
//其实分类 - list 页 url也应该存一个sitemap,看自己吧,如果也想告诉搜索引擎,我的list列表页也很重要,那么就做
/** 生成主索引文件 **/
if(siteMapList!=null && siteMapList.size()>0){
Collection s = new ArrayList();
for(String url:siteMapList){
s.add(new Sitemap(url));
}
Writer wt = new PrintWriter(new File(realPath + "/sitemap.xml"));
SiteMapUtils.writeSitemapIndex(wt, s.iterator());
wt.close();
}
long end = System.currentTimeMillis();
System.out.println("生成Sitemap完毕, 共耗时:"+(end-start));
} catch (Exception e) {
e.printStackTrace();
}
}
这样就会在seo目录下创建一个sitemap.xml总的索引文件,链接到a/b/c啥啥分类目录,每个分类目录下有个sitemap.xml用来记录该分类下的所有链接。