页面内容分析之404页面检测
先来看看机器人怎么说:
要检测一个网页是否是404页面,你可以尝试以下方法:
HTTP状态码检查:发送HTTP请求并检查返回的状态码。如果网页是404页面,服务器将返回404状态码。你可以使用编程语言或工具(如Python的requests库)发送HTTP请求,并检查返回的状态码是否为404。
页面内容检查:发送HTTP请求并获取网页内容。然后,检查页面内容是否包含404相关的文本或关键词。通常,404页面会包含类似于"404"、"页面未找到"、"Not Found"等关键词。你可以使用编程语言或工具对获取的页面内容进行文本匹配或关键词搜索,以确定是否为404页面。
网页截图比对:使用自动化测试工具,如Selenium,可以模拟浏览器行为打开网页并截取页面截图。然后,你可以比对该截图与预期的404页面截图进行比对。如果它们相似度很高,则可能是404页面。
这些方法可以根据你的需求和技术背景进行选择和实施。需要注意的是,有些网页可能会使用JavaScript动态生成内容,这可能需要更高级的技术来处理。
什么是404界面
狭义的404界面就是http响应码返回404或503等不可达值的网页
广义的来说,404界面是一个统称,指的是协议404或者内容404
404页面有哪些特征
1、返回的httpcode是404/503/401等不可达code
2、title里面有”不存在“、”不可达“等关键词
3、内容里面有“不存在”、“找不到”等关键词
4、链接被重定向到了特定的404或503域名或页面
5、对于单个网站来说,所有的404页面结构比较统一
检测方案
针对上述404页面的特征,我们需要逐个实现
响应码返回404/503等
public Boolean checkHttpStatusCode(String code) {
if (code.equals("404") || code.equals("503")) {
return true;
}
return false;
}
title里面有”不存在“、”不可达“等关键词
首先,我们使用jsoup库解析html,然后得到title标签里面的内容
public static String getHtmlTitle(String html){
try {
// 使用 Jsoup 解析 HTML
Document doc = Jsoup.parse(html);
// 获取 <title> 标签
Element titleElement = doc.select("title").first();
// 检查是否存在 <title> 标签并获取其内容
if (titleElement != null) {
String title = titleElement.text();
return title;
} else {
return "";
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
拿到title之后,我们再进行比对即可,利用java里面的contains方法即可
内容里面有“不存在”、“找不到”等关键词
首先需要获取到html里面的内容区,在没有使用智能提取技术之前,我们可以先去除标签获取内容,但是这确实是会带来很大的误报
import org.apache.commons.lang3.StringEscapeUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HtmlUtil {
public static String delHTMLTag(String htmlStr) {
String regEx_script = "<script[^>]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式
String regEx_style = "<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式
String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
Pattern p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
Matcher m_script = p_script.matcher(htmlStr);
htmlStr = m_script.replaceAll(""); //过滤script标签
Pattern p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
Matcher m_style = p_style.matcher(htmlStr);
htmlStr = m_style.replaceAll(""); //过滤style标签
Pattern p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
Matcher m_html = p_html.matcher(htmlStr);
htmlStr = m_html.replaceAll(""); //过滤html标签
return htmlStr.trim();
}
public static String htmlTextFormat(String htmlText) {
htmlText = htmlText
.replaceAll("(\\\\n)+", " ")
.replaceAll("(\\\\t)+"," ")
.replaceAll("(\t)+"," ")
.replaceAll("(\n)+"," ");
htmlText = htmlText.replaceAll(" +"," ");
htmlText = htmlText.replaceAll(" +"," ");
return htmlText;
}
public static String getContent(String html) {
String ans = "";
try {
html = StringEscapeUtils.unescapeHtml4(html);
html = delHTMLTag(html);
html = htmlTextFormat(html);
return html;
} catch (Exception e) {
e.printStackTrace();
}
return ans;
}
}
上面的getContent方法便是我们获取到html内容的方法。
链接被重定向到了特定的404或503域名或页面
需要传入url,并对url进行解析
public class UrlPattern {
private String pattern; // 正则
private String description; // 描述
private String location; // 位置
}
private Boolean checkOneUrlPattern(UrlPattern urlPattern, String url) {
URL url1 = null;
try {
url1 = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (Objects.isNull(url1)) return false;
Pattern pattern = Pattern.compile(urlPattern.getPattern());
String val = "";
if (urlPattern.getLocation().equalsIgnoreCase("path")) {
val = url1.getPath();
} else if (urlPattern.getLocation().equalsIgnoreCase("query")) {
val = url1.getQuery();
} else if (urlPattern.getLocation().equalsIgnoreCase("host")) {
val = url1.getHost();
}
if (!StringUtils.hasText(val))return false;
Matcher matcher = pattern.matcher(val);
if (matcher.find()) {
return true;
}
return false;
}
UrlPattern 是定义的一套检测规则的对象类,以下是大概的格式
- pattern: '\b(404|503)\b'
description: '检测链接的域名部分包含404或503'
location: 'host'
上述规则检测域名的host部分中是否有404或者503
对比页面结构,判定是否为404页面
这里有两个方案
方案一:自建404页面结构库,让用户调用时与我们的自建库进行比对
方案二:用户自己传入404页面和一个正常页面,我们利用页面结构相似度比较算法来确定是否为404页面
先来看下页面结构的算法
public class PageStructUtil {
public static List<String> getAllLabelsFromHtml(String html) {
Document document = Jsoup.parse(html);
Elements elements = document.getAllElements();
List<String> elementList = new ArrayList<>();
for (Element element : elements) {
elementList.add(element.nodeName());
}
return elementList;
}
/**
* a 基于 base的相似性
*
* @param a
* @param base
* @return
*/
public static Double pageStructScore(List<String> a, List<String> base) {
SequenceUtils<String> stringSequenceUtils = new SequenceUtils<>();
Integer length = stringSequenceUtils.getLongestCommonSequence(a, base);
int n = base.size();
int m = a.size();
// TODO: 2020/5/25 定义:页面结构的相似度为 (2.0*公共序列的长度)/(旧的公共序列的长度+新的公共序列的长度)
Double score = (2 * length) / ((n + m) * 1.0);
return score;
}
public static void main(String[] args) {
}
}
页面结构算法我在专栏的另外一篇博客中有专门讲解,请大家移步观看
然后,我们来看看调用的方法
/**
* 自出404页面的页面结构分析
*
* @param user404Html
* @param html
* @param score
* @return
*/
public Boolean checkUserPage404Struct(String user404Html, String html, Double score) {
try {
return PageStructUtil.pageStructScore(PageStructUtil.getAllLabelsFromHtml(user404Html), PageStructUtil.getAllLabelsFromHtml(html)) >= score;
} catch (Exception e) {
}
return false;
}
优化迭代
检测配置化
由于上述写法中我们都是将检测的东西写死在程序中,不具有可扩展性,所以我们需要对此进行扩展,扩展的思路便是对于所有判断性条件都做成配置形式的,而不是走特判。
例如,我们可以对链接里面有404或503的编写如下的配置
rule_name: '检测链接的域名部分包含404或503'
url_patterns:
- pattern: '\b(404|503)\b'
description: '检测链接的域名部分包含404或503'
location: 'host'
rule_name: '检测到路径中包含404或503'
url_patterns:
- pattern: '\/(?:.*\/)?(404|503)\/'
description: '检测到路径中包含404或503'
location: 'path'
rule_name: '检测到查询字符串中包含404或503'
url_patterns:
- pattern: '[?&](?:[^&=]+=[^&=]+&)*(?:[^&=]+=(404|503))(?:&|$)'
description: '检测到查询字符串中包含404或503'
location: 'query'
给title添加如下检测配置
404
503
页面未找到
服务不可用
错误
错误页面
访问被拒绝
权限拒绝
页面不存在
链接不存在
不存在
Not Found
http响应码的配置
404
503
401
400
500
页面结构的配置
rule_name: 'pan.baidu.com'
rules:
- struct: '#document-html-head-body-meta-title-link-link-style-style-style-div-header-a-nav-a-span-div-a-em-div-div-a-span-span-a-span-span-a-span-span-a-span-span-a-span-span-a-span-span-a-span-span-a-span-span-iframe-div-ul-li-a-li-em-span-div-iframe-em-div-span-a-span-span-a-span-a-span-a-span-a-span-a-span-a-span-a-section-div-div-div-div-h3-div-p-p-p-div-p-a-p-a-style-div'
score: '0.8'
name: '404页面'
- struct: '#document-html-head-body-title-meta-meta-meta-meta-meta-meta-meta-meta-link-link-link-link-link-link-link-link-link-style-link-link-link-link-style-style-style-style-div-div-div-div-dl-dt-a-dd-span-a-span-span-a-span-span-a-span-span-a-span-dl-dt-dd-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-span-a-p-dd-i-i-dd-span-span-i-i-span-a-i-dl-dt-i-i-span-span-i-i-span-span-a-p-i-span-dd-div-a-div-a-a-a-a-a-ul-li-a-li-a-li-a-li-a-span-span-li-a-li-a-i-a-a-i-a-div-div-div-span-a-div-span-dd-a-div-section-section-div-aside-dl-div-dl-div-a-p-p-a-p-p-a-p-p-a-p-p-div-div-div-img-div-section-link-div-div-a-a-a-a-a-a-a-style-div'
score: '0.8'
name: '链接失效页面'
接下来做的事儿
1、找到一堆404页面,得到其结构、title,响应码特征,丰富我们的上述配置
如何快速获取大量的404页面?可以找到一堆域名,然后添加一系列后缀(绝不可能存在的),得到的页面大概率就是404页面
2、对页面内容做智能解析提取,进而得到内容区的标题和正文,再进行进一步的分析。