Java爬虫之JSoup使用教程

Wesley13
• 阅读 469

title: Java爬虫之JSoup使用教程
date: 2018-12-24 8:00:00 +0800
update: 2018-12-24 8:00:00 +0800
author: me
cover: https://img-blog.csdnimg.cn/20181224144920712
tags:

  • 第三方类库
    preview: JSoup是一个用于处理HTML的Java库,它提供了一个非常方便类似于使用DOM,CSS和jquery的方法的API来提取和操作数据。

文章目录

Java爬虫之JSoup使用教程

Java爬虫之JSoup使用教程

代码下载地址

https://github.com/suveng/demo/releases/tag/jsoupDemo

实战获取githubpages的链接,并生成sitemap

介绍

JSoup是一个用于处理HTML的Java库,它提供了一个非常方便类似于使用DOM,CSS和jquery的方法的API来提取和操作数据。

jsoup实现WHATWG HTML5规范,并将HTML解析为与现代浏览器相同的DOM。

  • 从URL,文件或字符串中提取并解析HTML。
  • 查找和提取数据,使用DOM遍历或CSS选择器。
  • 操纵HTML元素,属性和文本。
  • 根据安全的白名单清理用户提交的内容,以防止XSS攻击。
  • 输出整洁的HTML。

jsoup旨在处理发现所有格式有差异的HTML; 从原始和验证,到无效的标签; jsoup将创建一个明智的解析树。

项目地址

能用Jsoup实现什么?

  • 从URL,文件或字符串中刮取并解析HTML
  • 查找和提取数据,使用DOM遍历或CSS选择器
  • 操纵HTML元素,属性和文本
  • 根据安全的白名单清理用户提交的内容,以防止XSS攻击
  • 输出整洁的HTML

文档地址

主要类

1. org.jsoup.Jsoup类

Jsoup类是任何Jsoup程序的入口点,并将提供从各种来源加载和解析HTML文档的方法。

Jsoup类的一些重要方法如下:

方法

描述

static Connection connect(String url)

创建并返回URL的连接。

static Document parse(File in, String charsetName)

将指定的字符集文件解析成文档。

static Document parse(String html)

将给定的html代码解析成文档。

static String clean(String bodyHtml, Whitelist whitelist)

从输入HTML返回安全的HTML,通过解析输入HTML并通过允许的标签和属性的白名单进行过滤。

2. org.jsoup.nodes.Document类

该类表示通过Jsoup库加载HTML文档。可以使用此类执行适用于整个HTML文档的操作。

Element类的重要方法可以参见 - http://jsoup.org/apidocs/org/jsoup/nodes/Document.html

3. org.jsoup.nodes.Element类

HTML元素是由标签名称,属性和子节点组成。 使用Element类,您可以提取数据,遍历节点和操作HTML。

Element类的重要方法可参见 - http://jsoup.org/apidocs/org/jsoup/nodes/Element.html

简单使用

安装

使用maven导包,也可以使用jar

<dependency>
  <!-- jsoup HTML parser library @ http://jsoup.org/ -->
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.10.2</version>
</dependency>

加载文档

1. URL加载文档

从URL加载文档,使用Jsoup.connect()方法从URL加载HTML。

try
{
   
   
   
    Document document = Jsoup.connect("http://www.yiibai.com").get();
    System.out.println(document.title());
} 
catch (IOException e) 
{
   
   
   
    e.printStackTrace();
}

2. 从文件加载文档

使用Jsoup.parse()方法从文件加载HTML。

try
{
   
   
   
    Document document = Jsoup.parse( new File( "D:/temp/index.html" ) , "utf-8" );
    System.out.println(document.title());
} 
catch (IOException e) 
{
   
   
   
    e.printStackTrace();
}

3. 从String加载文档

使用Jsoup.parse()方法从字符串加载HTML。

try
{
   
   
   
    String html = "<html><head><title>First parse</title></head>"
                    + "<body><p>Parsed HTML into a doc.</p></body></html>";
    Document document = Jsoup.parse(html);
    System.out.println(document.title());
} 
catch (IOException e) 
{
   
   
   
    e.printStackTrace();
}

提取数据

使用DOM方法导航文档

元素提供了一系列类似DOM的方法来查找元素,并提取和操作它们的数据。DOM getter是上下文的:在父文档上调用,他们在文档下找到匹配的元素; 他们在一个子元素上调用了那个孩子下面的元素。通过这种方式,您可以了解所需的数据。

