专注于技术分享
   工具共享。

java爬虫基础分析-教你爬取网站的高清壁纸

阅读(400)

Hellohao/26bd40418045150.png

说到爬虫,大家都知道Python,但是惭愧博主对Python不是很精通,就不能爬虫了?答案肯定是错误的,作为强大的编译性语言。java无所不能。下面简单介绍一下通过java简单实现爬虫。

就以爬取壁纸站的壁纸为例子

演示站:https://www.socwall.com

所需要的的依赖

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.13.1</version>
</dependency>

实现过程

首先需要分析网页的url结构:

他的页码结构是:https://www.socwall.com/wallpapers/page:2/

那为了方便爬虫,我们可以把第一页(首页)写成:https://www.socwall.com/wallpapers/page:1/

站点的源码结构:

页面图片列表的每个图片的主体部分如下结构:

  <ul class="wallpaperList">
   <li class="wallpaper wallpaper87010">
    <div>
     <a class="image" href="/desktop-wallpaper/87010/wallpaper-by-unknown-artist/" target="_blank"><img style="width: 290px; height: 260px;" src="/images/wallpapers/87010-290x260.jpg" />
      <div class="meta" style="top: 260px;">
       <h2 class="title">Wallpaper</h2>
       <p class="author">Unknown Artist</p>
      </div></a>
     <ul class="controls left">
      <li class="processing"></li>
      <li class="flagWallpaper"><a onclick="Socwall.showFlagWallpaperTip($(this), 87010);" title="Flag"></a></li>
     </ul>
     <ul class="controls right">
      <li class="vote voteDown"><a onclick="Socwall.rate(87010, 'down', $(this));" title="Vote Down"></a></li>
      <li class="vote voteUp"><a onclick="Socwall.rate(87010, 'up', $(this));" title="Vote Up"></a></li>
      <li class="favorite"><a onclick="Socwall.toggleFavorite(87010, $(this));" title="Save to Favorites"></a></li>
      <li class="rating">+5</li>
     </ul>
    </div></li>
   <li class="wallpaper wallpaper87009">
    <div>
     <a class="image" href="/desktop-wallpaper/87009/wallpaper-by-unknown-artist/" target="_blank"><img style="width: 290px; height: 260px;" src="/images/wallpapers/87009-290x260.jpg" />
      <div class="meta" style="top: 260px;">
       <h2 class="title">Wallpaper</h2>
       <p class="author">Unknown Artist</p>
      </div></a>
     <ul class="controls left">
      <li class="processing"></li>
      <li class="flagWallpaper"><a onclick="Socwall.showFlagWallpaperTip($(this), 87009);" title="Flag"></a></li>
     </ul>
     <ul class="controls right">
      <li class="vote voteDown"><a onclick="Socwall.rate(87009, 'down', $(this));" title="Vote Down"></a></li>
      <li class="vote voteUp"><a onclick="Socwall.rate(87009, 'up', $(this));" title="Vote Up"></a></li>
      <li class="favorite"><a onclick="Socwall.toggleFavorite(87009, $(this));" title="Save to Favorites"></a></li>
      <li class="rating">+5</li>
     </ul>
    </div></li>
  </ul>

图片详情页面主要代码:

<a class="wallpaperImageLink wallpaperLink" href="/images/wallpapers/87010-1920x1080.jpg" target="_blank">
    <img src="/images/wallpapers/87010-1920x1080.jpg" alt="Wallpaper" style="max-width: 1705px; max-height: 908px;">
</a>

通过以上代码我可以得出。每个图片的class="image" a标签href属性就是这张图片原图的地址。

那我们的思路就很明确了:遍历每页的各个图片。分别得到他们的href,进入原图页面后再提取原图img标签的src链接即可。

实现

我们首先需要分析怎么获取到每张图片的href

public static void getImgUrl(String url){
    try {
        URL u = new URL(url);
        Document doc = Jsoup.parse(u, 5000);
        Elements img = doc.getElementsByClass("wallpaperList").select("a.image");
        for (Element element : img) {
            System.out.println(element.attr("href"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

关键代码说明:其实Document对象的用法和js差不太多,但是还是有区别的:

Elements img = doc.getElementsByClass("wallpaperList").select("a.image");

我上面这句代码的意思是说:获取class为wallpaperList下所有class为image的a标签。

拿到所有我们想要的a标签之后,挨个遍历获取它的href属性即可:element.attr("href")

OK,我们那第一页的链接做一下测试,如下图:

Hellohao/8c3890418042522.png

然后访问我们获取的原图地址,获取原图的链接:

这里我先随便哪一个上边获取的地址做个测试

URL u = null;
try {
    u = new URL("https://www.socwall.com/desktop-wallpaper/87010/wallpaper-by-unknown-artist/");
    Document doc = Jsoup.parse(u, 5000);
    Elements imglink = doc.getElementsByClass("wallpaperImageLink").select("img");
    for (Element element : imglink) {
        System.out.println("高清壁纸获取成功:https://www.socwall.com"+element.attr("src"));
    }
} catch (Exception e) {
    e.printStackTrace();
}

结果如下图:

Hellohao/ef5bd0418043618.png

所有的爬虫核心逻辑完成了。

测试

下边我稍微整合了一下代码。集中测试一下吧。

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Hellohao
 * @version 1.0
 * @date 2020-04-18 15:29
 */
public class Demo {
    public static void main(String[] args) {
        //此循环为页码。标识爬取前10页的所有图
        for (int i = 1; i <=10 ; i++) {
            System.out.println("正在爬取第 "+i+" 页:");
            String url = "https://www.socwall.com/wallpapers/page:"+i;
            List<String> list = getImgUrl(url);
            for (String s : list) {
                URL u = null;
                try {
                    u = new URL("https://www.socwall.com"+s);
                    Document doc = Jsoup.parse(u, 5000);
                    Elements imglink = doc.getElementsByClass("wallpaperImageLink").select("img");
                    for (Element element : imglink) {
                        System.out.println("高清壁纸获取成功:https://www.socwall.com"+element.attr("src"));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //获取每页所有图片的原图url
    public static List<String> getImgUrl(String url){
        List<String> list = new ArrayList<>();
        try {
            URL u = new URL(url);
            Document doc = Jsoup.parse(u, 5000);
            Elements img = doc.getElementsByClass("wallpaperList").select("a.image");
            for (Element element : img) {
                //System.out.println(element.attr("href"));
                list.add(element.attr("href"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    return list;
    }

}

结果

Hellohao/96cf40418044114.png

— END —-

java获取指定文件的类型

阅读(1495)

某些情况下我们需要鉴别一个文件是什么类型的,此次演示一下如何获取文件的类型,非根据后缀名判断,及时没有后缀名也可以鉴别。

(更多…)

SpringBoot简单配置Spring Quartz定时器

阅读(1995)

在实际开发中,定时器使我们很多开发场景中必不可少的。比如我们需要每隔一个时间段执行一次操作。java再带的定时任务局限性太大。有了Quartz定时器用起来会方便很多。

(更多…)

java实现layui分页前后端完整实例

阅读(5010)

layui一直备受国内web开发者的喜欢。使用简单,样式也较为好看。本次说一下layui分页的实例,首先先引入layui,那是肯定的。

  • 效果:
    Hellohao图床
  • 引入一个maven依赖:
    pagehelper这个插件很方便,让我们写分页sql时只写查询就行。自动封装分页的一些sql,如:自动增加imit,自动count总数等。
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.10</version>
        </dependency>
  • 来看页面如何写:
    很简单,写一个table标签就行.
    需要注意的是:我在script标签中加入了th:inline="none",这里说明一下,由于我的项目是SpringBoot项目,用的模板引擎是thymeleaf所以加上th:inline="none" 就代表此区域不使用thymeleaf表达式。因为[[]]符号会冲突。没有使用的不用添加,会报错。

    <table id="dates" lay-filter="test"></table>
    
    <script type="text/javascript" th:inline="none">
        layui.use('table', function(){
        var table = layui.table;
                    table.render({
                elem: '#dates'
                ,url:'/admin/selectusertable'
                ,limit:5 //默认为10
                ,cols: [[
                    {field:'id', width:50, title: '编号', sort: true} //sort:true页面可进行排序操作
                    ,{field:'username',  title: '用户名'}
                    ,{field:'email',width:170,  title: '邮箱', sort: true}
                    ,{field:'password',width:170,  title: '密码'}
                    ,{field:'birthder',width:170,  title: '注册时间'}
                    ,{field:'level',width:100,  title: '等级',templet: function(d){
                        var str = '';
                        if(d.level==1){str='普通用户';}
                        else if(d.level==2){str='管理员';}
                            return str;
                        }
                        }
                    ,{field:'level',width:100,  title: '操作',templet: function(d){
                            return '<a class="layui-btn layui-btn-xs layui-btn-danger" onclick="dele('+d.id+');">删除</a>';
                        }
                    }
                ]]
                ,page: true
                ,done:function(res, curr, count){
                    //数据的回调用,可不写
                }
            });
        });
    </script>
  • 接下来时Controller实现:

        //获取用户信息列表
    @RequestMapping(value = "/selectusertable2")
    @ResponseBody
    public Map<String,Object>selectByFy12(HttpSession session, @RequestParam(required=false,defaultValue="1") int page,
                                          @RequestParam(required=false) int limit) {
        // 使用Pagehelper传入当前页数和页面显示条数会自动为我们的select语句加上limit查询
        // 从他的下一条sql开始分页
        PageHelper.startPage(page, limit);
        List<User> users  = userService.getuserlist();// 这是我们的sql
            // 使用pageInfo包装查询
        PageInfo<User> rolePageInfo = new PageInfo<>(users);//
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("code",0);
        map.put("msg","");
        map.put("count",rolePageInfo.getTotal());
        map.put("data",rolePageInfo.getList());
        return map;
    }
  • SQL语句就简单多了:
    什么都不用加,直接写查询就行,插件会自动帮你做limit或者count

     
     SELECT * FROM user order by birthder desc