如何通过WebCollector抓取到详情页数据的二次..
优采云 发布时间: 2021-06-17 04:25
如何通过WebCollector抓取到详情页数据的二次..
前言
上一篇文章我们简单讲过如何通过WebCollector抓取内容,但是这不符合我们的工作需要。在工作过程中,我们通常会抓取某个网页列表下的详情页。数据,这样我们不能简单地从某个列表页面抓取数据,我们需要跳转到详细信息页面进行第二次数据抓取。好了,废话不多说,先从代码开始讲解如何操作。
获取列表信息
假设我们抓取了扫东首页显示文章details的所有内容。如下图所示。
第一步,我们不忙创建爬虫,我们先分析一下我们需要爬取的网站的结构。根据网页的URL地址的特点,我们需要编写正确的正则表达式。如下图。
http://www.jianshu.com/p/700e01a938ce
与我写的文章URL 匹配的正则表达式如下所示。
"http://www.jianshu.com/p/.*"
第二步,正则表达式写好后,我们需要分析一下我们抓到的网站的结构和标签。以谷歌浏览器为例。打开开发者模式控制台。 (F12键Mac commond +alt +i,这里就不啰嗦了),假设我们需要抓取文章的标题和内容。我们先选择“选择工具”(快捷键:commond+shift+c),然后点击标题。标签信息将出现在控制台中。如下图。
我们发现title标签h1的Class为title,如下图。
标题准备好了。接下来,我们来看一下内容。经过以上步骤,我们发现内容在很多标签中。这时候我们只需要获取父标签中的所有内容即可。
顺便说一下,首页的列表页也是如上。如下图,所有超链接都是一个标签。
第三步,我们还是创建了一个继承自AbstractCrawler的爬虫类JianshuCrawler,然后在初始化方法中配置我们的正则表达式和其他一些我们需要爬取的网站Seeds等配置。如下图。
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);
}
在visit方法中,我们需要做两种处理,一种是爬取文章列表,另一种是爬取文章detail页面的内容。所以我们需要使用详情页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了,所以文章很多概念可能比较模糊,所以如果有错误,欢迎指导批评,非常感谢。