寻找元素
  • getElementById(String id)
  • getElementsByTag(String tag)
  • getElementsByClass(String className)
  • getElementsByAttribute(String key) (及相关方法)
  • 元素的兄弟姐妹:siblingElements()firstElementSibling()lastElementSibling()nextElementSibling()previousElementSibling()
  • 图:parent()children()child(int index)
处理元素数据
  • attr(String key)获取和attr(String key, String value)设置属性
  • attributes() 获得所有属性
  • id()className()classNames()
  • text()获取和text(String value)设置文本内容
  • html()获取和html(String value)设置内部HTML内容
  • outerHtml() 获取外部HTML值
  • data()获取数据内容(例如scriptstyle标签)
  • tag()tagName()
操纵HTML和文本
  • append(String html)prepend(String html)
  • appendText(String text)prependText(String text)
  • appendElement(String tagName)prependElement(String tagName)
  • html(String value)

使用selector-syntax查找元素

使用CSS或类似jquery的选择器语法来查找或操作元素。

使用Element.select(String selector)Elements.select(String selector)方法

jsoup元素支持CSS(或jquery)之类的选择器语法来查找匹配元素,从而允许非常强大和健壮的查询。

select方法在一个可用DocumentElement或在Elements。它是上下文的,因此您可以通过从特定元素中进行选择或通过链接选择调用来进行过滤。

Select返回一个Elements列表(as Elements),它提供了一系列提取和操作结果的方法。更多选择器的语法

从元素中提取属性,文本和HTML

在解析文档并找到一些元素之后,您将需要获取这些元素中的数据。

  • Element.id()
  • Element.tagName()
  • Element.className()Element.hasClass(String className)

您有一个包含相对URL的HTML文档,您需要将其解析为绝对URL

在HTML元素中,URL通常是相对于文档的locat编写的IOn : <a href="https://my.oschina.net/download">...</a>. 当您使用该Node.attr(String key)方法获取href属性时,它将按照源HTML中的指定返回。

如果要获取绝对URL,则会有一个属性键前缀abs:,该前缀将导致根据文档的基URI解析属性值(原始位置)ION): attr("abs:href")

对于此用例,在解析文档时指定基URI很重要。

如果您不想使用abs:前缀,还有一个方法Node.absUrl(String key)可以执行相同的操作,但可以通过自然属性键进行访问。

示例程序:列出链接

public class ListLinks {
   
   
   
    public static void main(String[] args) throws IOException {
   
   
   
        Validate.isTrue(args.length == 1, "usage: supply url to fetch");
        String url = args[0];
        print("Fetching %s...", url);

        Document doc = Jsoup.connect(url).get();
        Elements links = doc.select("a[href]");
        Elements media = doc.select("[src]");
        Elements imports = doc.select("link[href]");

        print("\nMedia: (%d)", media.size());
        for (Element src : media) {
   
   
   
            if (src.tagName().equals("img"))
                print(" * %s: <%s> %sx%s (%s)",
                        src.tagName(), src.attr("abs:src"), src.attr("width"), src.attr("height"),
                        trim(src.attr("alt"), 20));
            else
                print(" * %s: <%s>", src.tagName(), src.attr("abs:src"));
        }

        print("\nImports: (%d)", imports.size());
        for (Element link : imports) {
   
   
   
            print(" * %s <%s> (%s)", link.tagName(),link.attr("abs:href"), link.attr("rel"));
        }

        print("\nLinks: (%d)", links.size());
        for (Element link : links) {
   
   
   
            print(" * a: <%s>  (%s)", link.attr("abs:href"), trim(link.text(), 35));
        }
    }

    private static void print(String msg, Object... args) {
   
   
   
        System.out.println(String.format(msg, args));
    }

    private static String trim(String s, int width) {
   
   
   
        if (s.length() > width)
            return s.substring(0, width-1) + ".";
        else
            return s;
    }
}

示例输出

Media: (38)
 * img: <http://ycombinator.com/images/y18.gif> 18x18 ()
 * img: <http://ycombinator.com/images/s.gif> 10x1 ()
 * img: <http://ycombinator.com/images/grayarrow.gif> x ()
 * img: <http://ycombinator.com/images/s.gif> 0x10 ()
 * script: <http://www.co2stats.com/propres.php?s=1138>
 * img: <http://ycombinator.com/images/s.gif> 15x1 ()
 * img: <http://ycombinator.com/images/hnsearch.png> x ()
 * img: <http://ycombinator.com/images/s.gif> 25x1 ()
 * img: <http://mixpanel.com/site_media/images/mixpanel_partner_logo_borderless.gif> x (Analytics by Mixpan.)
 
