java从网页抓取数据( 如何通过WebCollector抓取到内容,?, )
优采云 发布时间: 2022-04-06 14:36java从网页抓取数据(
如何通过WebCollector抓取到内容,?,
)
前言
上一篇文章我们简单讲了如何通过WebCollector来抓取内容,但这不符合我们的工作需要。在工作过程中,我们通常会抓取某个网页列表下的详情页数据,所以不能简单的从某个列表页抓取数据,需要跳转到详情页进行二次抓取数据. 好了,废话不多说,我们先从代码开始讲解如何操作。
爬取列表信息
假设我们抓取搜东首页显示文章的所有细节。如下图所示。
第一步,我们不忙于创建爬虫,我们先分析一下我们需要爬取的网站的结构。根据我们需要的网页的URL地址的特点,写出正确的正则表达式。如下所示。
http://www.jianshu.com/p/700e01a938ce
我编写的匹配 文章 URL 的正则表达式如下所示。
"http://www.jianshu.com/p/.*"
第二步,正则表达式写好后,我们需要分析一下我们抓取到的网站的结构和标签。以谷歌浏览器为例。打开开发者模式的控制台。(Mac 常用的 F12 键 ++ alt +i,这里不再赘述),假设我们需要抓取 文章 的标题和内容。我们首先选择“选择工具”(快捷键:command +shift +c),然后点击标题,此时控制台中会出现标签信息。如下所示。
我们发现title的标签h1的Class就是title,如下图。
标题已经准备好了。接下来,我们来看看内容。经过以上步骤,我们发现内容在很多标签中。这时候我们只需要获取父类标签中的所有内容即可。
对了,首页的列表页也是如上做的。如下图,所有的超链接都是一个标签。
第三步,我们还是创建一个爬虫类JianshuCrawler,继承自AbstractCrawler,然后在初始化方法中配置我们的正则表达式和我们需要爬取的网站*敏*感*词*。其他一些配置如下。
private final static String crawlPath = "/Users/luying/data/db/sports";
private final static String seed = "http://www.jianshu.com/u/e39da354ce50";
private final static String regexRuleString = "http://www.jianshu.com/p/.*";
public JianshuCrawler() {
super(crawlPath, false);
CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");
addSeed(datum);
this.addRegex(regexRuleString);
setThreads(2);
}
在访问方法中,我们需要做两种处理,一种是爬取文章列表,另一种是爬取文章详情页的内容。所以我们需要用详情页URL的正则表达式来区分文章详情页和列表首页,结构如下。
@Override
public void visit(Page page, CrawlDatums next) {
if (page.matchUrl(regexRuleString)) {
//详情页会进入这个模块
} else {
//列表首页会进入这个模块
}
}
通过第二步的分析,我们知道列表页需要将所有超链接符合正则表达式的a标签添加到爬取序列中。具体操作如下。
Elements aBodys = page.select("a");
for (int i = 0; i < aBodys.size(); i++) {
Element aElement = aBodys.get(i);
logger.debug("url=" + aElement.attr("abs:href"));
String regEx = regexRuleString;
if (aElement.attr("abs:href").matches(regEx)) {
CrawlDatum datum = new CrawlDatum(aElement.attr("abs:href")).meta("depth", "1").meta("refer",
page.url());
next.add(datum);
} else {
System.out.println("URL不匹配!!");
}
}
对于详情页的逻辑,我们需要抓取对应元素的内容,可以使用类名(表单示例:div.xxx)或id名(表单示例:div[id = xxx]),这里没有id,所以我们直接使用类名,代码如下。
String title = page.select("h1.title").text();
String content = page.select("div.show-content").text();
我们创建一个 main 函数来创建我们的爬虫对象。然后,我们需要设置爬取深度,因为我们只需要跳转到页面一次,所以我们的爬取深度是 2. 最后运行这个类。如下图所示。
public static void main(String[] args) {
JianshuCrawler crawler = new JianshuCrawler();
crawler.start(2);
}
这样,我们就会在控制台中得到我们想要的数据。当然,对数据的处理,这里就不过多解释了。如下图所示。
爬虫类的完整代码如下所示。
public class JianshuCrawler extends AbstractCrawler {
private static Logger logger = LoggerFactory.getLogger(JianshuCrawler.class);
private final static String crawlPath = "/Users/luying/data/db/sports";
private final static String seed = "http://www.jianshu.com/u/e39da354ce50";
private final static String regexRuleString = "http://www.jianshu.com/p/.*";
public JianshuCrawler() {
super(crawlPath, false);
CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");
addSeed(datum);
this.addRegex(regexRuleString);
setThreads(2);
}
@Override
public void visit(Page page, CrawlDatums next) {
if (page.matchUrl(regexRuleString)) {
String title = page.select("h1.title").text();
String content = page.select("div.show-content").text();
System.out.println("标题 " + title);
System.out.println("内容 " + content);
} else {
Elements aBodys = page.select("a");
for (int i = 0; i < aBodys.size(); i++) {
Element aElement = aBodys.get(i);
logger.debug("url=" + aElement.attr("abs:href"));
String regEx = regexRuleString;
if (aElement.attr("abs:href").matches(regEx)) {
CrawlDatum datum = new CrawlDatum(aElement.attr("abs:href")).meta("depth", "1").meta("refer",
page.url());
next.add(datum);
} else {
System.out.println("URL不匹配!!");
}
}
}
}
public static void main(String[] args) {
JianshuCrawler crawler = new JianshuCrawler();
crawler.start(2);
}
}
抓取图像信息
刮图片更简单。假设我们仍然爬取搜东首页中所有显示的图片。如下所示。
爬虫类的基本步骤就不过多解释了。先说一下访问方面的处理。我们需要网页中标签的属性来判断。只有html属性和图片属性的标签可以是图片标签。所以我们首先得到如下图的标签名。
String contentType = page.response().contentType();
然后判断标签中收录的名称。如下所示执行不同的操作。
if (contentType == null) {
return;
} else if (contentType.contains("html")) {
// 如果是网页,则抽取其中包含图片的URL,放入后续任务
} else if (contentType.startsWith("image")) {
// 如果是图片,直接下载
}
标签是网页属性的处理,我们需要把里面的图片地址链接取出来,为接下来的处理做准备。代码如下。
Elements imgs = page.select("img[src]");
for (Element img : imgs) {
String imgSrc = img.attr("abs:src");
next.add(imgSrc);
}
如果是图片标签,我们可以直接取图片的字节数据存储。代码如下。
String extensionName = contentType.split("/")[1];
String imageFileName = imageId.incrementAndGet() + "." + extensionName;
File imageFile = new File(downloadDir, imageFileName);
try {
FileUtils.write(imageFile, page.content());
System.out.println("保存图片 " + page.url() + " 到 " + imageFile.getAbsolutePath());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
因为,标签类型可能是网页,也就是说,我们需要跳转到下一页。这时候我们在main函数中创建了一个对象,需要设置爬取深度为2.
public static void main(String[] args) throws Exception {
JianshuImageCrawler crawler = new JianshuImageCrawler();
crawler.start(2);
}
图片的存储过程就不过多解释了。通过运行,我们可以在对应的存储路径下找到我们的图片。如下所示。
整体代码如下所示。
package com.infosports.yuqingmanagement.crawler.impl;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import cn.edu.hfut.dmic.webcollector.model.CrawlDatum;
import cn.edu.hfut.dmic.webcollector.model.CrawlDatums;
import cn.edu.hfut.dmic.webcollector.model.Page;
import cn.edu.hfut.dmic.webcollector.plugin.berkeley.BreadthCrawler;
import cn.edu.hfut.dmic.webcollector.util.FileUtils;
import cn.edu.hfut.dmic.webcollector.util.RegexRule;
public class JianshuImageCrawler extends BreadthCrawler {
// 用于保存图片的文件夹
File downloadDir;
// 原子性int,用于生成图片文件名
AtomicInteger imageId;
private final static String crawlPath = "/Users/luying/data/db/jianshu";
private final static String downPath = "/Users/luying/data/db/jianshuImage";
private final static String seed = "http://www.jianshu.com/u/e39da354ce50";
RegexRule regexRule = new RegexRule();
public JianshuImageCrawler() {
super(crawlPath, false);
downloadDir = new File(downPath);
if (!downloadDir.exists()) {
downloadDir.mkdirs();
}
computeImageId();
CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");
addSeed(datum);
regexRule.addRule("http://.*");
}
@Override
public void visit(Page page, CrawlDatums next) {
String contentType = page.response().contentType();
if (contentType == null) {
return;
} else if (contentType.contains("html")) {
// 如果是网页,则抽取其中包含图片的URL,放入后续任务
Elements imgs = page.select("img[src]");
for (Element img : imgs) {
String imgSrc = img.attr("abs:src");
next.add(imgSrc);
}
} else if (contentType.startsWith("image")) {
// 如果是图片,直接下载
String extensionName = contentType.split("/")[1];
String imageFileName = imageId.incrementAndGet() + "." + extensionName;
File imageFile = new File(downloadDir, imageFileName);
try {
FileUtils.write(imageFile, page.content());
System.out.println("保存图片 " + page.url() + " 到 " + imageFile.getAbsolutePath());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
public void computeImageId() {
int maxId = -1;
for (File imageFile : downloadDir.listFiles()) {
String fileName = imageFile.getName();
String idStr = fileName.split("\\.")[0];
int id = Integer.valueOf(idStr);
if (id > maxId) {
maxId = id;
}
}
imageId = new AtomicInteger(maxId);
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
JianshuImageCrawler crawler = new JianshuImageCrawler();
crawler.start(2);
}
总结
本篇博客到此结束,但是分享技术点的过程还没有结束。当然,毕竟Java刚入门不久,所以文章很多概念可能有歧义,如有错误,欢迎指导批评,万分感谢。