根据java网络编程相关的内容,使用jdk提供的相关类可以得到url对应网页的html页面代码。
针对得到的html代码,通过使用正则表达式即可得到我们想要的内容。
比如,我们如果想得到一个网页上所有包括“java”关键字的文本内容,就可以逐行对网页代码进行正则表达式的匹配。最后达到去除html标签和不相关的内容,只得到包括“java”这个关键字的内容的效果。
从网页上爬取图片的流程和爬取内容的流程基本相同,但是爬取图片的步骤会多一步。
需要先用img标签的正则表达式匹配获取到img标签,再用src属性的正则表达式获取这个img标签中的src属性的图片url,然后再通过缓冲输入流对象读取到这个图片url的图片信息,配合文件输出流将读到的图片信息写入到本地即可。
爬虫除这两种玩法外,还有着许多的应用,比如还可以爬取一个网页上所有的邮箱、电话号码等等。
运行效果:
这些内容是我从csdn首页中根据“你”这一个关键字得到的。
这些是我从一些网站得到的图片
问题:
在看似没有问题的爬取代码背后其实还是存在着许多问题的,该程序只是对爬虫最初级的应用。比如我发现部分内容的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]*?)[a-zA-Z]*?>
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);
}
}
程序中还存在着许多问题,还请多多包涵。