Imports: (2)
 * link <http://ycombinator.com/news.css> (stylesheet)
 * link <http://ycombinator.com/favicon.ico> (shortcut icon)
 
Links: (141)
 * a: <http://ycombinator.com>  ()
 * a: <http://news.ycombinator.com/news>  (Hacker News)
 * a: <http://news.ycombinator.com/newest>  (new)
 * a: <http://news.ycombinator.com/newcomments>  (comments)
 * a: <http://news.ycombinator.com/leaders>  (leaders)
 * a: <http://news.ycombinator.com/jobs>  (jobs)
 * a: <http://news.ycombinator.com/submit>  (submit)
 * a: <http://news.ycombinator.com/x?fnid=JKhQjfU7gW>  (login)
 * a: <http://news.ycombinator.com/vote?for=1094578&dir=up&whence=%6e%65%77%73>  ()
 * a: <http://www.readwriteweb.com/archives/facebook_gets_faster_debuts_homegrown_php_compiler.php?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+readwriteweb+%28ReadWriteWeb%29&utm_content=Twitter>  (Facebook speeds up PHP)
 * a: <http://news.ycombinator.com/user?id=mcxx>  (mcxx)
 * a: <http://news.ycombinator.com/item?id=1094578>  (9 comments)
 * a: <http://news.ycombinator.com/vote?for=1094649&dir=up&whence=%6e%65%77%73>  ()
 * a: <http://groups.google.com/group/django-developers/msg/a65fbbc8effcd914>  ("Tough. Django produces XHTML.")
 * a: <http://news.ycombinator.com/user?id=andybak>  (andybak)
 * a: <http://news.ycombinator.com/item?id=1094649>  (3 comments)
 * a: <http://news.ycombinator.com/vote?for=1093927&dir=up&whence=%6e%65%77%73>  ()
 * a: <http://news.ycombinator.com/x?fnid=p2sdPLE7Ce>  (More)
 * a: <http://news.ycombinator.com/lists>  (Lists)
 * a: <http://news.ycombinator.com/rss>  (RSS)
 * a: <http://ycombinator.com/bookmarklet.html>  (Bookmarklet)
 * a: <http://ycombinator.com/newsguidelines.html>  (Guidelines)
 * a: <http://ycombinator.com/newsfaq.html>  (FAQ)
 * a: <http://ycombinator.com/newsnews.html>  (News News)
 * a: <http://news.ycombinator.com/item?id=363>  (Feature Requests)
 * a: <http://ycombinator.com>  (Y Combinator)
 * a: <http://ycombinator.com/w2010.html>  (Apply)
 * a: <http://ycombinator.com/lib.html>  (Library)
 * a: <http://www.webmynd.com/html/hackernews.html>  ()
 * a: <http://mixpanel.com/?from=yc>  ()

实战爬取个人博客链接,并生成sitemap.xml

步骤

  • 1 确定爬取链接
  • 2 获取当前链接页面所有链接
  • 3 过滤非本域名链接
  • 4 保存当前链接,判断当前链接是否已经被保存过了(set集合),若已保存,跳过,若未保存,跳回1
  • 5 根据生成的链接,构造符合google的sitemap标准的xml文件

核心代码

入口类main.java

public class Main {
   
   
   
  public static void main(String[] args) throws IOException {
   
   
   
    Links links = new Links();
    //获取链接,并保存到links.log
    links.myblog(links);
    SiteMapXML siteMapXML = new SiteMapXML();
    siteMapXML.createSiteMap("links.log");
    //生成后删除
    FileUtils.deleteQuietly(new File("links.log"));
  }
}

link.java 实现爬取链接

/**
 * @author 苏文广 created at 2018/12/22
 * @Description: jsoup 教程 实战 爬取连接
 */
@Data
public class Links {
   
   
   

  /**
   * 我的域名
   */
  private static final String DOMAIN = "https://suveng.github.io";
  /**
   * 目标域名
   */
  private String targetDomain = "https://suveng.github.io";
  /**
   * 保存了的链接
   */
  private Set<String> saved = new HashSet<>();

  /**
   * 最终链接
   */
  String targetHref;

