02
11 月

Java 正则表达式匹配网页链接地址

Java 正则表达式匹配网页链接地址

在网络爬虫中,HTML网页文件解析是重要的一环,原来实现数据的清洗和提取,Jsoup作为一款解析器被广泛的使用,但我在使用时总遇到一些莫名其妙的问题,就改用正则表达式来解析。

解析过程中链接地址提取必不可少,但格式多样性就会有许多问题,主要集中在a标签内引号的使用及链接地址相对和绝对的写法上。

1. <a>标签引号的使用

先看下面几种写法

<a href="http://xinyo.org/">雪痕的博客</a>
<a href = 'http://xinyo.org/'>雪痕的博客</a>
<a href= http://xinyo.org/>雪痕的博客</a>

上面三种写法中有双引号、单引号和空格,但目前各种浏览器都能正常解析,所以可以匹配为:

href\\s?=\\s?(['\"]?)([^'\">\\s]+)\\1[>\\s]

以第一种写法为例解析,其中

  • href\\s?=\\s?匹配href=
  • (['\"]?)匹配双引号
  • ([^'\">\\s]+)匹配链接地址http://xinyo.org/
  • \\1为反向引用(['\"]?)
  • [>\\s]匹配链接后的空格或标签闭合符号

2. 链接相对路径与绝对路径

链接相对路径与绝对路径依靠其开头区分,主要如下:

  • http(s)://开头为绝对路径,无需处理
  • //双斜杠开头也是绝对路径,会自动继承当前的http/https协议
  • /单斜杠开头为相对路径,继承自域名根目录
  • ../开头为相对路径,继承自当前页面上上级目录
  • ./开头为相对路径,继承自当前页面上级目录
  • 其他开头,即字母数字开头为相对路径,继承自当前页面目录

3. Java 正则表达式匹配网页链接地址

package org.xinyo;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ParseContext {

  public static synchronized void newUrls(String url, String strHTML) throws IOException {
    // 解析网页内容,从中提取链接
    int d = Crawler.depthMap.get(url);
    Matcher aTag = Pattern.compile("(<a\\s[^>]+>)").matcher(strHTML);
    while (aTag.find()) {
      Matcher links = Pattern.compile("href\\s?=\\s?(['\"]?)([^'\">\\s]+)\\1[>\\s]").matcher(aTag.group());

      while (links.find()) {
        String newUrl = links.group(2);
//        System.out.println(newUrl);
        // url地址预处理
        String urlStart = url.startsWith("https") == true ? "https" : "http";// 协议头
        String urlRoot = url.replaceAll("^(http(s)?://[^/]+)(/.*)?$", "$1");// 域名
        while (!newUrl.startsWith("http")) {// 查找http标记
          if (newUrl.startsWith("//")) {// 双斜杠开头不为相对链接
            newUrl = urlStart + ":" + newUrl;
          } else if (newUrl.startsWith("/")) {// 单斜杠开头指向根域名
            newUrl = urlRoot + newUrl;
            // 相对链接处理
          } else if (newUrl.startsWith("../")) {
            newUrl = url.replaceAll("(.+/)[^/]+/[^/]+$", "$1") + newUrl.replace("../", "");
          } else if (newUrl.startsWith("./")) {
            newUrl = url.replaceAll("(.+/)[^/]+$", "$1") + newUrl.replace("./", "");
          } else {
            newUrl = url.replaceAll("(.+/)[^/]+$", "$1") + newUrl;
          }
        }
        // System.out.println(newUrl+"\r\n");
        if (!Crawler.allUrlSet.contains(newUrl)) {
          AddUrl.add(newUrl, d + 1);// 加入一个新的url
          while (Crawler.threadWaitting > 0) { // 如果有等待的线程,则唤醒(可以唤醒因意外休眠未唤醒的进程)
            synchronized (Crawler.signal) { //
              Crawler.threadWaitting--;
              Crawler.signal.notify();
            }
          }
        }
      }
    }
  }
}

没有评论