Java爬取简单的网页内容和图片

Java爬取简单的网页内容和图片

根据java网络编程相关的内容,使用jdk提供的相关类可以得到url对应网页的html页面代码。
针对得到的html代码,通过使用正则表达式即可得到我们想要的内容。

比如,我们如果想得到一个网页上所有包括“java”关键字的文本内容,就可以逐行对网页代码进行正则表达式的匹配。最后达到去除html标签和不相关的内容,只得到包括“java”这个关键字的内容的效果。

从网页上爬取图片的流程和爬取内容的流程基本相同,但是爬取图片的步骤会多一步。

需要先用img标签的正则表达式匹配获取到img标签,再用src属性的正则表达式获取这个img标签中的src属性的图片url,然后再通过缓冲输入流对象读取到这个图片url的图片信息,配合文件输出流将读到的图片信息写入到本地即可。

爬虫除这两种玩法外,还有着许多的应用,比如还可以爬取一个网页上所有的邮箱、电话号码等等。

运行效果:
这些内容是我从csdn首页中根据“你”这一个关键字得到的。
Java爬取简单的网页内容和图片_第1张图片
这些是我从一些网站得到的图片
Java爬取简单的网页内容和图片_第2张图片

问题:
在看似没有问题的爬取代码背后其实还是存在着许多问题的,该程序只是对爬虫最初级的应用。比如我发现部分内容的html在浏览器的“检查”功能中明明存在,但是在网页源代码中就没有了相关的内容。还有文本匹配的正则表达式部分也存在问题。

在img src属性中,有部分图片的url是不带协议的(如:https://),还有部分图片的url是不带图片后缀(jpg、png、gif…)的。前者导致程序无法解析这个url,后者导致无法给下载的图片统一采用原命名。
有时匹配到的img src值还会出现非图片url的情况。还有时从页面上获取到的内容是unicode编码值。以及部分网站做了反爬虫处理等…

以上问题因为我对java知识、web知识、网络知识还有正则的知识了解的很少,所以我目前都无法解决。
这些问题就导致最后真正从网页上爬下来的内容,只是原网页很少的一部分。

我相信在以后这些问题都是会被逐个解决的。

另外我在程序中使用了多线程的功能,以达到可以在多个网页“同时”爬取的效果。

下面给出该程序的源代码。

编译环境:
windows jdk 9 idea

代码:
//Main.java 主类

package pers.baijiaming.download.main;

//主类
public class Main
{
    public static void main(String[] args)
    {
        createImageDownloadThread ("https://www.bilibili.com/", "downloadImg/b站/");
        createImageDownloadThread("https://cn.bing.com/images/search?q=%E7%A6%8F%E5%88%A9&qs=n&form=QBIR&sp=-1&pq=%E7%A6%8F%E5%88%A9&sc=3-2&cvid=2AF12F93CAB34E8FBF902905B185583C&first=1&scenario=ImageBasicHover", "downloadImg/福利/");

        createTextDownloadThread("https://www.csdn.net/", "你", "Text/", "testText.txt");
    }

    //创建并执行线程,获取图片
    private static void createImageDownloadThread(String url, String path)
    {
        new Thread(new DownloadPictures(url, path)).start();
    }

    //获取文本内容
    private static void createTextDownloadThread(String urlStr, String findText, String downloadPath, String fileName)
    {
        new Thread(new GetText(urlStr, findText, downloadPath, fileName)).start();
    }
}

//DownloadPictures.java 爬取图片类

package pers.baijiaming.download.main;

import java.io.*;           //io包
import java.util.regex.*;   //正则包
import java.net.*;          //网络包

/*
* 下载图片类
* */
public final class DownloadPictures implements Runnable
{
    private URL url = null;     //URL
    private URLConnection urlConn =  null;  //url连接
    private BufferedReader bufIn = null;    //缓冲读取器,读取网页信息

    private static final String IMG_REG = "]*?>";    //img标签正则
    private static final String IMG_SRC_REG = "src\\s*=\\s*\"?(.*?)(\"|>|\\s+)";    //img src属性正则

    private String downloadPath = null;     //保存路径

    //构造,参数:想要下载图片的网址、下载到的图片存放的文件路径
    public DownloadPictures(String urlStr, String downloadPath)
    {
        createFolder(downloadPath);     //创建文件夹

        try {
            url = new URL(urlStr);
            urlConn = url.openConnection();
            //设置请求属性,有部分网站不加这句话会抛出IOException: Server returned HTTP response code: 403 for URL异常
            //如:b站
            urlConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            bufIn = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        this.downloadPath = downloadPath;
    }

    //检测路径是否存在,不存在则创建
    private void createFolder(String path)
    {
        File myPath = new File(path);

        if (!myPath.exists())   //不存在则创建文件夹
            myPath.mkdirs();
    }

    //下载函数
    public void Download()
    {
        final int N = 20;    //每一次处理的文本行数,这个数越小越容易遗漏图片链接,越大效率越低 (理论上)

        String line = "";
        String text = "";

        while (line != null)            //网页内容被读完时结束循环
        {
            for(int i = 0; i < N; i++)  //读取N行网页信息存入到text当中,因为src内容可能分为多行,所以使用这种方法
                try {
                    line = bufIn.readLine();    //从网页信息中获取一行文本

                    if(line != null)            //判断防止把null也累加到text中
                        text += line;
                }
                catch (IOException e) {
                    e.printStackTrace();
                }

            //将img标签正则封装对象再调用matcher方法获取一个Matcher对象
            final Matcher imgM = Pattern.compile(IMG_REG).matcher(text);

            if(!imgM.find())    //如果在当前text中没有找到img标签则结束本次循环
                continue;

            //将img src正则封装对象再调用matcher方法获取一个Matcher对象
            //用于匹配的文本为找到的整个img标签
            final Matcher imgSrcM = Pattern.compile(IMG_SRC_REG).matcher(imgM.group());

            while (imgSrcM.find())      //从img标签中查找src内容
            {
                String imageLink = imgSrcM.group(1);    //从正则中的第一个组中得到图片链接

                print(imageLink);       //打印一遍链接

                //如果得到的src内容没有写协议,则添加上
//                if(!imageLink.matches("https://[\\s\\S]*"))       //这里有问题
//                    imageLink = "https://" + imageLink;

                print(imageLink);       //打印一遍链接

                try
                {
                    //缓冲输入流对象,用于读取图片链接的图片数据
                    //在链接的图片不存在时会抛出未找到文件异常
                    final BufferedInputStream in = new BufferedInputStream(new URL(imageLink).openStream());

                    //文件输出流对象用于将从url中读取到的图片数据写入到本地
                    //保存的路径为downloadPath,保存的图片名为时间戳+".png"
                    final FileOutputStream file = new FileOutputStream(new File(downloadPath + System.currentTimeMillis() + ".png"));

                    int temp;   //用于保存in从图片连接中获取到的数据
                    while ((temp = in.read()) != -1)
                        file.write(temp);   //将数据写入到本地路径中

                    //关闭流
                    file.close();
                    in.close();

                    //下载完一张图片后休息一会
                    try {
                        Thread.sleep(800);
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }

            //将text中的文本清空
            text = "";
        }
    }

    //run
    @Override
    public void run()
    {
        Download(); //下载函数
    }

    //打印语句
    public void print(Object obj)
    {
        System.out.println(obj);
    }
}

//GetTest.java 爬取文本内容类

package pers.baijiaming.download.main;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class GetText implements Runnable
{
    private URL url = null;     //URL
    private URLConnection urlConn =  null;  //url连接
    private BufferedReader bufIn = null;    //缓冲读取器,读取网页信息

//    private static final String TEXT_REG = "([[[^\\x00-\\xff]|\\w]&&[^u003E]])+";    //文本内容正则
    private static final String TEXT_REG = "[[\\s\\S]&&[^<>\\n ]]*";    //文本内容正则,这里有些问题,试了很久,无法排除一个空格的情况
//    <[a-zA-Z]+.*?>([\s\S]*?)

    private String findText = null;         //要查找的内容

    private String downloadPath = null;     //保存路径
    private String fileName = null;         //文件名

    //构造,参数:url、要查找的文本、保存路径,文件名
    public GetText(String urlStr, String findText, String downloadPath, String fileName)
    {
        createFolder(downloadPath);     //创建文件夹

        try {
            url = new URL(urlStr);
            urlConn = url.openConnection();
            //设置请求属性,有部分网站不加这句话会抛出IOException: Server returned HTTP response code: 403 for URL异常
            //如:b站
            urlConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            bufIn = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        this.downloadPath = downloadPath;
        this.fileName = fileName;
        this.findText = findText;
    }

    //检测路径是否存在,不存在则创建
    private void createFolder(String path)
    {
        File myPath = new File(path);

        if (!myPath.exists())   //不存在则创建文件夹
            myPath.mkdirs();
    }

    //下载函数
    private void Download()
    {
        final int N = 10;

        String line = "";
        String textStr = "";

        while (line != null)
        {
            for(int i = 0; i < N; i++)      //将N行内容追加到textStr字符串中
                try {
                    line = bufIn.readLine();

                    if(line != null)
                        textStr += line;
                }
                catch (IOException e) {
                    e.printStackTrace();
                }

            try
            {
                //将img标签正则封装对象再调用matcher方法获取一个Matcher对象
                final Matcher textMatcher = Pattern.compile(TEXT_REG + findText + TEXT_REG).matcher(textStr);

                final FileWriter fw = new FileWriter(downloadPath + fileName, true);   //文件编写,续写文件

                while (textMatcher.find())          //查找匹配文本
                {
                    fw.write(textMatcher.group() + "\n");  //写入到文件中
                    print(textMatcher.group());     //打印一遍
                }

//                print(textStr);
                fw.close();
                textStr = "";
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    //run
    @Override
    public void run()
    {
        Download();
    }

    //打印语句
    private void print(Object obj)
    {
        System.out.println(obj);
    }
}

程序中还存在着许多问题,还请多多包涵。

你可能感兴趣的:(Java)