 public void myblog(Links jsoupMain) throws IOException {
   
   
   
  Document doc = Jsoup.connect(Links.DOMAIN+"/blog").get();
  Elements links = doc.select("a[href]");
  jsoupMain.saveLinksByElements(links);

}
  private void saveLinksByElements(Elements links) throws IOException {
   
   
   
    if (links == null) {
   
   
   
      return;
    }
    for (Element element : links) {
   
   
   
      //处理连接类型
      dealwithHref(element);
      if (!saved.contains(targetHref)) {
   
   
   
        System.out.println("\nlink : " + targetHref);
        System.out.println("text : " + element.text());
        Elements targetLink = getLinks(targetHref);
        if (targetLink == null) {
   
   
   
          return;
        }
        saveLinks();
        saveLinksByElements(targetLink);
      }
    }
  }

  /**
   * 处理连接
   */
  private void dealwithHref(Element element) {
   
   
   

    targetHref=element.attr("abs:href");
    //处理编码
    try {
   
   
   
      this.targetHref = URLDecoder.decode(targetHref, "utf-8");
    } catch (UnsupportedEncodingException e) {
   
   
   
      System.err.println("不支持的编码,建议换成utf-8");
      this.targetHref = null;
      return;
    }
    dealwithDomain();
  }

  /**
   * 判断是否是当前域名,只有当前域名才支持爬取,不支持跨域
   */
  private void dealwithDomain() {
   
   
   
    if (!this.targetHref.contains(this.targetDomain)){
   
   
   
      this.targetHref=null;
    }
  }

  /**
   * 获取链接
   * @param url targetHref
   * @return  document.select("a[href]");
   */
  private Elements getLinks(String url) {
   
   
   
    try {
   
   
   
      Connection connect = Jsoup.connect(url);
      Document document;
      document = connect.get();
      return document.select("a[href]");
    } catch (Exception e) {
   
   
   
      System.err.println("错误链接");
      return null;
    }
  }

  /**
   * 保存链接
   * @throws IOException 读写文件
   */
  private void saveLinks() throws IOException {
   
   
   
    saved.add(this.targetHref);
    File links = new File("links.log");
    FileUtils.writeStringToFile(links, this.targetHref + '\n', "utf-8", true);
  }
}

siteMapXML.java 实现构造sitemap

采用dom4j 类库,估计还会写一个关于dom4j的文章

/**
 * @author 苏文广 created at 2018/12/22
 * @Description: sitemap 生成工具类
 */
public class SiteMapXML {
   
   
   


  public void createSiteMap(String linksPath) throws IOException {
   
   
   
    Document document = DocumentHelper.createDocument();
    Element locs = document.addElement("urlset","http://www.sitemaps.org/schemas/sitemap/0.9");
    List<String> strings = FileUtils.readLines(new File(linksPath), Charset.forName("utf-8"));
    for (String url : strings) {
   
   
   
      Element loc = locs.addElement("url","http://www.sitemaps.org/schemas/sitemap/0.9");
      loc.addElement("loc").setText(url);
      loc.addElement("lastmod")
          .setText(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
    }
    writeAndFlush(document);
  }

  public void writeAndFlush(Document document) {
   
   
   
    try {
   
   
   
      OutputFormat format = OutputFormat.createPrettyPrint();
      format.setEncoding(document.getXMLEncoding());
      Writer fileWriter = new FileWriter("sitemap.xml");
      XMLWriter xmlWriter = new XMLWriter(fileWriter, format);
      xmlWriter.write(document);
      xmlWriter.close();
    } catch (IOException e) {
   
   
   
      System.err.println("导出xml失败,检查 writeandflush()");
    }
  }

  /**
   * 对xml格式化并写入文件
   */
  protected void writeFile4Pretty(File file, Document document) throws IOException {
   
   
   

    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding(document.getXMLEncoding());
    XMLWriter writer = new XMLWriter(new FileWriter(file), format);
    writer.write(document);
    writer.flush();
    writer.close();
  }

  /***
   * 格式化xml为string
   */
  protected String prettysString(Document document) throws IOException {
   
   
   
    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding(document.getXMLEncoding());
    StringWriter stringWriter = new StringWriter();
    XMLWriter writer = new XMLWriter(stringWriter, format);
    writer.write(document);
    writer.close();
    return stringWriter.toString();
  }


}

https://github.com/suveng/demo/releases/tag/jsoupDemo

实战获取githubpages的链接,并生成sitemap

参考文章

https://www.yiibai.com/jsoup

本文同步分享在 博客“suveng”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
技术小男生 技术小男生
2个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
2个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
2个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
2个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
Wesley13 Wesley13
1年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
1年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
1年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
helloworld_28799839 helloworld_28799839
2个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue